Handle API Rate Limits in n8n Without Breaking Workflows
Your n8n workflow processes 100 records flawlessly. Then you run it with 1,000 records and it crashes halfway through with 429 Too Many Requests.
Rate limits exist to protect APIs from abuse, but they donβt care that your workflow was working fine yesterday. This guide shows you how to build n8n workflows that respect rate limits and keep running when others would fail.
Understanding API Rate Limits
Rate limits restrict how many API requests you can make in a given time period. When you exceed the limit, the API returns a 429 Too Many Requests error instead of your data.
Common rate limit structures:
| Type | Example | How It Works |
|---|---|---|
| Requests per second | 10 req/sec | Max 10 calls every second |
| Requests per minute | 60 req/min | Max 60 calls per minute (not 1/sec) |
| Requests per day | 1,000 req/day | Hard daily cap, resets at midnight |
| Concurrent requests | 5 simultaneous | Max 5 in-flight requests at once |
| Token-based | 90,000 tokens/min | AI APIs measure by data size, not requests |
What a 429 error looks like in n8n:
ERROR: Request failed with status code 429
NodeApiError: The service is receiving too many requests from you
Rate limit exceeded. Please retry after 60 seconds.
Quick Reference: Rate Limit Solutions
| Problem | Solution | Jump To |
|---|---|---|
| Processing too many items at once | Split In Batches node | Batching Requests |
| Requests firing too fast | Wait node between calls | Adding Delays |
| Occasional 429 errors | HTTP Request retry settings | Built-in Retries |
| Need smart retry logic | Exponential backoff in Code node | Exponential Backoff |
| Complex rate limit rules | Rate limiter pattern | Rate Limiter Pattern |
Using Built-in Retry Settings
Before building complex solutions, try n8nβs built-in retry mechanism in the HTTP Request node.
How to configure:
- Open your HTTP Request node
- Go to Settings (gear icon)
- Find Retry On Fail
- Configure:
- Retry On Fail: Enabled
- Max Tries: 3-5
- Wait Between Tries: 1000ms (or higher)
Settings:
βββ Retry On Fail: true
βββ Max Tries: 3
βββ Wait Between Tries (ms): 1000
When built-in retries work:
- Occasional rate limits from traffic spikes
- APIs that clear quickly after brief waits
- Low-volume workflows with infrequent 429s
When you need more:
- Processing hundreds or thousands of items
- APIs with strict per-second limits
- Multiple workflows hitting the same API
- Need exponential backoff (increasing delays)
For timeout-related issues that often accompany rate limits, see our guide to fixing n8n timeout errors.
Pattern 1: Batch Processing with Split In Batches
The most common rate limit fix: process items in smaller groups with pauses between them.
The workflow structure:
Trigger β Get Data β Split In Batches β Process β Wait β Loop Back
β
(When done) β Continue workflow
Step-by-step setup:
Step 1: Add Split In Batches Node
After your data source, add a Split In Batches node (also called βLoop Over Itemsβ):
Node: Split In Batches
βββ Batch Size: 10
βββ Options: (default)
The batch size depends on your APIβs rate limit:
- 10 req/sec limit: Batch size of 10, then wait 1 second
- 60 req/min limit: Batch size of 50, then wait 1 minute
- Concurrent limit of 5: Batch size of 5, process, repeat
Step 2: Add Your Processing Node
Connect the second output of Split In Batches to your HTTP Request or API node:
Split In Batches
βββ Output 1: "No items left" (workflow continues)
βββ Output 2: "Items in batch" β Your API node
Step 3: Add Wait Node
After your API node, add a Wait node:
Node: Wait
βββ Resume: After Time Interval
βββ Amount: 1
βββ Unit: Seconds
Step 4: Loop Back
Connect the Wait node back to the Split In Batches node input. This creates the loop.
Complete workflow example:
βββββββββββββββ ββββββββββββββββββββ βββββββββββββββ
β Trigger βββββΈβ Split In Batches βββββΈβ HTTP Req β
βββββββββββββββ ββββββββββββββββββββ βββββββββββββββ
β² β
β βΌ
ββββββββββ΄βββββββββ βββββββββββββββ
β Continue when β β Wait β
β done (out 1) β β 1 second β
βββββββββββββββββββ βββββββββββββββ
β
βΌ
(loops back)
Calculating Batch Size and Wait Time
Formula:
Batch Size = Rate Limit Γ Safety Margin
Wait Time = Time Window / (Rate Limit / Batch Size)
Examples:
| Rate Limit | Batch Size | Wait Time | Effective Rate |
|---|---|---|---|
| 10/second | 8 | 1 second | 8/sec (80% of limit) |
| 100/minute | 80 | 60 seconds | 80/min (80% of limit) |
| 1000/hour | 800 | 60 minutes | 800/hr (80% of limit) |
Why 80%? Leave headroom for:
- Other workflows using the same API
- Retry attempts
- Rate limit fluctuations
Pattern 2: Adding Delays Between Requests
For simple workflows processing items one at a time, add a Wait node after each API call.
When to use:
- Sequential processing (not batched)
- Low item counts (under 100)
- Simple rate limits (X per second)
Setup:
Trigger β Loop Over Items β HTTP Request β Wait (200ms) β Loop
Wait node settings:
Node: Wait
βββ Resume: After Time Interval
βββ Amount: 200
βββ Unit: Milliseconds
Calculate your delay:
// For a rate limit of 5 requests per second:
delay = 1000ms / 5 = 200ms per request
// For 60 requests per minute:
delay = 60000ms / 60 = 1000ms per request
// Add 20% safety margin:
safe_delay = delay Γ 1.2
Pattern 3: Exponential Backoff
When you hit a rate limit, waiting a fixed time isnβt always enough. Exponential backoff increases the wait time with each retry, reducing pressure on the API.
How it works:
- 1st retry: Wait 1 second
- 2nd retry: Wait 2 seconds
- 3rd retry: Wait 4 seconds
- 4th retry: Wait 8 seconds
Implementation with Code node:
Add this Code node before your HTTP Request:
// Get retry count from previous attempts (or start at 0)
const retryCount = $json.retryCount || 0;
const maxRetries = 5;
// Check if we've exceeded max retries
if (retryCount >= maxRetries) {
throw new Error(`Failed after ${maxRetries} retries`);
}
// Calculate delay with exponential backoff + jitter
const baseDelay = 1000; // 1 second
const maxDelay = 32000; // 32 seconds cap
const exponentialDelay = Math.min(baseDelay * Math.pow(2, retryCount), maxDelay);
// Add random jitter (Β±20%) to prevent thundering herd
const jitter = exponentialDelay * 0.2 * (Math.random() - 0.5);
const finalDelay = Math.round(exponentialDelay + jitter);
// Wait for the calculated delay
if (retryCount > 0) {
await new Promise(resolve => setTimeout(resolve, finalDelay));
}
// Pass through data with updated retry count
return {
json: {
...$json,
retryCount: retryCount,
nextRetryDelay: finalDelay
}
};
Handling 429 responses:
After your HTTP Request node, add an IF node to check for rate limits:
IF Node Conditions:
βββ Value 1: {{ $json.statusCode }}
βββ Operation: Equal
βββ Value 2: 429
If true (rate limited):
- Increment retry count
- Loop back to the exponential backoff Code node
If false (success):
- Continue with workflow
- Reset retry count for next item
Pattern 4: Rate Limiter with Sliding Window
For complex scenarios with multiple API calls or shared limits, implement a sliding window rate limiter.
Code node implementation:
// Configuration
const RATE_LIMIT = 10; // requests
const TIME_WINDOW = 1000; // milliseconds (1 second)
// Get or initialize the request log from static data
const staticData = $getWorkflowStaticData('global');
staticData.requestLog = staticData.requestLog || [];
// Clean old entries outside the time window
const now = Date.now();
staticData.requestLog = staticData.requestLog.filter(
timestamp => now - timestamp < TIME_WINDOW
);
// Check if we can make a request
if (staticData.requestLog.length >= RATE_LIMIT) {
// Calculate wait time until oldest request expires
const oldestRequest = Math.min(...staticData.requestLog);
const waitTime = TIME_WINDOW - (now - oldestRequest) + 100; // +100ms buffer
// Wait before proceeding
await new Promise(resolve => setTimeout(resolve, waitTime));
// Clean log again after waiting
const newNow = Date.now();
staticData.requestLog = staticData.requestLog.filter(
timestamp => newNow - timestamp < TIME_WINDOW
);
}
// Log this request
staticData.requestLog.push(Date.now());
// Continue with the item
return $input.all();
When to use this pattern:
- Multiple HTTP nodes hitting the same API
- Shared rate limits across workflows
- Need precise control over request timing
Service-Specific Rate Limit Settings
Different APIs have different limits. Hereβs how to configure n8n for popular services:
OpenAI / GPT APIs
Rate limits (vary by tier):
- Free tier: 3 RPM (requests per minute), 40,000 TPM (tokens per minute)
- Tier 1: 60 RPM, 60,000 TPM
- Tier 2+: Higher limits
Recommended n8n settings:
Split In Batches: 50 items
Wait: 60 seconds
Or for token-heavy requests:
Split In Batches: 10 items
Wait: 30 seconds
Pro tip: OpenAI rate limits are per-model. GPT-4 and GPT-3.5 have separate limits.
Airtable
- 5 requests per second per base
- Batch operations: 10 records per request
Recommended n8n settings:
Split In Batches: 10 records
Wait: 250 milliseconds
Use Airtableβs batch operations when possibleβcreating 10 records in one API call counts as 1 request, not 10.
HubSpot
- 100 requests per 10 seconds (standard)
- 150,000 requests per day
Recommended n8n settings:
Split In Batches: 80 items
Wait: 10 seconds
Google APIs (Sheets, Drive, Gmail)
Rate limits:
- Varies by API and quota
- Sheets: 300 requests per minute per project
- Drive: 1,000 requests per 100 seconds
Recommended n8n settings:
Split In Batches: 50 items
Wait: 20 seconds
Slack
Rate limits:
- Tier 1 methods: 1 request per second
- Tier 2 methods: 20 requests per minute
- Tier 3 methods: 50 requests per minute
Recommended n8n settings:
Split In Batches: 1 item (for posting messages)
Wait: 1 second
Notion
Rate limits:
- 3 requests per second
Recommended n8n settings:
Split In Batches: 3 items
Wait: 1 second
Handling Rate Limit Headers
Many APIs tell you exactly how to handle rate limits in their response headers:
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1640000000
Retry-After: 60
Extract and use these headers in a Code node:
const items = $input.all();
const results = [];
for (const item of items) {
// Check if we have rate limit info from previous request
const remaining = item.json.rateLimitRemaining;
const resetTime = item.json.rateLimitReset;
if (remaining !== undefined && remaining <= 1) {
// We're at the limit, wait until reset
const now = Math.floor(Date.now() / 1000);
const waitSeconds = Math.max(0, resetTime - now) + 1;
if (waitSeconds > 0) {
await new Promise(resolve =>
setTimeout(resolve, waitSeconds * 1000)
);
}
}
results.push(item);
}
return results;
After your HTTP Request, extract headers:
// In a Code node after HTTP Request
const response = $input.first();
return {
json: {
data: response.json,
rateLimitRemaining: parseInt(
response.headers['x-ratelimit-remaining'] || '999'
),
rateLimitReset: parseInt(
response.headers['x-ratelimit-reset'] || '0'
)
}
};
Monitoring Rate Limit Errors
Donβt wait for workflows to failβmonitor rate limits proactively.
Error Workflow for Rate Limits
Create an error handling workflow:
Error Trigger β IF (contains "429") β Alert (Slack/Email)
β Log to database
IF node condition:
{{ $json.error.message.includes('429') ||
$json.error.message.includes('rate limit') }}
Track Rate Limit Metrics
Add logging to your rate-limited workflows:
// Before making API calls, log the attempt
const staticData = $getWorkflowStaticData('global');
staticData.apiCalls = (staticData.apiCalls || 0) + 1;
staticData.lastCall = new Date().toISOString();
// After catching a 429
staticData.rateLimitHits = (staticData.rateLimitHits || 0) + 1;
staticData.lastRateLimitHit = new Date().toISOString();
return $input.all();
Common Mistakes to Avoid
Mistake 1: Not Accounting for All API Calls
Your workflow might make more API calls than you realize:
β Thinking: "I'm only calling the API once per item"
β Reality: OAuth refresh, pagination, and retries add extra calls
Fix: Count actual HTTP requests in n8nβs execution log, not just your main API nodes.
Mistake 2: Multiple Workflows Sharing Limits
Two workflows hitting the same API = double the requests against the same limit.
Fix: Use static data to coordinate across workflows, or stagger workflow execution times.
Mistake 3: Ignoring Burst Limits
Some APIs have both sustained limits AND burst limits:
Sustained: 100 requests/minute β
Burst: Max 20 requests in any 5-second window β
Fix: Even with correct per-minute pacing, add small delays between individual requests.
Mistake 4: Retrying Immediately on 429
// β Bad: Immediate retry
if (response.status === 429) {
return makeRequest(); // Just hammers the API
}
// β Good: Wait before retry
if (response.status === 429) {
const retryAfter = response.headers['retry-after'] || 60;
await sleep(retryAfter * 1000);
return makeRequest();
}
Testing Your Rate Limit Handling
Before processing production data, test your rate limit handling:
Test 1: Simulate Rate Limits
Use a Code node to randomly return 429 errors:
// Simulate 20% rate limit hits
if (Math.random() < 0.2) {
throw new Error('429 Too Many Requests (simulated)');
}
return $input.all();
Test 2: Time Your Batch Processing
Add timing to verify your pacing:
const startTime = Date.now();
const items = $input.all();
// After processing...
const elapsed = Date.now() - startTime;
const itemsPerSecond = items.length / (elapsed / 1000);
console.log(`Processed ${items.length} items in ${elapsed}ms`);
console.log(`Rate: ${itemsPerSecond.toFixed(2)} items/second`);
return items;
Test 3: Verify Backoff Behavior
Check that delays increase properly on retries:
const retryCount = $json.retryCount || 0;
const expectedDelay = 1000 * Math.pow(2, retryCount);
console.log(`Retry ${retryCount}: Expected delay ${expectedDelay}ms`);
Need Help?
Rate limit handling adds complexity to workflows. If youβre dealing with:
- High-volume data processing
- Multiple APIs with different limits
- Production workflows that canβt afford failures
Our workflow audit service can identify rate limit risks, and our development services build production-ready workflows with proper rate limiting from the start.
Frequently Asked Questions
What does a 429 error mean in n8n?
A 429 error means βToo Many Requestsββyouβve exceeded the APIβs rate limit. The API is telling n8n to slow down. This isnβt a bug in your workflow; itβs the API protecting itself from overload. The fix is to reduce your request frequency using batching, delays, or exponential backoff. Most APIs include a Retry-After header telling you how long to wait.
How do I know what rate limit an API has?
Check the APIβs documentation for rate limit informationβitβs usually in a section called βRate Limits,β βThrottling,β or βAPI Quotas.β If undocumented, look at response headers like X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset. You can also test empirically: send requests until you get a 429, then calculate the limit from the timing.
Whatβs the difference between Split In Batches and Loop Over Items?
Theyβre the same nodeββLoop Over Itemsβ is the display name, βSplit In Batchesβ is the technical name (n8n-nodes-base.splitInBatches). The node divides your items into smaller groups (batches) and processes them one batch at a time. Setting batch size to 1 processes items individually; setting it higher processes multiple items per loop iteration.
Should I use Wait node or setTimeout in Code node?
Use the Wait node for simple, fixed delaysβitβs cleaner and easier to configure. Use setTimeout in Code node when you need dynamic delays (like exponential backoff) or when the delay depends on API response data (like Retry-After headers). The Wait node pauses the entire workflow execution; setTimeout only pauses within that specific Code node.
How do I handle rate limits across multiple n8n workflows?
Use n8nβs static data with the βglobalβ scope to share rate limit tracking across workflows: $getWorkflowStaticData('global'). Store timestamps of recent requests and check this shared log before making new requests. Alternatively, stagger your workflow schedules so they donβt run simultaneously, or use a dedicated rate-limiting service external to n8n.