Infor OS / ION API Gateway REST Capabilities, Rate Limits & Pagination

Type: ERP Integration System: Infor OS / ION API Gateway (2024.x / 2025.x) Confidence: 0.82 Sources: 8 Verified: 2026-03-02 Freshness: evolving

TL;DR

System Profile

The Infor OS ION API Gateway is the centralized API management layer for all Infor cloud products. It acts as a NodeJS-based reverse proxy that receives HTTP requests from external clients, enforces security and throttling policies, and forwards them to target Infor applications (M3, LN, CloudSuite Distribution/Industrial/etc.). Every Landmark web service is accessible through the gateway, meaning every field of every screen of every Infor Landmark-based application can be read or written via REST.

This card covers the cloud-hosted ION API Gateway (Infor OS 2024.x/2025.x). On-premise ION Grid REST API v2 has a different endpoint structure and authentication model. The gateway also supports SOAP-to-REST transformation via Handlebars-based endpoint policies.

PropertyValue
VendorInfor
SystemInfor OS / ION API Gateway (2024.x / 2025.x)
API SurfaceREST (primary), SOAP (via transformation)
Current API Version2024.x / 2025.x (release-based)
Editions CoveredAll CloudSuite editions (Industrial, Distribution, M3, LN, etc.)
DeploymentCloud (multi-tenant)
API DocsInfor Developer Portal — API Gateway
StatusGA

API Surfaces & Capabilities

API SurfaceProtocolBest ForMax Records/RequestRate LimitReal-time?Bulk?
ION API Gateway (REST)HTTPS/JSONIndividual record CRUD, screen-level operationsApplication-dependentConfigurable per endpointYesNo
Compass V2 APIHTTPS/CSV or NDJSONData Lake queries, analytics, bulk reads100,000 rows / 10 MB per page10,000 calls/min per tenantNo (async)Yes
ION Connect (BODs)XML/OAGISEvent-driven publish-subscribe messagingN/A (message-based)ION Messaging limitsYes (event-driven)Via batch BODs
ION MapperFile-based (CSV/XML)Legacy data transformation, file import/exportFile-size dependentN/ANoYes
Landmark Web ServicesREST (proxied) or SOAPScreen-level field access, transaction processingApplication-dependentShared with gatewayYesNo
M3 API REST v2HTTPS/JSONM3 program calls (execute endpoint)Application-dependentShared with gatewayYesNo

Rate Limits & Quotas

Per-Request Limits

Limit TypeValueApplies ToNotes
Default gateway timeout1 minuteAll ION API requestsExtendable to max 5 minutes per endpoint
Buffered policy payload limit10 MBRequests using transformation policiesNo payload limit without buffered policies
Compass V2 max rows per page100,000 rowsCompass query resultsAlso subject to 10 MB size cap
Compass V2 max page size10 MBCompass query resultsWhichever limit is hit first applies
Compass query timeout60 minutesCompass SQL queriesLong-running queries abort after this

Rolling / Daily Limits

Limit TypeValueWindowEdition Differences
Compass /v2/compass/result calls10,000Per minute, per tenantHighest quota among Compass endpoints
Compass result availability~20 hoursAfter query FINISHED statusMust retrieve results within this window
spikeArrest.maxRequestsPerPeriodConfigurable (e.g., 1,000)Per timePeriodInMillisecondsSet per API suite endpoint policy
Quota policyConfigurable (allow + interval)Per endpointCan be per-user or global
rateSmoothing.delayAfterCountConfigurable (e.g., 1,000)Per time periodGradual delay after count exceeded

Throttling Policy Configuration

Infor does not publish a single global rate limit. Instead, throttling is configured per API suite endpoint via three policy types:

{
  "timePeriodInMilliseconds": 60000,
  "rateSmoothing": {
    "delayAfterCount": 1000,
    "delayFactorInMilliseconds": 1000
  },
  "spikeArrest": {
    "maxRequestsPerPeriod": 1000
  }
}

Authentication

