Rate limits

The admin API is rate limited per organization, not per key. Minting a second secret key does not buy a second allowance: every key an organization holds draws on the same bucket.

Requests you make from the dashboard while signed in are not counted. The limit applies only to requests authenticated with a secret key.

The ceiling

Burst600 tokens
Refill10 tokens per second

It is a token bucket. Every request spends tokens, and the bucket refills continuously at 10 a second up to its 600 ceiling. An idle integration therefore starts with the full 600 and can spend it as fast as it likes, then settles into 10 requests a second for as long as it keeps going.

Those numbers are sized for the heaviest thing a well-behaved integration does: a one-time migration that reads everything you have. At the maximum page size of 250 records, a 100,000 record catalog is 400 requests, which fits inside a single burst with room to spare. Nothing that walks your data page by page should ever see a 429.

The ceiling is your plan's apiRequestsPerMinute allowance, so it is a number on your organization rather than a constant. GET /organization returns the value your requests are actually measured against.

What each endpoint costs

Most endpoints spend one token. The ones that do noticeably more work per request cost more, so a loop over them is bounded by the work it causes rather than by the number of requests it makes.

CostEndpointsWhy
10POST /customers/import, POST /orders/import, POST /products/import, POST /reviews/import, POST /wishlists/import, POST /loyalty-memberships/import, POST /loyalty-historyOne call carries an unbounded batch of records and writes all of them.
10POST /import-jobs, POST /export-jobsQueues a background job that reads or writes up to 100,000 rows.
5POST /analytics/queryAggregates a whole resource, which is the most expensive read in the API.
5PATCH /customers/bulk-update, PATCH /orders/bulk-update, PATCH /products/bulk-update, PATCH /reviews/bulk-update, POST /loyalty-activity/bulk-expireUpdates every record matching a filter in one request.
1Everything elseOrdinary reads and single-record writes.

Each endpoint's cost is on its page in the API reference, and in the OpenAPI document as x-rate-limit-cost on the operation, so you can plan against it without reading this table.

Note that the cost is charged on reads as well as writes. Paginating a list endpoint without advancing your cursor is the most common way to blow through a limit, and only counting writes would miss it entirely.

When you go over

A request that cannot afford its cost gets a 429 and does nothing else. It is not queued and it is not partially applied.

{
  "error": {
    "code": "TOO_MANY_REQUESTS",
    "message": "Too many API requests for this organization. Slow down and try again in a moment."
  }
}

The response carries a Retry-After header with the whole number of seconds until you can afford the same request again. Wait that long rather than retrying immediately: a tighter retry loop only makes the wait longer for everything else your organization is doing.

HTTP/1.1 429 Too Many Requests
Retry-After: 3

The endpoints with their own limits

Three endpoints carry a separate bucket on top of this one, because they are called from your own backend at a very different rhythm:

  • POST /loyalty-events and POST /loyalty-stamps: a burst of 120, refilling at two a second. These follow your plan's loyaltyEventsPerMinute and loyaltyStampsPerMinute allowances.
  • POST /loyalty-history: a burst of 30, refilling at one a second. It is deliberately tighter than the ceiling would be on its own, so a runaway loop is told which endpoint it exhausted.

These are per organization and per endpoint, so they do not compete with each other. A request to one of them spends from both its own bucket and the organization-wide one.

Raising the limit

The ceiling is a field on your organization, so it can be raised for your account without a release. If a legitimate integration cannot fit inside it, get in touch before you start working around it: an import endpoint or a background export job is usually a better answer than a faster loop.