Infor Data Lake: Bulk Data Extraction via Compass Queries

Type: ERP Integration System: Infor Data Lake (Compass v2 API 2025.x) Confidence: 0.82 Sources: 8 Verified: 2026-03-02 Freshness: 2026-03-02

TL;DR

System Profile

Infor Data Lake is the centralized data repository within Infor OS (the cloud platform underpinning all Infor CloudSuites). Data from Infor applications — M3, LN, CloudSuite Industrial, CloudSuite Distribution, and others — is automatically published to Data Lake via ION messaging. The Compass query engine sits atop Data Lake and provides SQL-like query capabilities for extracting this data. [src3]

This card covers the Compass v2 REST API available through the Infor Data Fabric Suite, which replaced the deprecated ION API Suite Compass endpoints (removed April 2025). It does NOT cover the Data Lake API (for hierarchical document retrieval), ION Data Lake Flows (for automated S3-based data routing), or the Compass UI within the Data Fabric application. [src1, src5]

PropertyValue
VendorInfor
SystemInfor Data Lake (Data Fabric / Compass v2)
API SurfaceREST (asynchronous job-based)
Current API Versionv2 (Data Fabric Suite, 2025.x)
Editions CoveredAll Infor OS Cloud editions with Data Lake entitlement
DeploymentCloud (Infor multi-tenant)
API DocsInfor Data Fabric User Guide
StatusGA (v2 via Data Fabric Suite); v1/v2 via ION API Suite deprecated

API Surfaces & Capabilities

Infor provides multiple ways to extract data from Data Lake. The Compass API is the primary programmatic interface for SQL-based querying. [src5]

API SurfaceProtocolBest ForMax Records/RequestRate LimitReal-time?Bulk?
Compass v2 API (Data Fabric)HTTPS/RESTBulk SQL extraction, ETL100K rows / 10 MB per page100 jobs/min/tenantNo (async)Yes
Compass JDBC DriverJDBCBI tools (Tableau, Power BI, DBeaver)Driver-managed paginationShares Compass limitsNoYes
Data Lake APIHTTPS/RESTHierarchical document retrievalLimited filteringION API limitsYesNo
ION Data Lake FlowsION ConnectAutomated S3 export, CDC delta loadsFull table + deltasFlow-basedNoYes
Atlas UIBrowserInteractive data browsingN/AN/AYesNo

Rate Limits & Quotas

Per-Request Limits

Limit TypeValueApplies ToNotes
Max rows per result page100,000Compass v2 /result endpointWhichever limit (rows or size) is hit first
Max result page size10 MBCompass v2 /result endpointUse smaller page sizes for wide tables
Query timeout60 minutesAll Compass queriesTimed-out queries still consume compute time
Result expiration~20 hoursCompleted query resultsMust download before expiry
Content-Type headertext/plainv2 /jobs endpointRequired for query submission
[src1, src2]

Rolling / Daily Limits

Limit TypeValueWindowEdition Differences
POST /v2/compass/jobs100 callsPer minute, per tenantShared across all users in tenant
POST /v2/compass/{id}/status1,000 callsPer minute, per tenant---
GET /v2/compass/jobs/{id}/result10,000 callsPer minute, per tenant---
PUT /v2/compass/{id}/cancel1,000 callsPer minute, per tenant---
Compute timeMeteredPer tenantSubject to Infor OS subscription tier
EgressMeteredPer tenantCharged when data leaves Infor cloud
[src1, src2, src7]

Authentication

All Compass API calls go through the ION API Gateway, which uses OAuth 2.0 for authentication. Credentials are provisioned as a .ionapi file downloaded from the Infor OS Portal. [src1, src6]

FlowUse WhenToken LifetimeRefresh?Notes
OAuth 2.0 Resource Owner (Service Account)Server-to-server, unattended ETL~2 hoursYes (via refresh token)Use saak/sask from .ionapi file as username/password to obtain bearer token
OAuth 2.0 Authorization CodeUser-context, interactive tools~2 hoursYesRequires redirect URI; used by Compass JDBC and BI tools

Authentication Gotchas

Constraints

Integration Pattern Decision Tree