FlowUse WhenToken LifetimeRefresh?Notes
Resource Owner GrantServer-to-server backend integrations2 hours (default)YesUses service account AccessKey + SecretKey
Authorization Code GrantWeb apps, mobile apps requiring user login2 hours (default)YesRequires redirect URI
SAML Bearer GrantApplications within Infor Ming.le / OS portalSession-scopedReuses SSO tokenAlready-authenticated context
Implicit GrantSingle-page apps (SPAs)Short-livedNoNot recommended for server-to-server

Authentication Gotchas

Constraints

Integration Pattern Decision Tree

START — User needs to integrate with Infor CloudSuite via ION API Gateway
|-- What's the integration pattern?
|   |-- Real-time (individual records, <1s)
|   |   |-- Infor Landmark-based app? (M3, LN, CloudSuite)
|   |   |   |-- YES --> ION API Gateway: Landmark web services via REST proxy
|   |   |   +-- NO --> Register custom API suite (Swagger/OpenAPI)
|   |   +-- Need screen-level field access?
|   |       |-- YES --> Landmark web services (every field accessible)
|   |       +-- NO --> M3 API REST v2 (m3api-rest/v2/execute)
|   |-- Batch/Bulk (scheduled, high volume)
|   |   |-- Data volume < 100,000 records?
|   |   |   |-- YES --> Compass V2 API (single query, paginate)
|   |   |   +-- NO --> Compass V2 with query chunking
|   |   +-- Need write operations (inbound)?
|   |       |-- YES --> ION Connect BOD messaging
|   |       +-- NO --> Compass V2 for read-only bulk export
|   |-- Event-driven (real-time notifications)
|   |   |-- Infor-to-Infor?
|   |   |   |-- YES --> ION Connect publish-subscribe (BOD-based)
|   |   |   +-- NO --> ION Connect + ION API connection point
|   |   +-- Need guaranteed delivery?
|   |       |-- YES --> ION Connect with acknowledgment workflow
|   |       +-- NO --> ION API Gateway webhook
|   +-- File-based (CSV/XML)
|       +-- ION Mapper + ION Connect document flows
|-- Which direction?
|   |-- Inbound --> check per-endpoint write throttling
|   |-- Outbound --> check Compass quotas (10K/min result calls)
|   +-- Bidirectional --> design conflict resolution with BOD acknowledgment
+-- Error tolerance?
    |-- Zero-loss --> ION Connect with dead letter queue + BOD replay
    +-- Best-effort --> REST API with retry + exponential backoff

Quick Reference

OperationMethodEndpoint PatternPayloadNotes
Obtain OAuth tokenPOST{tokenEndpoint}grant_type=password&username=...&password=...Token URL from .ionapi file
Refresh tokenPOST{tokenEndpoint}grant_type=refresh_token&refresh_token=...Use before access token expiry
Revoke tokenPOST{revokeEndpoint}token=...&token_type_hint=access_tokenCall on integration shutdown
Call Landmark serviceGET/POST/{suite}/{service}/{endpoint}JSONBearer token required
M3 API executePOST/m3api-rest/v2/execute/{program}/{transaction}JSONM3-specific programs
Submit Compass queryPOST/v2/compass/jobsSQL (text/plain)Async — poll for status
Get Compass resultsGET/v2/compass/jobs/{queryId}/resultN/APaginated; max 100K rows / 10 MB
IFS User infoGET/ifsservice/usermgt/v2/users/meN/AQuick auth validation test

Step-by-Step Integration Guide

1. Download the .ionapi credentials file

The .ionapi file contains all OAuth endpoints and credentials for your tenant. Download from Infor OS Portal > ION API > Authorized Apps. [src3]

{
  "ti": "TENANT_ID",
  "cn": "CLIENT_NAME",
  "ci": "CLIENT_ID",
  "cs": "CLIENT_SECRET",
  "iu": "https://inforos-{region}.inforcloudsuite.com",
  "pu": "https://inforos-{region}.inforcloudsuite.com/{tenant}/as/token.oauth2",
  "oa": "https://inforos-{region}.inforcloudsuite.com/{tenant}/as/authorization.oauth2",
  "or": "https://inforos-{region}.inforcloudsuite.com/{tenant}/as/revoke_token.oauth2"
}

