Learn about API rate limits and how to work with them
Learn about API rate limits and how to work with them
In this article:
- Overview
- What SuperPath limits today
- Rate limit response headers
- Handling a 429 response
- Common causes of rate limiting
- Things to keep in mind
- FAQs
Overview
Rate limiting protects the SuperPath platform against bursts of incoming traffic, so that every organisation using it stays fast and available. When a request is rejected for exceeding a limit, the API responds with HTTP status 429, and your integration should treat that as "slow down and retry shortly" rather than as a permanent failure.
Limits are a safeguard, not a target. Even on endpoints where no limit is enforced today, build your integration to spread its work out over time, to back off when it sees a 429, and to use webhooks instead of polling wherever it can.
What SuperPath limits today
What is limited | Limit | Window | Scoped to | Response when exceeded |
|---|---|---|---|---|
Public, unauthenticated endpoints that use the shared IP limiter — currently the self-registration endpoints | 100 requests | 15 minutes | The calling IP address and the individual endpoint, so each endpoint gets its own count |
|
AI Agent messages | 500 messages by default (configurable per environment) | One UTC day, resetting at midnight UTC | Your organisation |
|
Authenticated REST endpoints on | No published per-request limit | — | — | — |
Note: "No published per-request limit" means exactly that — it is not a guarantee of unlimited throughput. SuperPath may introduce or adjust limits to protect platform stability, so your client should always be able to cope with a
429.
In hosted environments the request counts behind the IP limiter are held in a shared store, so the limit applies across all API instances rather than per instance. In other words, spreading your calls across multiple connections will not raise the effective limit — only spreading them out over time will.
If you expect an unusually large volume of traffic, for example a one-off data migration or a launch to a large population of learners, contact us at support@superpath.io ahead of time and we can advise on the safest way to run it.
Rate limit response headers
Endpoints protected by the IP limiter return standard RateLimit-* headers on every response, not just on rejected ones, so you can see how much of your allowance is left before you run out.
Header | Sent on | What it tells you |
|---|---|---|
| Every response from a limited endpoint | The maximum number of requests allowed in the current window |
| Every response from a limited endpoint | How many requests you have left in the current window |
| Every response from a limited endpoint | The number of seconds until the current window resets |
| Every response from a limited endpoint | The policy in force, expressed as |
|
| The number of seconds to wait before retrying |
Good to know: SuperPath sends the standard
RateLimit-*headers. The olderX-RateLimit-Limit,X-RateLimit-RemainingandX-RateLimit-Resetheaders are not sent, so do not build your client around them.
A rejected request looks like this:
HTTP/1.1 429 Too Many Requests
RateLimit-Policy: 100;w=900
RateLimit-Limit: 100
RateLimit-Remaining: 0
RateLimit-Reset: 412
Retry-After: 412
Content-Type: application/json; charset=utf-8
{
"error": "Too many requests. Please try again later."
}Handling a 429 response
The most reliable pattern is to watch for the 429 status code and retry on a schedule driven by the response itself:
- If a
Retry-Afterheader is present, wait at least that many seconds before retrying. It is the server telling you exactly when your window resets, so it always beats a guess. - If there is no
Retry-Afterheader, fall back to exponential backoff — double the wait between each attempt rather than retrying immediately. - Add a little randomness (jitter) to each wait so that multiple workers retrying at once do not all come back at the same moment and cause a thundering herd effect.
- Cap the number of retries and log the failure, so a sustained problem surfaces to you instead of looping silently.
async function callWithRetry(request, maxAttempts = 5) {
for (let attempt = 0; attempt < maxAttempts; attempt++) {
const response = await request();
if (response.status !== 429) return response;
// Prefer the server's own instruction; otherwise back off exponentially.
const retryAfter = Number(response.headers.get('Retry-After'));
const backoffSeconds = Number.isFinite(retryAfter) && retryAfter > 0
? retryAfter
: Math.pow(2, attempt);
const jitterSeconds = Math.random();
await new Promise(resolve => setTimeout(resolve, (backoffSeconds + jitterSeconds) * 1000));
}
throw new Error('Rate limited: giving up after the maximum number of retries.');
}
Retrying individual requests only gets you so far. If you regularly run high volumes, it is worth controlling the rate at which your application calls SuperPath at a global level — a client-side token bucket is a well-understood way to do this, and mature implementations exist in almost every language.
Common causes of rate limiting
- Bulk imports and migrations. Loading a large set of users, teams or completion records in a tight loop is the most common cause. Throttle the loop on your side rather than relying on the API to push back.
- Polling instead of subscribing. Repeatedly asking the API "has anything changed?" generates far more traffic than it needs to. Use webhooks to be told about changes as they happen.
- Retry storms. A failing job that retries immediately, with no backoff and no cap, can generate more load than the original work. Always back off and always cap your retries.
- Parallel workers sharing one allowance. Several workers running the same integration draw on the same allowance, because limits are counted per organisation or per IP address rather than per process.
- Registration pages exposed to the public internet. The self-registration endpoints are unauthenticated and therefore rate limited per IP address, which also means traffic from behind a single shared corporate NAT or proxy all counts against the same allowance.
Things to keep in mind
- A
429is always safe to retry — the request was rejected before it was processed, so nothing was partially created. - Limits may change without a code change on your side. Read the
RateLimit-*headers rather than hard-coding the numbers in this article. - Because the IP limiter counts each endpoint separately, being limited on one endpoint does not necessarily mean you are limited on another.
- The AI Agent daily allowance is counted per organisation on a UTC day and resets at midnight UTC, so it will not clear part-way through your working day if your team is in another time zone.
- If you suddenly start seeing a rising number of rate limited requests and cannot account for it, contact support@superpath.io.
FAQs
Which status code means I have been rate limited?429. Treat any other status code as a different kind of problem — see Learn how to handle and recover from errors received from the SuperPath API.
How long should I wait before retrying?
Use the Retry-After header if it is present, because it tells you precisely when your window resets. Otherwise use exponential backoff with jitter.
Can our limits be increased?
Get in touch at support@superpath.io with the volumes you need and what you are trying to do. Tell us well in advance of the date you need it, especially for a large one-off migration, so there is time to plan it properly.
Do rate limits apply per API key or per organisation?
The AI Agent daily allowance is counted per organisation. The limiter on public, unauthenticated endpoints is counted per IP address and per endpoint, and does not involve an API key at all, because those endpoints are called before authentication.
Does a rejected request still count against my allowance?
On the IP-limited endpoints, yes — every request is counted, including the ones that come back as 429. It does not push the reset out, though: the window runs for a fixed period from your first request in it, so RateLimit-Reset and Retry-After remain the times to trust.
Updated on: 25/07/2026
Thank you!