START — Extract data from Infor Data Lake
|-- What data format?
|   |-- Flat (DSV or newline-delimited JSON)
|   |   |-- Data volume < 100K rows per query?
|   |   |   |-- YES --> Compass v2 API (single page result)
|   |   |   +-- NO --> Compass v2 API with pagination (offset/limit)
|   |   |-- Need SQL joins across tables?
|   |   |   |-- YES --> Compass v2 API (supports JOINs) [src4]
|   |   |   +-- NO --> Data Lake API may also work for simple lookups [src5]
|   |   +-- Need BI tool integration?
|   |       |-- YES --> Compass JDBC driver (DBeaver, Tableau, Power BI) [src3]
|   |       +-- NO --> Compass v2 REST API
|   +-- Hierarchical/nested JSON
|       +-- Data Lake API (not Compass) [src5]
|-- What's the extraction pattern?
|   |-- One-time or ad-hoc extraction
|   |   +-- Compass v2 API or Compass UI [src3]
|   |-- Scheduled recurring ETL
|   |   |-- < 60 min query time?
|   |   |   |-- YES --> Compass v2 API with job scheduler
|   |   |   +-- NO --> ION Data Lake Flows (S3 export) [src5]
|   |   +-- Need incremental/delta loads?
|   |       |-- YES --> Filter on infor.lastModified() property [src5]
|   |       +-- NO --> Full table query
|   +-- Automated S3 export with CDC
|       +-- ION Data Lake Flows (first load = full, subsequent = delta) [src5]
|-- Authentication approach?
|   |-- Service account (unattended) --> OAuth 2.0 with saak/sask from .ionapi [src6]
|   +-- User context (interactive) --> OAuth 2.0 authorization code flow
+-- Error tolerance?
    |-- Zero-loss --> Implement idempotency via infor.DataObjectId tracking
    +-- Best-effort --> Simple retry on FAILED status

Quick Reference

OperationMethodEndpointPayloadNotes
Submit queryPOST/v2/compass/jobsSQL query as text/plain bodyReturns queryID for tracking [src1, src2]
Check statusPOST/v2/compass/{queryId}/statusNoneReturns RUNNING, FINISHED, FAILED, or CANCELLED [src2]
Get results (paginated)GET/v2/compass/jobs/{queryId}/result?offset=0&limit=1000NoneSupports text/csv or application/x-ndjson via Accept header [src2]
Cancel queryPUT/v2/compass/{queryId}/cancelNoneStops long-running queries [src2]
Get OAuth tokenPOST{pu}{ot} (from .ionapi)grant_type, username (saak), password (sask), client_id, client_secretReturns access_token (~2h lifetime) [src6]

Step-by-Step Integration Guide

1. Obtain ION API credentials

Navigate to Infor OS Portal > ION API > Authorized Apps. Create or select a Backend Service authorized app. Click "Download Credentials" and select "Create Service Account." Save the .ionapi file securely. [src6, src8]

{
  "ti": "TENANT_ID",
  "cn": "common_name",
  "ci": "client_id",
  "cs": "client_secret",
  "iu": "https://mingle-ionapi.inforcloudsuite.com/TENANT_ID",
  "pu": "https://mingle-sso.inforcloudsuite.com:443/TENANT_ID/as/",
  "oa": "authorization.oauth2",
  "ot": "token.oauth2",
  "or": "revoke_token.oauth2",
  "saak": "SERVICE_ACCOUNT_ACCESS_KEY",
  "sask": "SERVICE_ACCOUNT_SECRET_KEY"
}

Verify: Open the .ionapi file and confirm all fields are populated. The saak and sask fields must be present.

2. Acquire OAuth 2.0 bearer token

Use the token endpoint URL (constructed from pu + ot) with the service account credentials. [src6]

TOKEN_URL="https://mingle-sso.inforcloudsuite.com:443/TENANT_ID/as/token.oauth2"