Verify: Confirm the file contains non-empty ci, cs, pu, and service account fields.

2. Authenticate and obtain an access token

Use the Resource Owner grant for backend integrations. [src3, src4]

curl -X POST "{pu}" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=password" \
  -d "username={saession}" \
  -d "password={sapassword}" \
  -d "client_id={ci}" \
  -d "client_secret={cs}"

Verify: Response includes access_token and refresh_token with token_type: Bearer.

3. Call an ION API endpoint

With the bearer token, call any registered API suite endpoint. [src3]

curl -X GET "https://inforos-{region}.inforcloudsuite.com/{tenant}/ifsservice/usermgt/v2/users/me" \
  -H "Authorization: Bearer {access_token}" \
  -H "Accept: application/json"

Verify: HTTP 200 response with JSON payload containing user details.

4. Handle pagination for Compass V2 queries

Submit a query, poll for completion, then paginate results. [src6]

# Submit query
curl -X POST ".../v2/compass/jobs" -H "Content-Type: text/plain" -d "SELECT * FROM table LIMIT 50000"

# Poll status (repeat until FINISHED)
curl -X GET ".../v2/compass/jobs/{queryId}" -H "Authorization: Bearer {token}"

# Retrieve results with pagination
curl -X GET ".../v2/compass/jobs/{queryId}/result?resultFormat=application/x-ndjson&offset=0&limit=10000"

Verify: Result set returns with expected row count; increment offset by limit for next page.

5. Implement token refresh

Access tokens expire after 2 hours. Refresh before expiry. [src4]

curl -X POST "{pu}" \
  -d "grant_type=refresh_token&refresh_token={token}&client_id={ci}&client_secret={cs}"

Verify: New access_token and refresh_token returned.

Code Examples

Python: Authenticate and call ION API

# Input:  .ionapi credentials file path
# Output: JSON response from ION API endpoint

import json
import requests  # requests==2.31.0

def load_ionapi_credentials(filepath):
    with open(filepath, 'r') as f:
        return json.load(f)

def get_access_token(creds):
    resp = requests.post(creds['pu'], data={
        'grant_type': 'password',
        'username': creds.get('saession', creds.get('saak', '')),
        'password': creds.get('sapassword', creds.get('sask', '')),
        'client_id': creds['ci'],
        'client_secret': creds['cs'],
    }, timeout=30)
    resp.raise_for_status()
    return resp.json()

def call_ion_api(base_url, tenant, endpoint, token):
    url = f"{base_url}/{tenant}/{endpoint}"
    resp = requests.get(url, headers={
        'Authorization': f"Bearer {token}",
        'Accept': 'application/json',
    }, timeout=60)
    if resp.status_code == 429:
        retry_after = int(resp.headers.get('Retry-After', 60))
        raise Exception(f"Rate limited. Retry after {retry_after}s")
    resp.raise_for_status()
    return resp.json()

creds = load_ionapi_credentials('my_app.ionapi')
tokens = get_access_token(creds)
result = call_ion_api(creds['iu'], creds['ti'],
    'ifsservice/usermgt/v2/users/me', tokens['access_token'])

JavaScript/Node.js: Compass V2 query with pagination

// Input:  .ionapi credentials, SQL query string
// Output: All query result rows (paginated)

const fs = require('fs');
const axios = require('axios'); // [email protected]

