Developer ToolsFree generatorPDF, Excel, Word exportInfor ION

Infor Data Lake Compass SQL API Request Generator (Python and curl)

This generator writes Python and curl code for the Infor Data Lake Compass v2 query API: submit a SQL job, poll its status, then download the result in pages with offset and limit. It targets Infor OS tenants that call Compass through the Data Fabric suite of the ION API Gateway.

Fill in

Use the data object name as shown in Data Lake Atlas or the Data Catalog.

Generated code
Download:

Python: submit, poll and page a Compass v2 query

# Requires: pip install requests
# Token: create an ION API backend service client and get a bearer token
# from the token endpoint in the .ionapi file (pu + ot), then export it as ION_TOKEN.
import os
import time
import requests

BASE = "https://mingle-ionapi.inforcloudsuite.com/ACME_PRD/DATAFABRIC/compass/v2"
TOKEN = os.environ["ION_TOKEN"]
PAGE_LIMIT = int("10000")
POLL_SECONDS = float("5")
ACCEPT = "application/x-ndjson"

SQL = """SELECT * FROM YourDataObject LIMIT 100"""

session = requests.Session()
session.headers.update({"Authorization": "Bearer " + TOKEN})


def call(method, url, **kwargs):
    """One HTTP call with backoff on 429 and a clear message on 404."""
    for attempt in range(6):
        resp = session.request(method, url, timeout=300, **kwargs)
        if resp.status_code == 429:
            # Per-tenant per-minute quota hit: back off and retry
            time.sleep(min(60, 2 ** attempt))
            continue
        if resp.status_code == 404:
            raise RuntimeError(
                "404 Query not found. Check the queryId, the tenant in the URL, that the "
                "same principal (client) submitted the job, that results have not expired, "
                "and the Accept header. Body: " + resp.text[:500]
            )
        resp.raise_for_status()
        return resp
    raise RuntimeError("Still rate limited (429) after retries: " + url)


# 1) Submit the SQL as a job (body is plain text)
submit = call(
    "POST",
    BASE + "/jobs/",
    data=SQL.encode("utf-8"),
    headers={"Content-Type": "text/plain"},
)
query_id = submit.json()["queryId"]
print("queryId:", query_id)

# 2) Poll status until the job ends
while True:
    st = call("GET", BASE + "/jobs/" + query_id + "/status/").json()
    status = st.get("status")
    if status == "FINISHED":
        row_count = st.get("rowCount")
        print("FINISHED, rowCount:", row_count)
        break
    if status in ("FAILED", "CANCELLED"):
        # For FAILED jobs the result call returns the error details
        detail = call("GET", BASE + "/jobs/" + query_id + "/result/", headers={"Accept": ACCEPT})
        raise RuntimeError(status + ": " + detail.text[:2000])
    time.sleep(POLL_SECONDS)

# 3) Fetch result pages with offset and limit
extension = ".csv" if ACCEPT == "text/csv" else ".ndjson"
out_path = "compass_" + query_id + extension
offset = 0
header = None
written = 0
with open(out_path, "w", encoding="utf-8", newline="") as out:
    while True:
        page = call(
            "GET",
            BASE + "/jobs/" + query_id + "/result/",
            params={"offset": offset, "limit": PAGE_LIMIT},
            headers={"Accept": ACCEPT},
        )
        lines = [ln for ln in page.text.splitlines() if ln.strip()]
        if ACCEPT == "text/csv" and lines:
            if header is None:
                header = lines[0]
                out.write(header + "\n")
            if lines[0] == header:
                lines = lines[1:]
        for ln in lines:
            out.write(ln + "\n")
        written += len(lines)
        offset += PAGE_LIMIT
        if len(lines) < PAGE_LIMIT:
            break
        if row_count is not None and offset >= int(row_count):
            break

print("Wrote", written, "rows to", out_path)

curl equivalent (bash, needs jq)

BASE="https://mingle-ionapi.inforcloudsuite.com/ACME_PRD/DATAFABRIC/compass/v2"
TOKEN=$(printenv ION_TOKEN)

cat > query.sql <<'EOF'
SELECT * FROM YourDataObject LIMIT 100
EOF

# 1) Submit
QUERY_ID=$(curl -s -X POST "$BASE/jobs/" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: text/plain" \
  --data-binary @query.sql | jq -r '.queryId')
echo "queryId: $QUERY_ID"

# 2) Poll status
while true; do
  STATUS=$(curl -s "$BASE/jobs/$QUERY_ID/status/" -H "Authorization: Bearer $TOKEN" | jq -r '.status')
  echo "status: $STATUS"
  case "$STATUS" in
    FINISHED) break ;;
    FAILED|CANCELLED) curl -s "$BASE/jobs/$QUERY_ID/result/" -H "Authorization: Bearer $TOKEN"; exit 1 ;;
  esac
  sleep 5
done

# 3) First result page (repeat with offset=10000, 2x10000 ... until a short page)
curl -s "$BASE/jobs/$QUERY_ID/result/?offset=0&limit=10000" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Accept: application/x-ndjson" \
  -o page_0.out