ACCESS_TOKEN=$(curl -s -X POST "$TOKEN_URL" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=password" \
  -d "username=SERVICE_ACCOUNT_ACCESS_KEY" \
  -d "password=SERVICE_ACCOUNT_SECRET_KEY" \
  -d "client_id=CLIENT_ID" \
  -d "client_secret=CLIENT_SECRET" \
  | jq -r '.access_token')

Verify: echo $ACCESS_TOKEN should output a JWT string. Token should not be null or empty.

3. Submit a Compass query

POST your SQL SELECT query to the /v2/compass/jobs endpoint with Content-Type text/plain. [src1, src2]

API_BASE="https://mingle-ionapi.inforcloudsuite.com/TENANT_ID/IONSERVICES/datafabric"

QUERY_ID=$(curl -s -X POST "$API_BASE/v2/compass/jobs" \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: text/plain" \
  -d "SELECT CONO, ITNO, ITDS, FUDS FROM MITMAS ORDER BY CONO, ITNO" \
  | jq -r '.queryId')

Verify: Response contains a queryId string (UUID format).

4. Poll for query completion

Check the status endpoint until the query reaches FINISHED, FAILED, or CANCELLED state. [src2]

while true; do
  STATUS=$(curl -s -X POST "$API_BASE/v2/compass/$QUERY_ID/status" \
    -H "Authorization: Bearer $ACCESS_TOKEN" | jq -r '.status')
  echo "Status: $STATUS"
  [ "$STATUS" = "FINISHED" ] || [ "$STATUS" = "FAILED" ] && break
  sleep 5
done

Verify: Status transitions from RUNNING to FINISHED. The rowCount field shows total available rows.

5. Retrieve paginated results

Use offset and limit parameters to page through the result set. Maximum 100,000 rows or 10 MB per page. [src2]

curl -s "$API_BASE/v2/compass/jobs/$QUERY_ID/result?offset=0&limit=10000" \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Accept: text/csv" \
  -H "Accept-Encoding: gzip" \
  -o "result_page_0.csv"

Verify: Output file contains header row + data rows matching expected count.

6. Implement full pagination loop

For result sets larger than one page, iterate with increasing offsets until all rows are retrieved. [src2]

PAGE_SIZE=50000; OFFSET=0; PAGE=0
while [ $OFFSET -lt $TOTAL_ROWS ]; do
  curl -s "$API_BASE/v2/compass/jobs/$QUERY_ID/result?offset=$OFFSET&limit=$PAGE_SIZE" \
    -H "Authorization: Bearer $ACCESS_TOKEN" -H "Accept: text/csv" \
    -o "result_page_${PAGE}.csv"
  OFFSET=$((OFFSET + PAGE_SIZE)); PAGE=$((PAGE + 1))
done

Verify: Total rows across all pages equals the rowCount from status response.

Code Examples

Python: Bulk extract with pagination

# Input:  .ionapi credentials file path, SQL query
# Output: All query results as a pandas DataFrame

import json, time, requests, pandas as pd
from io import StringIO

def compass_query(ionapi_path, sql, page_size=50000):
    creds = json.load(open(ionapi_path))
    # Get token
    token_resp = requests.post(f"{creds['pu']}{creds['ot']}", data={
        'grant_type': 'password', 'username': creds['saak'],
        'password': creds['sask'], 'client_id': creds['ci'],
        'client_secret': creds['cs']})
    token = token_resp.json()['access_token']
    base = f"{creds['iu']}/IONSERVICES/datafabric"
    hdrs = {'Authorization': f'Bearer {token}'}
    # Submit query
    qid = requests.post(f"{base}/v2/compass/jobs",
        headers={**hdrs, 'Content-Type': 'text/plain'}, data=sql).json()['queryId']
    # Poll status
    while True:
        s = requests.post(f"{base}/v2/compass/{qid}/status", headers=hdrs).json()
        if s['status'] == 'FINISHED': break
        if s['status'] in ('FAILED','CANCELLED'): raise RuntimeError(s)
        time.sleep(3)
    # Paginate results
    dfs, offset = [], 0
    while offset < s.get('rowCount', 0):
        r = requests.get(f"{base}/v2/compass/jobs/{qid}/result",
            headers={**hdrs, 'Accept': 'text/csv'},
            params={'offset': offset, 'limit': page_size})
        dfs.append(pd.read_csv(StringIO(r.text))); offset += page_size
    return pd.concat(dfs, ignore_index=True) if dfs else pd.DataFrame()

JavaScript/Node.js: Compass query with async/await

// Input:  .ionapi file path, SQL query string
// Output: Array of result objects from Data Lake
// npm install axios@1
const axios = require('axios');
const fs = require('fs');

async function compassQuery(ionapiPath, sql, pageSize = 50000) {
  const creds = JSON.parse(fs.readFileSync(ionapiPath, 'utf-8'));
  const tokenResp = await axios.post(`${creds.pu}${creds.ot}`,
    new URLSearchParams({ grant_type: 'password', username: creds.saak,
      password: creds.sask, client_id: creds.ci, client_secret: creds.cs }));
  const token = tokenResp.data.access_token;
  const base = `${creds.iu}/IONSERVICES/datafabric`;
  const hdrs = { Authorization: `Bearer ${token}` };
  // Submit
  const { data: { queryId } } = await axios.post(`${base}/v2/compass/jobs`,
    sql, { headers: { ...hdrs, 'Content-Type': 'text/plain' } });
  // Poll
  let status = 'RUNNING', rowCount = 0;
  while (status === 'RUNNING') {
    await new Promise(r => setTimeout(r, 3000));
    const s = await axios.post(`${base}/v2/compass/${queryId}/status`, null, { headers: hdrs });
    status = s.data.status; rowCount = s.data.rowCount || 0;
  }
  if (status !== 'FINISHED') throw new Error(`Query ${status}`);
  // Paginate (NDJSON)
  const rows = []; let offset = 0;
  while (offset < rowCount) {
    const r = await axios.get(`${base}/v2/compass/jobs/${queryId}/result`,
      { headers: { ...hdrs, Accept: 'application/x-ndjson' },
        params: { offset, limit: pageSize } });
    rows.push(...r.data.trim().split('\n').map(l => JSON.parse(l)));
    offset += pageSize;
  }
  return rows;
}

cURL: Quick Compass API test

# Input:  .ionapi credentials, SQL query
# Output: First page of query results

# Step 1: Get token
TOKEN=$(curl -s -X POST \
  "https://mingle-sso.inforcloudsuite.com:443/TENANT/as/token.oauth2" \
  -d "grant_type=password&username=SAAK&password=SASK&client_id=CI&client_secret=CS" \
  | jq -r '.access_token')

# Step 2: Submit query
BASE="https://mingle-ionapi.inforcloudsuite.com/TENANT/IONSERVICES/datafabric"
QID=$(curl -s -X POST "$BASE/v2/compass/jobs" \
  -H "Authorization: Bearer $TOKEN" -H "Content-Type: text/plain" \
  -d "SELECT TOP 10 CONO, ITNO, ITDS FROM MITMAS" | jq -r '.queryId')

# Step 3: Wait and check status
sleep 10
curl -s -X POST "$BASE/v2/compass/$QID/status" \
  -H "Authorization: Bearer $TOKEN" | jq .

# Step 4: Get results as CSV
curl -s "$BASE/v2/compass/jobs/$QID/result?offset=0&limit=100" \
  -H "Authorization: Bearer $TOKEN" -H "Accept: text/csv"

Data Mapping

Infor Data Lake Property Reference

Data Lake PropertySQL NameTypePurposeGotcha
infor.DataObjectIddl_idStringUnique identifier per data object in Data LakeNot the same as the source system record ID
infor.lastModified()dl_document_indexed_dateTimestampWhen data object was added/updated in Data LakeWas named dl_document_date before 2023.02
infor.DataObjectSeqIdN/AIntegerRecord/line number within a data objectUseful for deduplication in multi-line objects
Source system fieldsOriginal namesVariesBusiness data fields (e.g., CONO, ITNO from M3)Field names are ERP-specific
[src5]

Data Type Gotchas

Error Handling & Failure Points

Common Error Codes

CodeMeaningCauseResolution
FAILED (job status)Query execution errorSyntax error, invalid table/field, timeoutRetrieve error details from /result endpoint; fix query syntax
401 UnauthorizedInvalid or expired tokenOAuth token expired (~2h) or invalidRefresh token or request new one using .ionapi credentials
429 Too Many RequestsRate limit exceededExceeded per-minute API call quotaImplement exponential backoff; reduce polling frequency
Process ERROR 401Invalid property nameReferencing non-existent column/fieldVerify field names using Atlas UI or Data Catalog
INTERNAL ERROR 624Internal Compass errorInfrastructure issueContact Infor Support with full error message
Timeout (60 min)Query exceeded time limitQuery too complex or scanning too much dataOptimize with WHERE clauses; use LIMIT; consider Data Lake Flows
[src1, src2, src3]

Failure Points in Production

Anti-Patterns

Wrong: Polling status in a tight loop

# BAD -- burns through 1,000/min status rate limit in seconds
while True:
    status = check_status(query_id)
    if status == 'FINISHED':
        break

Correct: Poll with exponential backoff

# GOOD -- respects rate limits, starts fast and backs off
import time
delay = 2
max_delay = 30
while True:
    status = check_status(query_id)
    if status in ('FINISHED', 'FAILED', 'CANCELLED'):
        break
    time.sleep(delay)
    delay = min(delay * 1.5, max_delay)

Wrong: Requesting a new OAuth token for every API call

# BAD -- wastes time, may trigger auth server rate limits
for page in range(total_pages):
    token = get_access_token(creds)  # new token per page!
    get_results(token, query_id, offset=page * page_size)

Correct: Cache token and reuse until near expiry

# GOOD -- token cached, only refreshed when near expiry
token = get_access_token(creds)
token_expiry = time.time() + 7000  # ~2h minus buffer
for page in range(total_pages):
    if time.time() > token_expiry:
        token = get_access_token(creds)
        token_expiry = time.time() + 7000
    get_results(token, query_id, offset=page * page_size)

Wrong: Full table scan for incremental loads

-- BAD -- scans entire table every time, may timeout
SELECT * FROM MITMAS ORDER BY ITNO

Correct: Use infor.lastModified() for incremental extraction

-- GOOD -- only extracts records added/modified since last run
SELECT CONO, ITNO, ITDS, FUDS FROM MITMAS
WHERE dl_document_indexed_date > '2026-02-28T00:00:00Z'
ORDER BY ITNO

Common Pitfalls

Diagnostic Commands

# Test OAuth token acquisition
curl -s -X POST "https://mingle-sso.inforcloudsuite.com:443/TENANT/as/token.oauth2" \
  -d "grant_type=password&username=SAAK&password=SASK&client_id=CI&client_secret=CS" \
  | jq '{access_token: .access_token[:20], expires_in: .expires_in, token_type: .token_type}'

# Submit a minimal test query
curl -s -X POST "$BASE/v2/compass/jobs" \
  -H "Authorization: Bearer $TOKEN" -H "Content-Type: text/plain" \
  -d "SELECT TOP 1 * FROM MITMAS" | jq .

# Check query status
curl -s -X POST "$BASE/v2/compass/$QID/status" \
  -H "Authorization: Bearer $TOKEN" | jq .

# Cancel a running query
curl -s -X PUT "$BASE/v2/compass/$QID/cancel" \
  -H "Authorization: Bearer $TOKEN" | jq .

Version History & Compatibility

VersionReleaseStatusBreaking ChangesMigration Notes
Compass v2 (Data Fabric Suite)2024CurrentN/ARecommended endpoint for all new integrations
Compass v2 (ION API Suite)2022Deprecated (removed Apr 2025)Base URL changedMigrate from /datalakeapi to /datafabric
Compass v1 (ION API Suite)2019Deprecated (removed Apr 2025)No pagination supportMigrate to v2 for pagination and better rate limits
JDBC Connection String Auth2021-06CurrentN/ASimplified authentication vs file-based approach

Deprecation Policy

Infor deprecates API endpoints with advance notice through release notes and Documentation Central. The Compass v1/v2 ION API Suite endpoints were deprecated with a migration window of approximately 12-18 months before removal. Always monitor Infor's release notes for upcoming changes. [src1]

When to Use / When Not to Use

Use WhenDon't Use WhenUse Instead
Extracting bulk data from Infor ERP products (M3, LN, CloudSuite) published to Data LakeNeed to write data back to Infor applicationsION API or BOD-based integration
Building ETL pipelines to external warehouses (Snowflake, Databricks, BigQuery)Need real-time (<1 second) data from InforDirect ION API calls to source ERP
Running SQL joins across multiple Infor data objects in Data LakeNeed to query hierarchical/nested JSON documentsData Lake API (non-Compass)
BI tool connectivity (Tableau, Power BI) via Compass JDBC driverData volume requires >60 minutes of query timeION Data Lake Flows (S3 export)
Ad-hoc data analysis and explorationNeed guaranteed delivery with CDC/event-driven patternsION Connect with Business Events

Important Caveats

Related Units