async function compassQuery(ionapiPath, sql) {
  const creds = JSON.parse(fs.readFileSync(ionapiPath, 'utf8'));
  const tokenResp = await axios.post(creds.pu, new URLSearchParams({
    grant_type: 'password',
    username: creds.saession || creds.saak,
    password: creds.sapassword || creds.sask,
    client_id: creds.ci,
    client_secret: creds.cs,
  }).toString(), { headers: { 'Content-Type': 'application/x-www-form-urlencoded' } });

  const token = tokenResp.data.access_token;
  const baseUrl = `${creds.iu}/${creds.ti}`;
  const headers = { Authorization: `Bearer ${token}` };

  // Submit query
  const jobResp = await axios.post(`${baseUrl}/v2/compass/jobs`, sql,
    { headers: { ...headers, 'Content-Type': 'text/plain' } });
  const queryId = jobResp.data.queryId;

  // Poll with backoff
  let status = 'RUNNING', delay = 1000;
  while (status !== 'FINISHED' && status !== 'FAILED') {
    await new Promise(r => setTimeout(r, delay));
    const s = await axios.get(`${baseUrl}/v2/compass/jobs/${queryId}`, { headers });
    status = s.data.status;
    delay = Math.min(delay * 2, 30000);
  }

  // Paginate results
  const allRows = [];
  let offset = 0, limit = 10000, hasMore = true;
  while (hasMore) {
    const r = await axios.get(`${baseUrl}/v2/compass/jobs/${queryId}/result`,
      { params: { resultFormat: 'application/x-ndjson', offset, limit }, headers });
    const rows = r.data.split('\n').filter(Boolean).map(JSON.parse);
    allRows.push(...rows);
    hasMore = rows.length === limit;
    offset += limit;
  }
  return allRows;
}

cURL: Quick authentication test

# Input:  .ionapi credentials (manually extracted)
# Output: User info JSON confirming successful auth

TOKEN=$(curl -s -X POST "{tokenEndpoint}" \
  -d "grant_type=password&username={accessKey}&password={secretKey}&client_id={clientId}&client_secret={clientSecret}" \
  | jq -r '.access_token')

curl -s "https://inforos-{region}.inforcloudsuite.com/{tenant}/ifsservice/usermgt/v2/users/me" \
  -H "Authorization: Bearer $TOKEN" | jq .

Data Mapping

ION API Gateway Data Patterns

Source/ConceptION API EquivalentTypeTransformGotcha
REST request bodyJSON payload to Landmark serviceJSONDirect pass-throughGateway may transform if endpoint policies configured
SOAP requestJSON via Handlebars transformationXML-to-JSONPolicy-defined template10 MB payload limit with buffered policies
Compass SQL querytext/plain POST bodyStringDirect SQLMust match Data Lake schema, not app schema
Compass resultsCSV or NDJSONStreamingFormat via resultFormat paramCSV lacks type info; NDJSON preserves types
BOD documentsOAGIS-standard XMLXMLION Mapper transformationsBOD schema is fixed by OAGIS standard
Auth credentials.ionapi JSON fileJSON configExtract pu, ci, cs, saessionField names vary between file versions

Data Type Gotchas

Error Handling & Failure Points

Common Error Codes

CodeMeaningCauseResolution
401UnauthorizedToken expired or invalidRefresh token and retry; check .ionapi credentials
403ForbiddenInsufficient permissionsVerify ION API authorized app has correct security roles in IFS
404Not FoundEndpoint not registeredCheck API suite registration; verify proxy context path
408Request TimeoutGateway timeout exceeded (default 1 min)Increase timeout (max 5 min) or switch to async pattern
429Too Many RequestsThrottling/spikeArrest limit hitExponential backoff; respect Retry-After header
500Internal Server ErrorBackend application errorCheck ION API logs; verify backend health
502Bad GatewayTarget unreachableCheck application server status; retry with backoff
503Service UnavailableGateway or backend overloadedWait and retry; check Infor Status page

Failure Points in Production

Anti-Patterns

Wrong: Hardcoding the OAuth token endpoint URL

# BAD — hardcoded token URL breaks when migrating between tenants or regions
TOKEN_URL = "https://inforos-us.inforcloudsuite.com/MYTENANT/as/token.oauth2"

Correct: Extract token URL from .ionapi credentials file

# GOOD — token URL is tenant-specific and changes with migration
creds = json.load(open('my_app.ionapi'))
TOKEN_URL = creds['pu']  # always correct for this tenant

