n8n getWorkflowStaticData: 64KB of Cross-Execution State Without External Dependencies
Most n8n state management advice starts with “add a Redis node” or “write to a database.” For small amounts of state — a cursor for pagination, a counter, a last-processed timestamp — that is overkill.
getWorkflowStaticData is n8n’s built-in solution for exactly this use case.
What getWorkflowStaticData Does
getWorkflowStaticData(type) returns a JavaScript object that is persisted between executions of the same workflow. You read from it, modify it, and n8n saves the updated state automatically when the execution completes.
The state survives:
- Multiple executions of the same workflow
- n8n restarts
- Workflow deactivation and reactivation
The state is scoped to the workflow — it is not shared between different workflows.
The function takes a type argument: 'global' or 'node'. Global state is shared across all executions of the workflow. Node state is scoped to the specific node calling the function.
The 64KB Limit
Static data is capped at 64KB per workflow. For most use cases — cursor values, timestamps, small lookup objects, counters — this is more than sufficient.
If you need to store more data persistently, you need external storage (Redis, PostgreSQL, or n8n’s built-in execution data via Set Variables). For everything under 64KB, static data is simpler.
Common Use Cases
Tracking the last processed record:
const staticData = $getWorkflowStaticData('global');
const lastProcessedId = staticData.lastProcessedId || 0;
// ... process items with id > lastProcessedId ...
// Update after processing
staticData.lastProcessedId = maxProcessedId;
return $input.all();
Cursor-based pagination:
const staticData = $getWorkflowStaticData('global');
const cursor = staticData.cursor || null;
// Use cursor in API request
const response = await $http.get(`https://api.example.com/items?cursor=${cursor}`);
// Store next cursor for next execution
staticData.cursor = response.json.nextCursor;
return [{ json: { items: response.json.items } }];
Deduplication counter:
const staticData = $getWorkflowStaticData('global');
if (!staticData.processedIds) staticData.processedIds = [];
const newItems = $input.all().filter(item => {
return !staticData.processedIds.includes(item.json.id);
});
staticData.processedIds = [
...staticData.processedIds,
...newItems.map(i => i.json.id)
].slice(-500); // Keep last 500 IDs to stay under 64KB
return newItems;
Rate limit tracking:
const staticData = $getWorkflowStaticData('global');
const now = Date.now();
const windowStart = now - 60000; // 1 minute window
if (!staticData.requests) staticData.requests = [];
staticData.requests = staticData.requests.filter(ts => ts > windowStart);
if (staticData.requests.length >= 100) {
throw new Error('Rate limit: 100 requests/minute exceeded');
}
staticData.requests.push(now);
How the Persistence Works
Static data is stored in n8n’s internal database (SQLite by default, PostgreSQL for self-hosted instances). At the end of each execution, n8n serializes the static data object back to the database.
This means:
- Reads are cheap — the object is loaded at execution start
- Writes happen at execution end — mid-execution, changes are in memory only
- Failed executions may not persist updates — if the workflow errors, the final save may not happen. Design your state updates defensively.
Resetting Static Data
To clear the static data for a workflow:
- Open the workflow in the editor
- Click the three-dot menu
- Select “Settings”
- Find the Static Data section and click “Reset”
You can also reset it programmatically by setting the object properties to null or deleting keys:
const staticData = $getWorkflowStaticData('global');
delete staticData.cursor; // Reset cursor, keep other state
// or
Object.keys(staticData).forEach(key => delete staticData[key]); // Reset all
When Not to Use It
Static data is not appropriate for:
- High-concurrency workflows — if multiple executions run simultaneously, they may overwrite each other’s state (last write wins)
- Data that must survive workflow deletion — deleting the workflow deletes its static data
- State shared across workflows — static data is workflow-scoped; use external storage for cross-workflow state
- Large datasets — if you need more than a few hundred small items, use external storage
For sequential workflows (scheduled runs, non-concurrent triggers), static data is the simplest possible way to maintain state across executions. No external dependencies, no credentials to manage, no cleanup. Just a JavaScript object that persists.