Rate Limits
The Schedulala API enforces rate limits to ensure fair usage and platform stability. The API, MCP connector and CLI are included with every Schedulala plan, so your limits match your plan. Request limits are applied per API key.
Limits by Plan
| Plan | Requests / Minute | Posts / Month | Connected Accounts |
|---|---|---|---|
| Free | 30 | 4 | 2 |
| Personal ($29/mo) | 120 | Unlimited | 10 |
| Team ($49/mo) | 120 | Unlimited | 25 |
| Agency ($99/mo) | 120 | Unlimited | 100 |
Daily Posting Limits
To comply with each social platform's own rate limits, daily posts per profile are capped. These limits apply to all plans:
| Platform | Daily Limit |
|---|---|
| X / Twitter | 20 posts/day |
| 100 posts/day | |
| 100 posts/day | |
| Threads | 250 posts/day |
| All other platforms | 50 posts/day |
Rate Limit Headers
Every API response includes headers that tell you your current rate limit status:
X-RateLimit-Limit: 120X-RateLimit-Remaining: 118X-RateLimit-Reset: 1704825060000
| Header | Description |
|---|---|
X-RateLimit-Limit | Maximum number of requests allowed per minute for your plan |
X-RateLimit-Remaining | Number of requests remaining in the current window |
X-RateLimit-Reset | Unix timestamp (milliseconds) when the rate limit window resets |
Tip: Check the X-RateLimit-Remaining header before making requests in tight loops. This lets you throttle proactively instead of hitting 429 errors.
When You Hit the Limit
If you exceed the rate limit, the API returns a 429 Too Many Requests response:
HTTP/1.1 429 Too Many RequestsX-RateLimit-Limit: 120X-RateLimit-Remaining: 0X-RateLimit-Reset: 1704825060000
Use the X-RateLimit-Reset header (a Unix timestamp in milliseconds) to determine when you can make requests again. The response body will also include an error message:
{"error": {"code": "rate_limit_exceeded","message": "Rate limit exceeded. Please retry after the reset period."}}
Unauthenticated endpoints (key registration, OAuth, and the device flow) are rate limited per IP instead of per key. They return 429 with a Retry-After header telling you how many seconds to wait.
Handling Rate Limits
The recommended approach is exponential backoff: wait progressively longer between retries, and use the X-RateLimit-Reset header to calculate how long to wait.
async function fetchWithRetry(url, options, maxRetries = 3) {for (let attempt = 0; attempt <= maxRetries; attempt++) {const response = await fetch(url, options);if (response.status !== 429) {return response;}// Use X-RateLimit-Reset header (ms timestamp), otherwise exponential backoffconst resetMs = response.headers.get('X-RateLimit-Reset');const waitMs = resetMs? Math.max(0, parseInt(resetMs, 10) - Date.now()): Math.pow(2, attempt) * 2000; // 2s, 4s, 8sconst waitSeconds = Math.ceil(waitMs / 1000);console.log(`Rate limited. Retrying in ${waitSeconds}s (attempt ${attempt + 1}/${maxRetries})`);await new Promise(resolve => setTimeout(resolve, waitMs));}throw new Error('Max retries exceeded');}// Usageconst response = await fetchWithRetry('https://schedulala.com/api/v1/posts',{method: 'POST',headers: {'Authorization': `Bearer ${process.env.SCHEDULALA_API_KEY}`,'Content-Type': 'application/json'},body: JSON.stringify({content: 'Hello world!',platforms: [{ platform: 'twitter', accountId: 'acc_123' }],publishNow: true})});
Best Practices
Use exponential backoff
When retrying after a 429, increase the wait time exponentially (e.g., 2s, 4s, 8s). Use the X-RateLimit-Reset header to calculate the exact wait time — it's a Unix timestamp in milliseconds indicating when your rate limit window resets.
Check X-RateLimit-Remaining
Monitor this header on every response. If you see it approaching zero, slow down your request rate before you get rate limited. This is especially important for batch operations.
Cache responses when possible
Endpoints like GET /accounts and GET /posts/:id return data that doesn't change frequently. Cache these responses locally to reduce the number of API calls you need to make.
Spread requests over time
If you need to create many posts, spread them across multiple minutes rather than sending them all at once. This avoids bursting through your per-minute limit.
Note: Repeatedly ignoring rate limits and sending requests without backoff may result in your API key being temporarily suspended. Always implement proper retry logic.