Limits are per workspace: every API key in it, live or test, shares the same count. Every response to an API key says where you stand, so your client never has to guess.
Limits by plan
Requests are counted in two windows at once, one minute and ten minutes. A request over either limit is refused.
| Plan | Requests a minute | Requests per 10 minutes |
|---|---|---|
| Free | 60 | 300 |
| Starter | 60 | 300 |
| Pro | 100 | 500 |
| Agency | 200 | 1,000 |
| Scale | 500 | 2,500 |
Every call counts once: a post, a status check, a profile lookup. A post to five networks is still one request (it counts as five uploads against a monthly upload limit, which only the Free plan has; see Plans and limits).
The windows are fixed, not rolling: the minute window starts again at the top of each minute and the ten-minute window at :00, :10, :20 and so on (UTC).
Headers
Every answer to an API key carries:
| Header | Meaning |
|---|---|
X-RateLimit-Limit |
Requests allowed in the window that is closest to running out. |
X-RateLimit-Remaining |
Requests left in that window. |
X-RateLimit-Reset |
Unix time, in seconds, when that window starts again. |
Retry-After |
On a 429 only: seconds to wait before trying again. |
The headers describe whichever window is tighter at that moment, so X-RateLimit-Limit can switch between the per-minute and the per-10-minute figure.
await reelspan.me();
console.log(reelspan.rateLimit); // { limit: 100, remaining: 97, reset: 1796198460 }reelspan.me()
print(reelspan.rate_limit) # {'limit': 100, 'remaining': 97, 'reset': 1796198460}When you go over
HTTP/1.1 429 Too Many Requests
Retry-After: 23
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1796198460
{ "success": false, "message": "Rate limit exceeded", "error_code": "RATE_LIMITED" }Wait for Retry-After seconds, then send the request again. A refused request was not carried out, so retrying it is safe; for posts, keep the same Idempotency-Key anyway (see Retrying safely). The Node.js and Python SDKs wait and retry 429s on their own (up to 60 seconds at a time, twice by default).
Other 429s are not about the request rate. Tell them apart by error_code:
error_code |
Cause |
|---|---|
RATE_LIMITED |
The request rate above. |
AUTH_RATE_LIMITED |
10 failed authentications from your IP address within 5 minutes. Every request from that address is refused for up to 5 minutes (Retry-After: 300). Check your key before trying again. |
TEST_LIMIT |
500 test-key posts in a day. |
USAGE_LIMIT, or a 429 with usage |
The monthly upload limit. Waiting a few seconds won't help: it resets on the 1st (UTC), or upgrade. |
AI_CREDITS_EXHAUSTED |
No AI caption credits left this month. |
A few actions have their own, smaller limits on top: connecting an account with credentials (POST /v1/accounts/{platform}) allows 30 attempts per 10 minutes per workspace, and manual webhook tests and redeliveries allow 10 a minute and 100 an hour.
Polling for status
A status check costs one request like any other, so polling many posts at once can use up your limit. Poll Post status every 5 seconds or more, and back off for big videos, which can take minutes on some networks:
| Post | Suggested interval |
|---|---|
| Text and photos | every 5 seconds |
| Short video | every 5 to 10 seconds |
| Long or large video | every 15 to 30 seconds |
| Scheduled post | don't poll before its scheduled_date |
Better still, don't poll: an upload_completed webhook arrives for each network as it finishes, at no cost to your limit. And requests that finish within 59 seconds return the results directly, with nothing to poll.
Good habits
- Read
X-RateLimit-Remainingand slow down before you reach zero, rather than waiting for a 429. - Spread bulk jobs out: a burst of hundreds of posts is better sent as a steady stream, or scheduled with
scheduled_dateor the queue. - Post to many networks in one request instead of one request per network.
- Page through history with
limit=100instead of many small pages.