Rate limits
Every GraphQL request is assigned a numeric cost before it is executed. Each merchant has a budget that refills at a steady rate up to its maximum bucket size. As long as a request fits into the currently available budget, it is executed and its cost is deducted; otherwise it is rejected with a THROTTLED error.
Rate limits are tracked per merchant, not per API token. Creating additional tokens does not raise the budget.
Budgets by plan
The API is available on the Professional and Enterprise plans. Each of those plans has a bucket size (the maximum budget available at any moment) and a restore rate (how quickly the budget refills, in points per second). Short bursts are allowed up to the bucket size; sustained throughput is capped by the restore rate.
| Plan | Restore rate | Bucket size |
|---|---|---|
| Professional | 500 points/s | 5,000 points |
| Enterprise | 2,000 points/s | 20,000 points |
A merchant on the Professional plan, for example, can burst up to 5,000 points of request cost from a full bucket and sustain about 500 points/s of cost on average once the bucket has been drawn down.
Single-query cap
In addition to the per-merchant bucket, every individual request is subject to a hard upper bound on cost. Requests that exceed this cap are rejected up front, even when the merchant's bucket would have room for them. This cap exists to keep individual queries from monopolizing API capacity and to protect downstream services from very large operations.
If a single request fails because it exceeds the cap, split the operation into smaller pages or query a smaller set of fields rather than retrying with the same shape.
Inspecting the current budget
Every response includes the current budget state in the GraphQL extensions object:
{
"extensions": {
"cost": {
"requestedQueryCost": 350,
"throttleStatus": {
"maximumAvailable": 5000,
"currentlyAvailable": 4650,
"restoreRate": 500
}
}
}
}
requestedQueryCost— the cost calculated for the request before execution and charged against the bucket.throttleStatus.maximumAvailable— the bucket size for this merchant.throttleStatus.currentlyAvailable— points remaining in the bucket right after this response.throttleStatus.restoreRate— points per second added to the bucket until it is full.
Clients that batch synchronization work can use currentlyAvailable and restoreRate to pace requests instead of relying on retries.
Handling throttling
When a request does not fit into the current budget, the API returns a GraphQL error with the THROTTLED code and a retryAfter field in the error's extensions:
{
"errors": [
{
"message": "Throttled",
"extensions": {
"code": "THROTTLED",
"retryAfter": 1.7
}
}
]
}
retryAfter is the number of seconds the client should wait before sending the next request. Back off until that time has elapsed before retrying.
Reducing request cost
Cost grows with the number of items returned by list fields and with the depth of nested connections, so the most effective ways to lower cost are:
- Request only the fields the integration needs; drop nested connections that are not used.
- Use smaller page sizes on list fields, and paginate explicitly with
first+after. - Cache stable objects such as warehouses, bin locations, and suppliers locally where possible.
- Prefer incremental synchronization by filtering on fields such as
updatedAtwhen the relevant type exposes them.