Skip to main content

Querying the endpoints

All endpoints accept standard OData query options. Use them to read only the rows and fields you need.

OptionPurposeExample
$filterFilter rows$filter=postingDate ge 2026-01-01
$selectPick fields$select=entryNo,postingDate,gLAccountNo,amount
$orderbySort$orderby=postingDate desc
$topLimit the number of rows$top=100
$skipSkip rows$skip=200
Prefer: odata.maxpagesize=NPage 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.)
  • $expand on the data endpoints. Join on the relationships instead.
  • Aggregation ($apply). Aggregate in your BI tool or warehouse.