Rate Limits
API quotas by plan, posting velocity caps, and handling 429s.
API Request Limits
Quotas live at the workspace level: every API key, dashboard session, and MCP call in one organization draws from the same bucket. Windows slide, so each request ages out on its own and capacity trickles back continuously instead of unlocking all at once on a minute boundary.
One API key is enough, even when a single integration serves many end customers. Throughput follows the workspace plan, so extra keys buy no speed. Mint more keys to draw boundaries (a key per tenant, a read-only key for a status page), not to go faster.
| Plan | Requests per Minute |
|---|---|
| Free | 60 |
| Pay as you go | 600 |
Per-Second Limits For Analytics
Analytics endpoints run on a tighter clock: a 1-second window instead of a minute. Your per-second budget scales off the minute budget:
requests_per_second = max(6, requests_per_minute / 60)The 6 req/s floor exists for fan-out: a single dashboard render fires a handful of analytics calls at once, and the free tier shouldn't choke on its own overview page.
| Plan | Requests per Minute | Requests per Second (analytics) |
|---|---|---|
| Free | 60 | 6 |
| Pay as you go | 600 | 10 |
The per-second window covers:
GET /v1/analyticsGET /v1/analytics/timeline
Everything else runs on the per-minute budget above.
Rate Limit Headers
Every API response carries these headers:
| Header | Description |
|---|---|
X-RateLimit-Limit | Budget for the window behind this endpoint (per-minute, or per-second on analytics routes) |
X-RateLimit-Remaining | Calls left before the window fills |
X-RateLimit-Reset | Unix time (seconds) when the oldest in-flight request ages out |
Retry-After | 429 responses only: seconds to sit out before retrying |
Handling Rate Limits
Over budget and the API answers 429 Too Many Requests:
{
"error": "Rate limit exceeded. Please retry after 12 seconds.",
"code": "RATE_LIMITED",
"details": {
"currentCount": 600,
"limit": 600,
"retryAfterSeconds": 12
}
}Don't fly blind: watch X-RateLimit-Remaining and ease off as it drains. On a 429, sleep for Retry-After, a relative second count that is immune to clock skew between your server and ours. The body repeats the same number as details.retryAfterSeconds. Error shape and codes are covered in Error Handling.
Both SDKs throw on non-2xx. Trap the 429 and ride out the cooldown:
import { ResponseError } from "@onepostly/sdk";
try {
await posts.createPost({ /* … */ });
} catch (error) {
if (error instanceof ResponseError && error.response.status === 429) {
const retryAfter = Number(error.response.headers.get("Retry-After") ?? 60);
await new Promise((resolve) => setTimeout(resolve, retryAfter * 1000));
}
throw error;
}Posting Velocity Limits
Separate from API quotas, each connected account has its own publishing speed limit. Platforms throttle (or suspend) accounts that fire too aggressively, so these caps keep every account safely under that line:
| Limit | Value |
|---|---|
| Velocity cap | 25 posts per hour per account |
| Daily caps | Instagram 100, Facebook 100, Threads 250, X 50, Pinterest 25, 50 for every other platform |
The budget only counts posts that actually left the building. Queued, scheduled, and never-sent failures don't touch it, so lining up next month's calendar can't trip today's cap.
Publish past the pace and the call fails with a 429 naming the cooldown. Scheduled posts can't answer synchronously, so they fail terminally at fire time instead. Look for VELOCITY_LIMITED on the post.platform.failed webhook.
Analytics Data Freshness
See Analytics Freshness for details.
Tips For Staying Within Limits
- Remember the budget is shared - Every key, open dashboard tab, and MCP session in the workspace draws from the same bucket. Splitting traffic across keys buys zero headroom.
- Page through lists - Never pull everything at once. Use
limitwith the list endpoint's paging parameter. - Cache aggressively - Keep a local copy of slow-moving data instead of re-fetching it.
- Subscribe instead of polling - Webhooks tell you when a post publishes or fails. Polling burns quota for no reason.
- Batch destinations into one post - One
POST /v1/postswith several destinations costs a single API call. Velocity is still counted per destination.