API Basics
Rate Limits
Request limits, rate limit headers, and how many posts and actions each account can take.
The API limits workspace requests, and each account's posts and actions.
API Request Limits
Limits apply to the whole workspace. Every API key, dashboard session and MCP app shares them, so more keys do not raise them. The limit follows the number of connected accounts, as the table shows. See Pricing.
| Workspace | Requests per minute | Analytics per second |
|---|---|---|
| Free, up to 2 accounts | 60 | 6 |
| Paid, 3 or more accounts | 600 | 10 |
- Analytics endpoints have their own per-second limit, apart from the per-minute one. These are
GET /v1/posts/{id}/analytics,GET /v1/posts/{id}/analytics/timelineandGET /v1/accounts/{id}/analytics. - The windows roll. A request counts for 60 seconds, or 1 second on analytics.
Rate Limit Headers
Every authenticated response has these headers.
| Header | Description |
|---|---|
X-RateLimit-Limit | The window's limit |
X-RateLimit-Remaining | Requests left in the window |
X-RateLimit-Reset | Unix seconds when the oldest request stops counting |
Retry-After | Seconds to wait, only on a 429 |
Handle 429 Responses
Over the limit, you get 429 RATE_LIMITED with a Retry-After header.
{
"error": "Rate limit exceeded. Please retry after 12 seconds.",
"code": "RATE_LIMITED",
"details": {
"currentCount": 600,
"limit": 600,
"retryAfterSeconds": 12
},
"requestId": "req_8f2a64c1d3b94e0f9a7c5e2b1d0f3a6c"
}details.retryAfterSeconds matches Retry-After. A 429 request did nothing, so send it again unchanged after the wait.
The SDKs throw on every error. This helper waits for Retry-After and retries a few times. It gives up on other codes and on waits over 60 seconds, such as a daily limit, and throws the error again.
import { Configuration, PostsApi, ResponseError } from "@onepostly/sdk";
const postsApi = new PostsApi(
new Configuration({ accessToken: process.env.ONEPOSTLY_API_KEY })
);
async function withRetry<T>(
call: () => Promise<T>,
{ maxAttempts = 3, maxWaitSeconds = 60 } = {}
): Promise<T> {
for (let attempt = 1; ; attempt++) {
try {
return await call();
} catch (error) {
if (!(error instanceof ResponseError) || error.response.status !== 429) {
throw error;
}
const envelope = await error.response.json().catch(() => null);
const waitSeconds = Number(
error.response.headers.get("Retry-After") ??
envelope?.details?.retryAfterSeconds ??
1
);
if (
envelope?.code !== "RATE_LIMITED" ||
attempt >= maxAttempts ||
waitSeconds > maxWaitSeconds
) {
throw error;
}
await new Promise((resolve) => setTimeout(resolve, waitSeconds * 1000));
}
}
}
const response = await withRetry(() =>
postsApi.createPost({
createPostBody: {
text: "Release notes are live.",
accounts: [{ accountId: "acc_YOUR_X_ACCOUNT_ID" }],
},
})
);
console.log(response);Publishing Limits
Each account can publish 25 posts in any 60 minutes. It can also publish only a set number per day, which depends on the platform.
| Platform | Posts per day |
|---|---|
| X | 50 |
| 100 | |
| 100 | |
| Threads | 250 |
| 50 | |
| YouTube | 50 |
| TikTok | 50 |
| 25 | |
| Bluesky | 50 |
- The daily limit resets at 00:00 UTC.
- Only published posts count, not scheduled or failed ones.
- A post counts once per account. A request to 3 accounts counts for each.
| Post | Over the limit | What to do |
|---|---|---|
| Published now | 429 RATE_LIMITED. Nothing is created, for any account | Wait for Retry-After, or drop that account |
| Scheduled | Ends failed with RATE_LIMITED. Other accounts are not affected | Create a new post later |
A scheduled post's error.message says how long to wait.
Action Limits
Each account can take only so many platform actions in a window, such as replies, likes and messages. These follow the platform's own limits. Threads, for example, allows 1,000 replies in 24 hours.
| Request | Over the limit |
|---|---|
| A comment, like, repost, bookmark, delete or message | 429 QUOTA_EXCEEDED, with no Retry-After |
| A post | Ends failed with QUOTA_EXCEEDED |
The error message says when to try again. Nothing happened on the platform, and the limit does not affect other accounts.
Platform Limits
Platforms set their own limits and do not publish them. When an account hits one, the response or the post shows one of these codes.
| Code | Meaning | What to do |
|---|---|---|
RATE_LIMITED | The platform slowed the account | Wait, then post again |
QUOTA_EXCEEDED | An action limit, or the platform is at capacity | Try again after the time in the message |
POSTING_LIMIT_REACHED | TikTok says the account posted too often | Wait before posting again |
CAPACITY_LIMITED | TikTok is not taking more direct posts now | Try again in a few hours |
UPLOAD_LIMIT_EXCEEDED | The YouTube channel hit its upload limit | Wait for YouTube's reset |
Other codes are in Error Codes.
Best Practices
- Slow down before
X-RateLimit-Remainingreaches0. - Send one
POST /v1/postswith severalaccounts. It is one API request, though publishing limits count per account. - Page with
limitandcursorinstead of fetching everything. - Use webhooks instead of polling post status.
- Spread one account's scheduled posts over the day to stay under the hourly limit.