I’m making multiple API calls using the HTTP Request node, but after a while I start getting 429 Too Many Requests errors. What’s the best way to handle API rate limits.
Hey @Victory1, while you wait for a response, here are some things that might help:
Suggested resources
Automatically matched to your question.
Docs:
Forum:
@Yo_its_prakash, @MutedJam, @Niffzy - you’ve helped with similar issues before, can you take a look?
Automatically suggested by n8n’s community bot. It’s a pilot - please share feedback here.
Pace your request to avoid overloading the API endpoint.
Add in sufficient wait time
A 429 error means you’ve exceeded the API’s request limit.
Try adding a Wait node between requests, process items in batches using Loop Over Items (Batching), or implement retry logic with exponential backoff.
It’s also worth checking the API documentation for rate limit guidelines to avoid unnecessary failures.
Hi @Victory1
Small but important distinction on top of the above — the HTTP Request node has its own Batching option, which is a different thing from Loop Over Items batching and needs no extra nodes:
1. HTTP Request → Options → Add Option → Batching
Items per Batch 1, Batch Interval 1000 = one request/second. No Loop Over Items, no Wait node, no rewiring.
2. Two catches worth knowing
- Within a batch, requests fire in parallel — Items per Batch
10is 10 simultaneous connections, not 10 spaced out. Keep it at 1 if the API caps concurrency. - n8n’s built-in Retry On Fail is fixed-interval and ignores
Retry-After— so it isn’t exponential backoff. For real backoff you need an IF + Wait loop reading the header yourself.
3. Work out which limit you’re hitting first
Log x-ratelimit-limit / -remaining / -reset from a successful response:
- per-second burst → pacing fixes it
- daily quota → pacing won’t, you need fewer calls
- concurrency cap → only Items per Batch 1 fixes it
Same 429, three different problems.
4. If the workflow runs many times in parallel
Pacing inside one execution won’t help — each run waits politely and they still flood the API together. Self-hosted: N8N_CONCURRENCY_PRODUCTION_LIMIT=1.
Which API is it? Happy to be more specific.
Hi @Victory1
If the API has a bulk or batch endpoint, the request count itself can come down. Put an Aggregate node before the HTTP Request node with Aggregate set to All Item Data, and the whole set arrives as one item under data. Reference it in the JSON body:
{{ JSON.stringify($json.data) }}
Fifty items then go out as one request instead of fifty.
Good breakdown from @anon71933009. Filling in the part he pointed
at but didn’t build, plus one category of 429 that none of the
above fixes.
The Retry-After loop, since it was mentioned but not shown
HTTP Request node: turn OFF Retry On Fail, and set Options >
Response > Never Error = true. You need the 429 to come back as
data, not as a thrown error, or you can’t read the header.
Then IF on {{ $json.statusCode }} equals 429 → Code node to work
out the wait → Wait node → loop back to the HTTP node.
Code node:
const h = $json.headers || {};
let sec = 1;
if (h[‘retry-after’]) {
const v = h[‘retry-after’];
// Retry-After is either delta-seconds or an HTTP-date
sec = isNaN(v) ? Math.max(0, (new Date(v) - Date.now()) / 1000)
: Number(v);
} else if (h[‘x-ratelimit-reset’]) {
const r = Number(h[‘x-ratelimit-reset’]);
// some APIs send epoch seconds, some send seconds-remaining
sec = r > 1e9 ? r - Math.floor(Date.now() / 1000) : r;
} else {
const n = $runIndex || 0;
sec = Math.min(2 ** n, 60);
}
// jitter — this line matters more than it looks
sec = sec * (0.5 + Math.random() * 0.5);
return [{ json: { waitSeconds: Math.ceil(sec) } }];
Three things in there that are easy to miss:
Retry-After is allowed to be an HTTP-date, not just a number of
seconds. Parsing it as an integer silently gives you NaN and a
zero-second wait, so you retry instantly and get 429 again.
x-ratelimit-reset is inconsistent across APIs — GitHub sends epoch
seconds, others send seconds-remaining. The magnitude check handles
both.
The jitter line is the one people skip. Without it, if you have 20
items that all hit 429 at the same moment, they all wait exactly
the same duration and all retry at the same instant. You’ve
rebuilt the original problem with an extra step. Randomising the
wait spreads them out.
The retry storm, which pacing doesn’t solve
Worth being explicit about: Retry On Fail retries per item. Fifty
items, three attempts each, and a 429 that hits at item 20 means
you can fire 90 extra requests into an API that just told you to
stop. Some APIs respond to that by extending the block.
If you’re using Retry On Fail on a batch, set Max Tries to 2 and
handle the real retrying with the loop above, where the pacing is
under your control.
The one nobody’s mentioned: token limits, not request limits
@Victory1 — if this is OpenAI, Anthropic, Cohere or any LLM API,
there’s a good chance you’re not hitting a request limit at all.
Those providers enforce two separate limits: requests per minute
and tokens per minute. TPM is usually the one you hit first, and
none of the pacing advice above touches it, because the constraint
is payload size rather than call frequency.
Symptom that tells you which: if the 429s get worse when your input
documents get longer, but the number of calls hasn’t changed, it’s
TPM.
Fixes are different too — you reduce tokens rather than slow down.
Trim the prompt, drop max_tokens if you’ve set it high (some
providers count the reservation against your budget, not the actual
output), split long documents, or route short calls to a smaller
model.
The response headers name it directly if you log them:
x-ratelimit-remaining-requests vs x-ratelimit-remaining-tokens.
Whichever hits zero first is your actual limit.
And one that wastes an afternoon
Some APIs don’t return 429 at all. Shopify’s GraphQL endpoint
returns 200 with a throttle status inside the body. So does more
than one payment provider.
If your workflow “isn’t erroring” but data is quietly missing,
check the response body rather than the status code. An IF on
statusCode will never catch it.
Which API is it? The right fix is different for all three cases.