Querying the endpoints
All endpoints accept standard OData query options. Use them to read only the rows and fields you need.
| Option | Purpose | Example |
|---|---|---|
$filter | Filter rows | $filter=postingDate ge 2026-01-01 |
$select | Pick fields | $select=entryNo,postingDate,gLAccountNo,amount |
$orderby | Sort | $orderby=postingDate desc |
$top | Limit the number of rows | $top=100 |
$skip | Skip rows | $skip=200 |
Prefer: odata.maxpagesize=N | Page size (a request header) | Prefer: odata.maxpagesize=5000 |
$filter
All endpoints can be filtered on most of their fields, with a few restrictions caused by technical limitations. For example, G/L entries posted since 2026-01-01:
GET .../companies({companyId})/glEntries?$filter=postingDate ge 2026-01-01
Operators are eq, ne, gt, ge, lt, le, and, or and not, plus
functions such as startswith and contains:
# One customer's entries
GET .../customerLedgerEntries?$filter=customerNo eq 'C00010'
# A date range
GET .../glEntries?$filter=postingDate ge 2026-01-01 and postingDate lt 2027-01-01
# Changed since a point in time
GET .../glEntries?$filter=systemModifiedAt ge 2026-01-01T00:00:00.000Z
# Document numbers with a prefix
GET .../salesInvoiceHeaders?$filter=startswith(no, 'PS-INV')
- Text values go in single quotes:
'DK'. A quote inside a value is doubled:'O''Brien'. - Dates and date-times have no quotes:
2026-04-30,2026-04-30T08:00:00Z. - Enum values are text in the API output language; filter on the value as the endpoint returns it.
See Microsoft's filter expressions in OData URIs for more expressions.
$select
Read only the fields you need. It reduces the payload and speeds up large loads:
GET .../glEntries?$select=entryNo,postingDate,gLAccountNo,amount,systemModifiedAt
Keep the business key and systemModifiedAt in the selection when you load
incrementally.
$orderby
GET .../glEntries?$orderby=postingDate asc,entryNo asc
Pagination
A response holds at most 20,000 rows. When the query matches more, the
response contains an extra key, @odata.nextLink, whose value is the next
page of the same query:
"@odata.nextLink": "https://api.businesscentral.dynamics.com/v2.0/{tenantId}/{environment}/api/datawareness/endpoints/v1.0/companies({companyId})/glEntries?$filter=postingDate%20ge%202026-01-01&$skiptoken=469268"
To get the complete dataset, keep calling the URL in @odata.nextLink until
the key is no longer returned. Use the link exactly as given.
url = f"{base}/glEntries?$filter=postingDate ge 2026-01-01"
headers = {"Authorization": f"Bearer {token}", "Prefer": "odata.maxpagesize=5000"}
rows = []
while url:
page = requests.get(url, headers=headers).json()
rows.extend(page["value"])
url = page.get("@odata.nextLink")
:::tip Smaller pages
If Business Central times out or returns errors when loading large datasets,
limit the page size, for example to 5,000 rows, with the request header
Prefer: odata.maxpagesize=5000. When you are throttled (429), go lower,
for example 500.
:::
Not supported
- Writes. The data endpoints are read-only. (Custom endpoint definitions are written through the separate Management API.)
$expandon the data endpoints. Join on the relationships instead.- Aggregation (
$apply). Aggregate in your BI tool or warehouse.