Rate Limits
Two limits apply, and a request can run into either one:
| Limit | Applies to | Allowance |
|---|---|---|
| Route limit | Every API request, including /auth and /me | 60 requests per minute per client IP |
| Resource limit | Every endpoint except /auth and /me | 30 requests per minute |
In practice the resource limit is the one you’ll meet: a job that only calls resource endpoints runs out at 30 a minute. /auth and /me don’t count against it, which makes them cheap health checks.
The route limit is per client IP, so integrations and workers that share an IP address share those 60 requests.
The headers
Section titled “The headers”Resource endpoints report the 30-per-minute window on successful responses, and on 429s from that limit:
X-RateLimit-Limit: 30X-RateLimit-Remaining: 24X-RateLimit-Reset: 47| Header | Meaning |
|---|---|
X-RateLimit-Limit | Maximum requests allowed in the window |
X-RateLimit-Remaining | Requests left in the window |
X-RateLimit-Reset | Seconds until another request is available |
/auth and /me report the route limit instead: X-RateLimit-Limit: 60 and the requests left in that bucket. When the route limit rejects a request, on any endpoint, the headers describe that limit: X-RateLimit-Limit is 60, and X-RateLimit-Reset is a Unix timestamp, not a number of seconds.
So don’t carry a count from one endpoint to another. X-RateLimit-Remaining from /me describes the 60-request bucket, not the 30 your resource calls draw on.
When you get a 429
Section titled “When you get a 429”Stop sending, and wait:
- If
Retry-Afteris present, honor it. It’s a number of seconds whichever limit fired. - Otherwise wait for
X-RateLimit-Reset. Both windows are one minute long, so a value above60is a Unix timestamp rather than a number of seconds.
The body depends on which limit fired. The resource limit returns:
{ "message": "Too many requests. Please try again later.", "errors": { "rate_limit": "Too many requests. Please try again later." }}The route limit returns only a message:
{ "message": "Too Many Attempts."}Neither has a status field. See Errors for how these fit with the other error shapes.
Backing off well
Section titled “Backing off well”- Queue your work rather than firing many requests concurrently.
- Cache stable IDs. Brand and collection IDs don’t change during a job. Resolve them once.
- Page efficiently.
per_pagegoes up to 100, so one page often beats four. - Use the narrowest list. Post lists filter on the server:
?view=SCHEDULEDon one brand-wide call beats paging through every collection. - Add jitter when several workers share one key or one IP, or they’ll all retry in lockstep and re-trigger the limit.
- Retry reads after the server’s reset time.
- Don’t blindly retry writes after a timeout or a
5xx. Without a documented idempotency key, the request may have already succeeded. See Errors.
The limits apply on every surface
Section titled “The limits apply on every surface”MCP tool calls and CLI commands go through the same public API, so the same limits apply to them. This matters more than it sounds: one conversational request like “show me everything in my account” can chain a brand list, then a collection, channel, media, and post list per brand, and spend a dozen requests before it answers.