Wrong: Polling Compass query status in a tight loop

// BAD — burns through API quota (10K calls/min) and adds unnecessary load
while (status !== 'FINISHED') {
  const resp = await axios.get(`${baseUrl}/v2/compass/jobs/${queryId}`);
  status = resp.data.status;
}

Correct: Poll with exponential backoff

// GOOD — respectful polling with increasing intervals
let delay = 1000;
while (status !== 'FINISHED' && status !== 'FAILED') {
  await new Promise(r => setTimeout(r, delay));
  const resp = await axios.get(url, { headers });
  status = resp.data.status;
  delay = Math.min(delay * 2, 30000); // max 30s between polls
}

Wrong: Ignoring gateway timeout for long-running operations

# BAD — large data export times out at 60 seconds
response = requests.get(f"{base_url}/myapp/api/export-all",
    headers=headers, timeout=300)  # client timeout is 5 min but gateway kills at 1 min

Correct: Use Compass V2 async queries for bulk data

# GOOD — Compass handles long-running queries asynchronously
job = requests.post(f"{base_url}/v2/compass/jobs",
    data="SELECT * FROM large_table",
    headers={**headers, 'Content-Type': 'text/plain'})
query_id = job.json()['queryId']
# Poll for completion, then paginate results

Common Pitfalls

Diagnostic Commands

# Test authentication — obtain access token
curl -s -X POST "{tokenEndpoint}" \
  -d "grant_type=password&username={accessKey}&password={secretKey}&client_id={clientId}&client_secret={clientSecret}" \
  | jq '{access_token: .access_token, token_type: .token_type, expires_in: .expires_in}'

# Verify token validity — get current user info
curl -s -X GET "https://inforos-{region}.inforcloudsuite.com/{tenant}/ifsservice/usermgt/v2/users/me" \
  -H "Authorization: Bearer {token}" | jq .

# Test a specific API suite endpoint
curl -s -o /dev/null -w "HTTP %{http_code} — %{time_total}s\n" \
  -X GET "https://inforos-{region}.inforcloudsuite.com/{tenant}/{suite}/{endpoint}" \
  -H "Authorization: Bearer {token}"

# Check Compass query status
curl -s "https://inforos-{region}.inforcloudsuite.com/{tenant}/v2/compass/jobs/{queryId}" \
  -H "Authorization: Bearer {token}" | jq '{status: .status, rowCount: .rowCount}'

# Revoke token (cleanup)
curl -s -X POST "{revokeEndpoint}" \
  -d "token={token}&token_type_hint=access_token&client_id={clientId}&client_secret={clientSecret}"

Version History & Compatibility

Platform ReleaseRelease PeriodStatusKey ChangesMigration Notes
Infor OS 2025.x2025 H1CurrentCompass V2 GA, enhanced throttlingCompass V1 deprecated — migrate to V2
Infor OS 2024.x2024SupportedBaaS endpoint policies, OpenAPI 3.0.xNew policy configuration format
Infor OS 2022.x2022Supported (limited)SOAP-to-REST via Handlebars, Swagger 2.0Older .ionapi credential format
Infor OS 12.0.x2020-2021EOLInitial Compass V1 APIsMust upgrade to 2024.x+ for Compass V2

When to Use / When Not to Use

Use WhenDon't Use WhenUse Instead
Real-time REST API calls to Infor Landmark appsHigh-volume bulk export > 100K records in real-timeCompass V2 API (async, paginated)
Server-to-server integration with OAuth 2.0Event-driven publish-subscribe messagingION Connect with BOD workflows
Unified auth across multiple Infor applicationsDirect database access to Infor backendNever bypass gateway for cloud deployments
SOAP-to-REST transformation for legacy servicesOn-premise ION Grid REST API integrationION Grid REST API v2 (different base URL/auth)
Web/mobile apps needing user-context API accessFile-based batch import/export (CSV/XML)ION Mapper + ION Connect document flows

Important Caveats

Related Units