Rate limits
Burst limit, quota pools and the gateway limit of the Mangools API, and how to pace requests and handle 429 responses.
The Mangools API applies two independent limits to every account. A request has to pass both.
- Burst limit — how many requests you can send in a short period.
- Quota pools — how many lookups your plan allows per day or per month.
Either one answers with 429 Too Many Requests, but the body tells them apart. A third, per-IP limit sits in front of the API at the gateway (see Gateway limit).
Burst limit
| Client | Limit |
|---|---|
| API-key clients: plain REST, SDKs, curl, n8n HTTP Request nodes | 3 requests per 30-second sliding window, per account and per resource pool |
Mangools web apps and the hosted MCP server (mcp.mangools.com) | 3 requests per second |
For API-key clients, leave at least 11 seconds between requests to the same pool, because the 30-second window is inclusive at its edge: a request sent exactly 30 seconds after the first one still counts as inside the window. The window slides, so a fourth request sent within 30 seconds of the first one is refused even if your daily quota is untouched.
When you exceed the burst limit, the API answers 429 with a Retry-After header and this JSON body:
{
"error": {
"type": "RateLimit",
"message": "Too many requests in short period...",
"retry_after": 30
}
}retry_after (and the Retry-After header) is the length of the burst window in seconds: 30 for API-key clients, 1 for the web apps and the hosted MCP server. After waiting that long, all 3 slots of the window are free again.
What to do when you get a 429
- Look at
error.typein the JSON body. - If it is
RateLimit: you sent requests too fast, your quota is fine. Readerror.retry_after(a number of seconds; the same number is in theRetry-Afterheader). Wait exactly that many seconds. Then send the same request again, once. Do not send anything else to the API while you wait. - If it is
CreditLimit: your plan has no lookups left for this resource. Waiting does not help. CallGET /kwfinder/limitsto see what is left and when it resets, and stop. - If the body is HTML instead of JSON: the gateway refused you for sending more than 4 requests per second from one IP. Wait a second and slow down.
- From then on send one request at a time, never several at once, with at least 11 seconds between requests to the same pool.
If you are an AI agent or automation: call the Mangools API one request at a time, leave at least 11 seconds between requests to the same pool, and on a 429 with "type": "RateLimit" wait exactly retry_after seconds before retrying once; on "type": "CreditLimit" stop and report that the quota is spent.
Quota pools
Each paid endpoint spends lookups from a quota pool of your plan:
- Daily pools, counted over a rolling 24 hours, for example
serpsandrelated-keywords. - Monthly pools, for example
links.
Read the current state of every pool with Get current and free limits (GET /kwfinder/limits).
When a pool is spent, the API answers 429 with "type": "CreditLimit" and no retry_after. Waiting a few seconds does not help: the pool refills only when the rolling window or billing month moves on. Check GET /kwfinder/limits instead of retrying.
What is not counted
- Cached requests. An identical request repeated within 24 hours is served from cache and counts against neither the burst limit nor the quota.
- Refused requests. A request answered with
429is not counted either, so a retry afterretry_afterseconds starts with a full window.
Gateway limit
A gateway (nginx) in front of the API allows 4 requests per second per client IP. Its 429 is nginx's own HTML error page (text/html), not JSON, so check the Content-Type header before calling response.json(). See Errors.
Recommended pacing
- Send lookups sequentially. Do not run requests to the same pool in parallel.
- For a batch of 20 to 50 SERP or keyword lookups, leave at least 11 seconds between requests to the same pool (the 30-second window is inclusive at its edge).
- On a
429with"type": "RateLimit", waitretry_afterseconds (or theRetry-Afterheader) and retry the same request. Fixed short backoffs such as 2, 4 and 8 seconds are shorter than the 30-second window and fail again. - On a
429with"type": "CreditLimit", stop and checkGET /kwfinder/limits.
const API = 'https://api.mangools.com/v3';
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
async function mangoolsGet(path, apiKey, maxRetries = 3) {
for (let attempt = 0; ; attempt++) {
const res = await fetch(`${API}${path}`, { headers: { 'X-Access-Token': apiKey } });
if (res.status !== 429) {
return res.json();
}
const isJson = (res.headers.get('content-type') || '').includes('application/json');
const body = isJson ? await res.json() : null;
if (body?.error?.type === 'CreditLimit') {
throw new Error('Quota spent, check GET /kwfinder/limits');
}
if (attempt >= maxRetries) {
throw new Error('Rate limited, giving up');
}
// Burst limit: honour retry_after. Gateway HTML 429: fall back to the header or 1 s.
const seconds = body?.error?.retry_after ?? (Number(res.headers.get('retry-after')) || 1);
await sleep(seconds * 1000);
}
}
// Sequential batch, at least 11 seconds between lookups to the same pool.
for (const kw of keywords) {
const serp = await mangoolsGet(`/kwfinder/serps?kw=${encodeURIComponent(kw)}`, API_KEY);
// ... use serp
await sleep(11_000);
}