n8n Data Tables and the Data Table node
Updated 2026-10-21 · 5 min read
n8n Data Tables are small tables stored inside your n8n instance. Workflows read and write them through the Data Table node, people browse and edit them in the Data tables tab, and scripts reach them through the /datatables API. They replace the spreadsheet or tiny database that many automations used to need for "remember this between runs": processed IDs, last sync time, lookup lists, prompts. This page covers what they are good for, every node operation, the limits, and patterns you can copy.
What Data Tables are (and aren't)
From the docs, typical uses are:
- Persisting data across workflows in the same project
- Markers to prevent duplicate runs
- Reusable prompts and messages
- Lookup tables
- Storing data produced by executions
They are scoped to a project: everyone in the project can use a table, and owners and admins can see all tables. They were introduced in n8n 1.113 and are available on all plans (release notes).
They are not a production database. The limit is small and the Code node can't query them directly. For millions of rows, use Postgres or another database node.
Create a table
In a project, open Data tables > Create Data table, name it, and choose:
- From scratch: define columns and add rows in the UI.
- Import CSV: columns and rows come from the file.
You can rename and reorder columns, edit rows, and Download CSV from the table's menu. The Data Table node can also create, list, update and delete tables from a workflow.
Data Table node operations
From the node docs:
| Resource | Operation | What it does |
|---|---|---|
| Row | Insert | Add rows from input items |
| Row | Get | Return rows that match filters |
| Row | Update | Update matching rows |
| Row | Upsert | Update if a matching row exists, insert otherwise |
| Row | Delete | Delete matching rows |
| Row | If Row Exists | Pass through input items that match a row |
| Row | If Row Does Not Exist | Pass through input items that don't |
| Table | Create, List, Update, Delete | Manage tables |
If Row Exists and If Row Does Not Exist are the most useful for automation: they act as filters against the table, so "skip anything we've already processed" becomes one node.
Limits
| Limit | Value |
|---|---|
| Total storage per instance | 200 MiB by default |
| Change it (self-hosted) | N8N_DATA_TABLES_MAX_SIZE_BYTES |
| Warning | At 80% of the limit |
| Over the limit | Manual additions disabled; inserts and updates in workflows fail |
| Code node access | Not supported |
200 MiB is shared by every table on the instance. A table that logs every execution will hit it eventually; prune old rows with a scheduled Delete.
Patterns
1. Process each record once
Schedule → HTTP Request (new orders) → Data Table: If Row Does Not Exist (order_id)
→ Process → Data Table: Insert (order_id, processed_at)
Rows that already exist are dropped before processing. Insert only after processing succeeds, so a failed run retries next time.
2. Remember the last sync time
A table sync_state with columns key, value.
- Data Table Get where
key = orders_last_sync. - HTTP Request with
?updated_since={{ $json.value }}. - After processing, Data Table Upsert
key = orders_last_sync,value = {{ $now.toISO() }}.
This works in test runs too, which workflow static data doesn't.
3. Lookup table
A country_routing table maps country codes to Slack channels or sales owners. Non-developers edit the table in the UI; the workflow reads it with Get. No redeploy for a routing change.
4. Alert deduplication
In an error workflow, Get the last alert time for the failing workflow, skip if it was less than an hour ago, otherwise alert and Upsert the timestamp.
5. Using rows in code
Read with a Data Table node, then the Code node receives the rows as items:
const rows = $('Get routing').all().map(i => i.json);
const byCountry = Object.fromEntries(rows.map(r => [r.country, r.channel]));
return $input.all().map(i => ({
json: { ...i.json, channel: byCountry[i.json.country] ?? '#general' }
}));
Data Tables vs static data vs Variables
| Data Tables | Workflow static data | Variables | |
|---|---|---|---|
| Shape | Rows and columns | A small JSON object | Key → string |
| Scope | Project | One workflow (or one node) | Instance or project |
| Written by workflows | Yes | Yes, JavaScript Code node only | No (read-only) |
| Works in test runs | Yes | No: saved only for published, triggered runs | Yes |
| Plan | All | All | Cloud Pro/Enterprise, self-hosted Business/Enterprise |
The static data docs themselves recommend Data Tables for persisting data between executions: static data is experimental, may be unreliable under high-frequency runs, and should stay small.
Moving data in
- CSV: export from the old system, create the table with Import CSV.
- API: POST rows to the
/datatablesendpoint of the n8n public API. - Workflow: a one-off workflow that reads the source (HTTP Request, Google Sheets, a database node) and runs Data Table Insert or Upsert.
From Pipedream data stores
Pipedream data stores were key-value stores shared across workflows, with TTL support, used from code (this.dataStore.get/set) or no-code actions (Pipedream data stores). They are not in the workflow export, and Pipedream deletes all Workflows data by April 30, 2027.
| Pipedream | n8n Data Tables |
|---|---|
| Data store | Table with columns key, value (plus expires_at if you used TTL) |
get(key) |
Get where key = … |
set(key, value) |
Upsert on key |
has(key) |
If Row Exists |
delete(key) |
Delete |
| TTL | expires_at column + scheduled Delete of expired rows |
Values that were objects become JSON strings in a column, or separate columns if the shape is fixed. Backup steps and code for each call are in Pipedream data stores to n8n.
Our converter lists every step that touches a data store. In the converted code those calls use workflow static data so the workflow runs at all; for anything you rely on, move them to Data Tables as shown above. Check single steps with the code step checker or the whole export with the converter. The report and 1 workflow are free. Full export: $3 per workflow, $19 minimum — coming soon.
Not affiliated with Pipedream or n8n.
FAQ
- What are n8n Data Tables?
- Built-in tables stored inside your n8n instance and scoped to a project. Workflows read and write them with the Data Table node; you can also manage them in the Data tables tab or through the /datatables API.
- How much data can n8n Data Tables hold?
- By default, 200 MiB in total across all data tables in an instance. Self-hosted instances can change it with N8N_DATA_TABLES_MAX_SIZE_BYTES. Past the limit, inserts and updates fail.
- Can I read a Data Table from the n8n Code node?
- No. Direct access from a Code node isn't supported. Read rows with the Data Table node first and pass them into the Code node as input.
- Is there an n8n data store node?
- The built-in option for storing records between executions is the Data Table node. For small values tied to one workflow, the JavaScript Code node also has workflow static data.
- Can I import a CSV into an n8n Data Table?
- Yes. In the Data tables tab, create a table with Import CSV. You can also download any table as CSV.
Related
Sources
- https://docs.n8n.io/build/work-with-data/data-tables
- https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.datatable
- https://docs.n8n.io/build/code-in-n8n/cookbook/built-in-methods-and-variables-examples/getworkflowstaticdata
- https://docs.n8n.io/changelog/release-notes-1.x
- https://pipedream.com/docs/workflows/data-management/data-stores