Developer docs
On this page
How the limit works#
- Each key has its own limit. It isn't shared with your other keys or counted by IP address.
- A key can send up to its whole limit at once, then refills steadily through the minute. At 120 a minute, that's 2 more requests every second.
- The limit is set when the key is created, under Requests per minute: empty means 120, and you can choose 1 to 6000. To change it, create a new key.
Headers#
| Header | What it says |
|---|---|
X-RateLimit-Limit | Requests this key may make a minute. |
X-RateLimit-Remaining | Requests it can make right now. |
Retry-After | On a 429 only: seconds to wait before you try again. |
A request refused before its key is checked, such as a 401 for a missing key, carries none of these.
HTTP/1.1 429
Retry-After: 2
X-RateLimit-Limit: 30
X-RateLimit-Remaining: 0
Content-Type: application/problem+json
{
"type": "urn:problem:rate_limited",
"title": "Too many requests",
"status": 429,
"detail": "This key is limited to 30 requests a minute. Retry after 2 seconds.",
"instance": "/api/v1/subscribers",
"code": "rate_limited"
}Stay under it#
- Use webhooks instead of asking every few seconds whether something changed.
- Use lookup to find one subscriber instead of paging through all of them.
- Ask for size=200 when you read in bulk.
- Spread big jobs out, and slow down when X-RateLimit-Remaining gets low.
- On a 429, wait Retry-After seconds. Retrying at once only fails again.
// Wait as long as CoreSync asks, then try again.
async function withRateLimit(call, tries = 5) {
for (let i = 1; ; i++) {
try {
return await call();
} catch (err) {
if (err.status !== 429 || i === tries) throw err;
await new Promise((r) => setTimeout(r, (err.retryAfter || 1) * 1000));
}
}
}
const plans = await withRateLimit(() => api("/plans", { query: { status: "ACTIVE" } }));Stuck, or missing something? Tell us what you're building.
hello@coresyncisp.com