Overview
When you need to call multiple APIs from a single n8n workflow, two common challenges appear: keeping OAuth2 access tokens fresh (token rotation) and handling transient errors or rate limits gracefully. This tutorial walks you through a robust pattern using n8n nodes and a small Function (Code) node to implement token refresh, store tokens safely, and apply exponential backoff with jitter and retry limits.
What you’ll learn:
- How to store and rotate OAuth2 tokens for multiple APIs in one workflow
- How to detect expired tokens and refresh them automatically
- How to implement exponential backoff with jitter and a delay node
- Best practices for security, scaling, and monitoring
- Use workflow static data (or an external store for multi-instance setups) to keep tokens across executions.
- Use a dedicated Token Refresh sub-flow that calls each provider’s token endpoint.
- Use a Function node to compute backoff delays and persist retry counts.
- tokens:
- Trigger node (Cron or Webhook)
- Set node to create a list of requests or read from a data source
- SplitInBatches (or SplitOutItems) to iterate
- Before the HTTP Request node, add a Function node to attach Authorization header using the stored token
This guide is aimed at intermediate to advanced n8n users comfortable with basic nodes, expressions, and the Function/Code node.
—
Architecture & Design
High-level flow:
1. Trigger (schedule / webhook / manual)
2. Prepare list of API calls (Set or Read from DB)
3. For each API call: attach the correct access_token (from workflow static data or centralized store)
4. Execute HTTP Request node
5. If 401/invalid token → refresh token flow → store new tokens → retry original request
6. If other transient error (429, 5xx) → apply exponential backoff + jitter and retry up to max attempts
7. Log final success/failure and proceed
Key decisions:
—
Step-by-step Implementation
1) Store tokens in workflow static data
You can store per-API tokens in workflow static data so subsequent runs can reuse them. For single-instance n8n this is fine; for clustered/self-hosted systems prefer Redis/DB.
Example initial static data structure (conceptual):
– apiA: { access_token, refresh_token, expires_at }
– apiB: { access_token, refresh_token, expires_at }
You can populate this initially using a credentials setup workflow or a manual setup node.
2) Build the main loop
Function node snippet to attach token (JavaScript inside a Function node):
javascript
// get workflow-scoped static data
const wfData = this.getWorkflowStaticData('workflow');
const tokens = wfData.tokens || {};
// assume each item contains apiKey identifying which API to call
items.forEach(item => {
const apiKey = item.json.apiKey;
const apiToken = tokens[apiKey] && tokens[apiKey].access_token;
item.json.authHeader = apiToken ? `Bearer ${apiToken}` : null;
});
return items;
In the HTTP Request node, set the Authorization header using an expression:
3) Detect expired tokens and refresh
After the HTTP Request node, add an If node to check for 401 status or other provider-specific error body that signals token expiry.
If expired, route to a Token Refresh sub-workflow:
Function node example to persist tokens:
javascript
const wfData = this.getWorkflowStaticData('workflow');
if (!wfData.tokens) wfData.tokens = {};
const apiKey = $json.apiKey; // or pass apiKey as parameter
wfData.tokens[apiKey] = {
access_token: $json.access_token,
refresh_token: $json.refresh_token,
expires_at: Date.now() + ($json.expires_in * 1000)
};
return items;
After storing, return to the original request step and retry (use an Execute Workflow node or loop back in the same workflow). You can also re-run the HTTP Request node directly after refresh.
4) Exponential backoff with jitter
For transient errors (429, 503, 500), implement exponential backoff with jitter and a maximum retry count. Use a counter on the item JSON (retryCount) and a Function node to compute delayMs.
Backoff formula with capped delay and jitter:
delayMs = Math.min(baseMs 2 * retryCount, maxMs)
jitter = Math.floor(Math.random() * jitterWindowMs)
finalDelay = delayMs + jitter
Sample Function node to calculate delay:
javascript
const baseMs = 1000; // 1s
const maxMs = 60000; // 60s
const jitterWindowMs = 500; // up to 0.5s random
items.forEach(item => {
item.json.retryCount = item.json.retryCount ? item.json.retryCount + 1 : 1;
const delay = Math.min(baseMs * Math.pow(2, item.json.retryCount - 1), maxMs);
const jitter = Math.floor(Math.random() * jitterWindowMs);
item.json.delayMs = delay + jitter;
});
return items;
Then route to a Delay node configured with “Delay For” and use expression {{$json.delayMs}} and loop back to the HTTP Request node until retryCount reaches your maxRetries. Add an If node to stop after max retries and log or alert.
5) Best practices
6) Example: Refresh request (HTTP Request node)
– grant_type: refresh_token
– refresh_token: {{$workflowStaticData.tokens.apiA.refresh_token}}
– client_id: {{$credentials.client_id}}
– client_secret: {{$credentials.client_secret}}
Parse response and persist as shown above.
—
Conclusion and Next Steps
Implementing OAuth2 token rotation and exponential backoff inside a single n8n workflow gives you resilient, maintainable integrations across multiple APIs. Key takeaways:
Next steps:
If you want, I can produce a downloadable example workflow JSON with nodes wired together for one provider (including token refresh and backoff) that you can import into your n8n instance.