Skip to content
Start Trading

API Fundamentals

Understand response envelopes, pagination, and API error handling.

All API responses follow a consistent envelope:

{
"data": ...,
"error": null,
"metadata": {
"request_id": "523be14bfc539676885a2ef4629e5322",
"page_number": 1,
"total_items": 560,
"total_pages": 6,
"next_page_token": "AAAAAAAAAGQAAAAAAAAAZA",
"previous_page_token": null
}
}
  • data contains the response payload — a single object for detail endpoints, an array for list endpoints.
  • error is null on success. On failure, it contains a structured error object.
  • metadata always includes request_id. Pagination fields appear only on list endpoints.

Endpoints support page_size and page_token query parameters:

GET /v1/accounts/{account_id}/orders?page_size=6&page_token=AAAAAAAAAGQAAAAAAAAAZA

The response metadata includes page_number, next_page_token, and previous_page_token. Some endpoints also return total_items and total_pages; where computing an exact total would be expensive they are omitted, and next_page_token is the way to tell whether more results exist.

"metadata": {
"request_id": "523be14bfc539676885a2ef4629e5322",
"page_number": 1,
"total_items": 560,
"total_pages": 6,
"next_page_token": "AAAAAAAAAGQAAAAAAAAAZA"
}

Some list endpoints filter a field by a range, using <field>.<op> query parameters:

GET /v1/accounts/{account_id}/orders?updated_at.gte=2025-04-24&updated_at.lt=2025-04-25

<field> names the filtered field (for example, updated_at). <op> is one of:

OperatorMeaning
gtstrictly after
gteon or after
ltstrictly before
lteon or before

Set any combination of these on the same field; every value you set must hold (AND semantics).

Each parameter accepts either:

  • An RFC3339 datetime, for example 2025-04-24T14:30:00Z.
  • A bare YYYY-MM-DD date, for example 2025-04-24.

A bare date expands to a specific instant depending on the operator, so every operator reads as a bound on whole days (all times UTC):

OperatorA bare date D expands toEffect
gtD 23:59:59.999999999matches from the start of D+1
gteD 00:00:00matches from the start of D
ltD 00:00:00matches up to (not including) the start of D
lteD 23:59:59.999999999matches through the end of D

Combine a lower bound (gt/gte) and an upper bound (lt/lte) on the same field to filter a window:

GET /v1/accounts/{account_id}/orders?updated_at.gte=2025-04-24&updated_at.lt=2025-04-25

This returns rows updated anywhere in the day 2025-04-24 (UTC).

If the resolved lower bound is strictly after the resolved upper bound, the request fails with 400. Equal bounds are allowed (they match a single instant).

When an error occurs, the response includes a structured error in the error field as shown with a code along with a descriptive message.

{
"error": {
"code": 422,
"message": "Invalid limit price: must be positive"
},
"metadata": {
"request_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}
}
CodeMeaning
200Success
400Bad request — the request body or query parameters failed validation. Inspect error.message for the specific field and reason; correct and retry.
401Unauthorized — the Authorization header is missing or contains an invalid or expired API key. Generate a new key in the web app and retry.
403Forbidden — the API key is valid but lacks permission for this account or action. Verify the account ID and the key’s scope; contact support if access should be granted.
404Not found — the requested account, order, or resource does not exist or is not visible to this API key. Verify the ID in the path.
422Unprocessable — the request was syntactically valid but rejected by a downstream check (e.g., risk, buying power, restricted list). The error.code field identifies the specific rejection reason; see the Order Rejection Reasons reference.
500Internal server error — an unexpected failure occurred on the server. Retry with exponential backoff. If the problem persists, contact support and include the request_id from the response metadata.