Limits to design around

ItemDocumented behaviour
Result page sizelimit up to 100,000 rows per call, with a 10 MB cap per page
Result retentionRoughly 20 hours after the job reaches FINISHED
Who can read resultsOnly the tenant and principal that submitted the job
Rate limitsPer-tenant per-minute quotas apply to the jobs, status and result endpoints; a 429 means back off. Check the current values in the Data Fabric API documentation
Status values handled hereRUNNING, FINISHED, FAILED, CANCELLED (any other value is treated as still in progress)

Verify on your tenant before production use: open the Infor Data Fabric suite in API Gateway and compare the Compass v2 paths, verbs and parameters with this code (newer tenants expose Compass under the Data Fabric suite; older material may show it under the ION API suite). Page limits, quotas and retention come from the Infor OS Data Fabric documentation and can change between releases.

Get your Compass extract reviewed

Send us your SQL, data object and volume and we will suggest partition filters, page size and a schedule that stays inside your tenant quotas.

Optional. The tool and every export work without this. No spam.

Was this tool useful?

How to use it

Open your downloaded .ionapi file and copy the iu value (the ION API base URL) and ti value (the tenant). Get a bearer token for a backend service client from the token endpoint in the same file, export it into the environment variable you named, and paste your SQL. The SQL runs against Data Lake data objects, so use object names as they appear in Atlas or the Data Catalog. Pick NDJSON when you want one JSON object per row for loading into Python or a warehouse, or CSV for spreadsheets. Keep the page size well below 100,000 if rows are wide, because the 10 MB page cap applies first.

  • Run a COUNT(*) version of the query first to see how many pages you will pull.
  • Store the queryId: you can re-read pages for about 20 hours without re-running the SQL.
  • Run the script under the same API client that submitted the job.

How the Compass v2 flow works

Compass is asynchronous. The jobs call accepts the SQL as a text/plain body and returns a queryId straight away. The status call reports RUNNING until the engine ends with FINISHED, FAILED or CANCELLED; a FINISHED status includes the total rowCount. Only then do you call result. Version 2 differs from version 1 in that the result is returned in row sets: offset is a 0-based row position and limit is the page size, and both apply only to the result call. The script loops offset by the page size and stops on a short page or when it reaches rowCount. For a FAILED job, the result call returns the error text, which is usually a SQL or data object name problem.

  • POST .../jobs/ submits, GET .../jobs/{queryId}/status/ polls, GET .../jobs/{queryId}/result/ pages.
  • Content-Type text/plain is required on submit; a different type is rejected.
  • Accept selects text/csv or application/x-ndjson for the result.

Errors you will actually see

A 404 Query not found on status or result nearly always means one of four things: the queryId was mistyped or truncated, the tenant in the URL differs from the one that submitted, a different API client (principal) is asking, or the result expired after about 20 hours. A 429 means a per-tenant per-minute quota was hit, so batch work into fewer, larger queries rather than one query per record. A 401 means the token expired; tokens are short-lived, so long downloads should refresh the token between pages.

  • Poll status every few seconds, not in a tight loop, to stay inside the status quota.
  • Add WHERE clauses on partition or date fields to keep jobs short.

When Compass is the wrong tool

Compass is built for analytical pulls and extracts from the Data Lake, not for transactional lookups. If you need one record in real time, call the source application API through ION API instead. If you need a recurring feed into another system, an ION data flow or Data Lake flow is usually easier to monitor than a scheduled script. For BI tools there is also a Compass JDBC driver, which handles paging for you. Use this generated script for one-off extracts, data science notebooks and lightweight scheduled jobs where you control the code.

Frequently Asked Questions

What is Infor Data Lake?

Infor Data Lake is the storage layer of Infor OS Data Fabric. ION and other publishers load data objects from Infor applications such as M3, LN and CloudSuite Industrial into it, and you query them with Compass SQL through the Compass UI, the JDBC driver or the Compass REST API shown here.

What is the difference between Compass v1 and v2?

Version 1 returns the whole result set in one result call. Version 2 returns it in pages, with offset and limit parameters on the result call, so large extracts do not need one huge response. In both versions the SQL is the body of the jobs call.

How do I get a token for the ION API Gateway?

In Infor OS, register an authorized app as a backend service in ION API, download its .ionapi credentials file, then request a token from the token endpoint built from the pu and ot values using the client and service account credentials in that file. Pass it as a Bearer token.

Why do I get 404 Query not found right after submitting?

Check that the status call uses exactly the returned queryId and the same tenant, and that the same API client makes both calls, because results are visible only to the submitting principal. If it worked earlier and fails now, the roughly 20 hour retention window may have passed.

If you want people to ask questions of your Infor Data Lake in plain English on infrastructure you control, we can build that layer on top of these same Compass calls.

ArchRay builder
Design the on-prem AI stack: model, GPUs, RAG pipeline, guardrails
See memory, speed and cost as you swap parts, then export the diagram. Free, in your browser.
Open the builder

Keep exploring

Free tools

ERP fixes and how-tos

Guides

AI on your ERP

Integrations