Workflow: ↔️ Airtable Batch Processing
Integrations: aggregate, code, debugHelper, executeWorkflow, executeWorkflowTrigger, httpRequest, if, manualTrigger, merge, set, splitInBatches, splitOut, stickyNote, switch, wait

{"id": "121pu6oiTjzkJ8OT", "meta": {"instanceId": "d160e539d2f1a627c61dec8128071eca3529ebaa5ae124b8b92c197acd24da57"}, "name": "\u2194\ufe0f Airtable Batch Processing", "tags": [{"id": "Lt9iCvabUby2qWDA", "name": "subprocess", "createdAt": "2025-03-31T18:34:58.629Z", "updatedAt": "2025-03-31T18:34:58.629Z"}], "nodes": [{"id": "35a541ff-867e-4578-bfff-d955c6cce6c9", "name": "upsert", "type": "n8n-nodes-base.httpRequest", "maxTries": 5, "position": [760, -200], "parameters": {"url": "=https://api.airtable.com/v0/{{ $('Airtable Subprocess').first().json.baseId }}/{{ $('Airtable Subprocess').first().json.tableIdOrName }}", "method": "PATCH", "options": {"response": {"response": {"fullResponse": true}}}, "jsonBody": "={\n  \"performUpsert\": {\n    \"fieldsToMergeOn\": {{ $('Airtable Subprocess').first().json.fieldsToMergeOn.toJsonString() }}\n  },\n  \"records\": {{ $json.records.toJsonString() }}\n}", "sendBody": true, "specifyBody": "json", "authentication": "predefinedCredentialType", "nodeCredentialType": "airtableTokenApi"}, "credentials": {"airtableTokenApi": {"id": "c3XcXntDvRoTITuL", "name": "Airtable s.mayerhofer"}}, "retryOnFail": true, "typeVersion": 4.2, "waitBetweenTries": 5000}, {"id": "c422716d-c0c6-4998-ac99-d62a4d298aae", "name": "random data", "type": "n8n-nodes-base.debugHelper", "position": [-240, -720], "parameters": {"category": "randomData", "randomDataType": "address"}, "typeVersion": 1}, {"id": "e0a01b39-a431-40cd-bfa3-1764fd2af4cf", "name": "When clicking \u2018Test workflow\u2019", "type": "n8n-nodes-base.manualTrigger", "position": [-460, -720], "parameters": {}, "typeVersion": 1}, {"id": "6082ce07-b4c0-42ac-92ea-302bd943ddad", "name": "Split Out", "type": "n8n-nodes-base.splitOut", "position": [-280, -260], "parameters": {"include": "allOtherFields", "options": {"destinationFieldName": "fields"}, "fieldToSplitOut": "records"}, "typeVersion": 1}, {"id": "b9cd2614-4a7b-4d68-aa4f-4cc44b27e6de", "name": "batch 10", "type": "n8n-nodes-base.splitInBatches", "position": [-40, -260], "parameters": {"options": {}, "batchSize": 10}, "typeVersion": 3}, {"id": "bcc2b8b5-b8cf-4e0b-89dd-584c16baefa1", "name": "Airtable Subprocess", "type": "n8n-nodes-base.executeWorkflowTrigger", "position": [-480, -260], "parameters": {"workflowInputs": {"values": [{"name": "baseId"}, {"name": "tableIdOrName"}, {"name": "mode"}, {"name": "fieldsToMergeOn", "type": "array"}, {"name": "records", "type": "array"}]}}, "typeVersion": 1.1}, {"id": "9cb16c31-6d7b-4e49-9d24-92047a52d5e7", "name": "Switch", "type": "n8n-nodes-base.switch", "position": [180, -260], "parameters": {"rules": {"values": [{"outputKey": "update", "conditions": {"options": {"version": 2, "leftValue": "", "caseSensitive": true, "typeValidation": "strict"}, "combinator": "and", "conditions": [{"id": "1062f23a-900f-4d7e-b16d-f3c20675a435", "operator": {"name": "filter.operator.equals", "type": "string", "operation": "equals"}, "leftValue": "={{ $json.mode }}", "rightValue": "update"}]}, "renameOutput": true}, {"outputKey": "upsert", "conditions": {"options": {"version": 2, "leftValue": "", "caseSensitive": true, "typeValidation": "strict"}, "combinator": "and", "conditions": [{"id": "9abb6abc-f7dc-4c6a-a32a-b7e05cf8da4b", "operator": {"type": "string", "operation": "equals"}, "leftValue": "={{ $json.mode }}", "rightValue": "upsert"}]}, "renameOutput": true}, {"outputKey": "insert", "conditions": {"options": {"version": 2, "leftValue": "", "caseSensitive": true, "typeValidation": "strict"}, "combinator": "and", "conditions": [{"id": "00e1d8d7-19bd-434d-afd2-29c9aee3f3b8", "operator": {"name": "filter.operator.equals", "type": "string", "operation": "equals"}, "leftValue": "={{ $json.mode }}", "rightValue": "insert"}]}, "renameOutput": true}]}, "options": {}}, "typeVersion": 3.2}, {"id": "7dede519-a841-45a2-b85c-6f2cd4868ddb", "name": "insert", "type": "n8n-nodes-base.httpRequest", "maxTries": 5, "position": [760, 40], "parameters": {"url": "=https://api.airtable.com/v0/{{ $('Airtable Subprocess').first().json.baseId }}/{{ $('Airtable Subprocess').first().json.tableIdOrName }}", "method": "POST", "options": {"response": {"response": {"fullResponse": true}}}, "jsonBody": "={\n  \"records\": {{ $json.records.toJsonString() }}\n}", "sendBody": true, "specifyBody": "json", "authentication": "predefinedCredentialType", "nodeCredentialType": "airtableTokenApi"}, "credentials": {"airtableTokenApi": {"id": "c3XcXntDvRoTITuL", "name": "Airtable s.mayerhofer"}}, "retryOnFail": true, "typeVersion": 4.2, "waitBetweenTries": 5000}, {"id": "f05b5c6b-38c9-455b-966b-c3e62c5856a0", "name": "rate limit?", "type": "n8n-nodes-base.if", "position": [940, -200], "parameters": {"options": {}, "conditions": {"options": {"version": 2, "leftValue": "", "caseSensitive": true, "typeValidation": "strict"}, "combinator": "and", "conditions": [{"id": "432e1be3-b3f3-4be3-bf0d-6b3f1b724fe7", "operator": {"type": "number", "operation": "equals"}, "leftValue": "={{ $json.statusCode }}", "rightValue": 429}]}}, "typeVersion": 2.2}, {"id": "41fe368a-0fdb-4cec-b51e-a725c376f136", "name": "Wait 0.2s to prevent rate limits", "type": "n8n-nodes-base.wait", "position": [1140, 300], "webhookId": "918cd011-855e-4702-bec0-6b066b4d9765", "parameters": {"amount": 0.2}, "typeVersion": 1.1}, {"id": "15936739-2967-4d15-87ed-9320467a6d73", "name": "retry request", "type": "n8n-nodes-base.merge", "position": [1340, -220], "parameters": {"mode": "chooseBranch"}, "typeVersion": 3}, {"id": "697de0ae-7122-42a3-beb7-db6ddba3783b", "name": "rate limit?1", "type": "n8n-nodes-base.if", "position": [940, 40], "parameters": {"options": {}, "conditions": {"options": {"version": 2, "leftValue": "", "caseSensitive": true, "typeValidation": "strict"}, "combinator": "and", "conditions": [{"id": "432e1be3-b3f3-4be3-bf0d-6b3f1b724fe7", "operator": {"type": "number", "operation": "equals"}, "leftValue": "={{ $json.statusCode }}", "rightValue": 429}]}}, "typeVersion": 2.2}, {"id": "51929ff9-e297-454d-b1ae-54df37534b2f", "name": "retry request1", "type": "n8n-nodes-base.merge", "position": [1340, 20], "parameters": {"mode": "chooseBranch"}, "typeVersion": 3}, {"id": "57015189-8a53-4de9-bcf3-370161ddc6a6", "name": "Sticky Note", "type": "n8n-nodes-base.stickyNote", "position": [1020, 220], "parameters": {"width": 360, "height": 260, "content": "### Adjust if your monthly call limit exceeded\nOn the Team plan this means 2 requests per second [Source](https://support.airtable.com/docs/managing-api-call-limits-in-airtable#monthly-call-limits-for-free-and-team-plans) -> 0.5 second wait"}, "typeVersion": 1}, {"id": "2ceae0cb-48e2-4833-89d6-6dd3565237e5", "name": "Sticky Note1", "type": "n8n-nodes-base.stickyNote", "position": [-520, -480], "parameters": {"color": 5, "width": 2080, "height": 1000, "content": "# Subprocess\n[[API Docs](https://airtable.com/developers/web/api/update-multiple-records)]"}, "typeVersion": 1}, {"id": "bb2fcf9b-da31-4827-b8d5-23c500431557", "name": "Sticky Note2", "type": "n8n-nodes-base.stickyNote", "position": [-520, -820], "parameters": {"width": 440, "height": 260, "content": "## Run with test data\nConnect to Set Fields"}, "typeVersion": 1}, {"id": "7be6bdf3-a320-4005-8ccb-0ef7ac63c0cd", "name": "Sticky Note3", "type": "n8n-nodes-base.stickyNote", "position": [0, -880], "parameters": {"color": 3, "width": 340, "height": 320, "content": "## Set Fields\nEnter your row data you want to send to Airtable. The key needs to correspond to the exact column name\n\u26a0\ufe0f  Only use fields which exist in the table  \u26a0\ufe0f"}, "typeVersion": 1}, {"id": "9fa08e5a-2657-44b1-8403-cb70c3d15940", "name": "rate limit?2", "type": "n8n-nodes-base.if", "position": [940, -440], "parameters": {"options": {}, "conditions": {"options": {"version": 2, "leftValue": "", "caseSensitive": true, "typeValidation": "strict"}, "combinator": "and", "conditions": [{"id": "432e1be3-b3f3-4be3-bf0d-6b3f1b724fe7", "operator": {"type": "number", "operation": "equals"}, "leftValue": "={{ $json.statusCode }}", "rightValue": 429}]}}, "typeVersion": 2.2}, {"id": "47867b7b-a7d3-484e-b320-1377159f1b58", "name": "retry request2", "type": "n8n-nodes-base.merge", "position": [1340, -460], "parameters": {"mode": "chooseBranch"}, "typeVersion": 3}, {"id": "c96df332-3338-4d05-82db-4a3de9f767b3", "name": "Aggregate3", "type": "n8n-nodes-base.aggregate", "position": [560, -440], "parameters": {"options": {}, "fieldsToAggregate": {"fieldToAggregate": [{"fieldToAggregate": "records"}]}}, "typeVersion": 1}, {"id": "70298130-8fe6-46e6-985c-32093a04ae49", "name": "Edit Fields4", "type": "n8n-nodes-base.set", "position": [380, -440], "parameters": {"include": "except", "options": {}, "assignments": {"assignments": [{"id": "99890d82-1a4f-432d-b7f0-e1b6492d1154", "name": "records.fields", "type": "object", "value": "={{ Object.fromEntries(Object.entries($json.fields).filter(([key]) => key !== 'id')) }}"}, {"id": "82479869-a473-4540-84a5-d5ff8ebadcd0", "name": "records.id", "type": "string", "value": "={{ $json.fields.id }}"}]}, "excludeFields": "fields", "includeOtherFields": true}, "typeVersion": 3.4}, {"id": "e9c5789c-2fc9-4107-8d07-6ad164ad7a69", "name": "Aggregate2", "type": "n8n-nodes-base.aggregate", "position": [560, 40], "parameters": {"options": {}, "fieldsToAggregate": {"fieldToAggregate": [{"fieldToAggregate": "records"}]}}, "typeVersion": 1}, {"id": "955e1311-7ff6-460c-bf0a-65b3f4968cbf", "name": "Edit Fields2", "type": "n8n-nodes-base.set", "position": [380, 40], "parameters": {"include": "except", "options": {}, "assignments": {"assignments": [{"id": "99890d82-1a4f-432d-b7f0-e1b6492d1154", "name": "records.fields", "type": "object", "value": "={{ $json.fields }}"}]}, "excludeFields": "fields", "includeOtherFields": true}, "typeVersion": 3.4}, {"id": "602c0a94-643e-4e13-b534-3df4d0044428", "name": "Aggregate1", "type": "n8n-nodes-base.aggregate", "position": [560, -200], "parameters": {"options": {}, "fieldsToAggregate": {"fieldToAggregate": [{"fieldToAggregate": "records"}]}}, "typeVersion": 1}, {"id": "0f52badd-a85b-43b1-840f-dc4931a647e1", "name": "Edit Fields1", "type": "n8n-nodes-base.set", "position": [380, -200], "parameters": {"include": "except", "options": {}, "assignments": {"assignments": [{"id": "99890d82-1a4f-432d-b7f0-e1b6492d1154", "name": "records.fields", "type": "object", "value": "={{ $json.fields }}"}]}, "excludeFields": "fields", "includeOtherFields": true}, "typeVersion": 3.4}, {"id": "a6ba6292-9883-48e6-b341-a67121c42968", "name": "update", "type": "n8n-nodes-base.httpRequest", "maxTries": 5, "position": [760, -440], "parameters": {"url": "=https://api.airtable.com/v0/{{ $('Airtable Subprocess').first().json.baseId }}/{{ $('Airtable Subprocess').first().json.tableIdOrName }}/", "method": "PATCH", "options": {"response": {"response": {"fullResponse": true}}}, "jsonBody": "={\n  \"records\": {{ $json.records.toJsonString() }}\n}", "sendBody": true, "specifyBody": "json", "authentication": "predefinedCredentialType", "nodeCredentialType": "airtableTokenApi"}, "credentials": {"airtableTokenApi": {"id": "c3XcXntDvRoTITuL", "name": "Airtable s.mayerhofer"}}, "retryOnFail": true, "typeVersion": 4.2, "waitBetweenTries": 5000}, {"id": "da6761a1-ffa2-43f3-a53d-cbd7d09af474", "name": "Airtable Batch", "type": "n8n-nodes-base.executeWorkflow", "position": [440, -720], "parameters": {"options": {}, "workflowId": {"__rl": true, "mode": "list", "value": "121pu6oiTjzkJ8OT", "cachedResultName": "\u2194\ufe0f Airtable Batch Processing"}, "workflowInputs": {"value": {"mode": "upsert", "baseId": "appXXXXXXXXXXXXX", "records": "={{ $json.records }}", "tableIdOrName": "tblXXXXXXXXXXXXX", "fieldsToMergeOn": "={{[\"field1\", \"field2\"]}}"}, "schema": [{"id": "baseId", "type": "string", "display": true, "removed": false, "required": false, "displayName": "baseId", "defaultMatch": false, "canBeUsedToMatch": true}, {"id": "tableIdOrName", "type": "string", "display": true, "removed": false, "required": false, "displayName": "tableIdOrName", "defaultMatch": false, "canBeUsedToMatch": true}, {"id": "mode", "type": "string", "display": true, "removed": false, "required": false, "displayName": "mode", "defaultMatch": false, "canBeUsedToMatch": true}, {"id": "fieldsToMergeOn", "type": "array", "display": true, "removed": false, "required": false, "displayName": "fieldsToMergeOn", "defaultMatch": false, "canBeUsedToMatch": true}, {"id": "records", "type": "array", "display": true, "removed": false, "required": false, "displayName": "records", "defaultMatch": false, "canBeUsedToMatch": true}], "mappingMode": "defineBelow", "matchingColumns": [], "attemptToConvertTypes": false, "convertFieldsToString": true}}, "typeVersion": 1.2}, {"id": "1c309f68-345c-459a-b8a4-dfe716f5badf", "name": "Set Fields", "type": "n8n-nodes-base.set", "position": [40, -720], "parameters": {"options": {}, "assignments": {"assignments": []}}, "typeVersion": 3.4}, {"id": "9be3eba5-fb5d-4fda-83c4-1295e5ed31a5", "name": "Aggregate", "type": "n8n-nodes-base.aggregate", "position": [240, -720], "parameters": {"options": {}, "aggregate": "aggregateAllItemData", "destinationFieldName": "records"}, "typeVersion": 1}, {"id": "cb0960ff-61e1-4930-881c-a370bb6aaf4d", "name": "Sticky Note5", "type": "n8n-nodes-base.stickyNote", "position": [680, -1140], "parameters": {"color": 3, "width": 420, "height": 580, "content": "## Airtable Batch\n### mode\npossible values: `upsert|insert|update`\n`upsert`: update if exists or insert new\n`insert`: always insert new\n`update`: update existing record. A field with the name `id` **must** be provided.\n### fieldsToMergeOn\nWill be used as an external ID to match records for updates. For records where no match is found, a new Airtable record will be created.\npossible values: `array of strings`. Example: `{{[\"field1\", \"field2\"]}}`\nAn array with at least one and at most three field names or IDs. IDs must uniquely identify a single record. These cannot be computed fields (formulas, lookups, rollups), and must be one of the following types: number, text, long text, single select, multiple select, date.\n### baseId\nThe part with `app...` in the URL:\nairtable\\.com / **app8pqOLekaICglwg** / tblnXZOdy8VtkAAJD/...\n### tableIdOrName \nThe part with `tbl...` in the URL:\nairtable\\.com / app8pqOLekaICglwg / **tblXXZOdy8VtkAAJD** /..."}, "typeVersion": 1}, {"id": "907fe5d6-9562-4ea9-a819-71ff4b34a9bb", "name": "Sticky Note4", "type": "n8n-nodes-base.stickyNote", "position": [0, -980], "parameters": {"color": 3, "width": 620, "height": 420, "content": "# Copy to your workflow"}, "typeVersion": 1}, {"id": "27f94e0c-c3bd-40f3-b509-e7301339111d", "name": "Wait 5s2", "type": "n8n-nodes-base.wait", "position": [1120, -460], "webhookId": "918cd011-855e-4702-bec0-6b066b4d9765", "parameters": {}, "typeVersion": 1.1}, {"id": "0378ae4c-9c77-4d7d-8211-f0aa3a29ab51", "name": "Wait 5s", "type": "n8n-nodes-base.wait", "position": [1120, -220], "webhookId": "918cd011-855e-4702-bec0-6b066b4d9765", "parameters": {}, "typeVersion": 1.1}, {"id": "0098737b-9482-42fe-afbe-c28f1f9569d1", "name": "Wait 5s1", "type": "n8n-nodes-base.wait", "position": [1140, 20], "webhookId": "918cd011-855e-4702-bec0-6b066b4d9765", "parameters": {}, "typeVersion": 1.1}, {"id": "2ec31fa9-6ea3-4a20-b6ed-e1d47b80e187", "name": "return merged output", "type": "n8n-nodes-base.code", "position": [140, -440], "parameters": {"jsCode": "const output = {\n  records: [],\n  updatedRecords: [],\n  createdRecords: []\n};\n\nfor (const item of $input.all()) {\n  output.records = output.records.concat(item.json.body.records ?? [])\n  output.updatedRecords = output.updatedRecords.concat(item.json.body.updatedRecords ?? [])\n  output.createdRecords = output.createdRecords.concat(item.json.body.createdRecords ?? [])\n}\n\nreturn output;"}, "typeVersion": 2}], "active": false, "pinData": {}, "settings": {"executionOrder": "v1"}, "versionId": "333e3b43-c098-4a97-8c47-df93df2672ed", "connections": {"Switch": {"main": [[{"node": "Edit Fields4", "type": "main", "index": 0}], [{"node": "Edit Fields1", "type": "main", "index": 0}], [{"node": "Edit Fields2", "type": "main", "index": 0}]]}, "insert": {"main": [[{"node": "rate limit?1", "type": "main", "index": 0}]]}, "update": {"main": [[{"node": "rate limit?2", "type": "main", "index": 0}]]}, "upsert": {"main": [[{"node": "rate limit?", "type": "main", "index": 0}]]}, "Wait 5s": {"main": [[{"node": "retry request", "type": "main", "index": 1}]]}, "Wait 5s1": {"main": [[{"node": "retry request1", "type": "main", "index": 1}]]}, "Wait 5s2": {"main": [[{"node": "retry request2", "type": "main", "index": 1}]]}, "batch 10": {"main": [[{"node": "return merged output", "type": "main", "index": 0}], [{"node": "Switch", "type": "main", "index": 0}]]}, "Aggregate": {"main": [[{"node": "Airtable Batch", "type": "main", "index": 0}]]}, "Split Out": {"main": [[{"node": "batch 10", "type": "main", "index": 0}]]}, "Aggregate1": {"main": [[{"node": "upsert", "type": "main", "index": 0}, {"node": "retry request", "type": "main", "index": 0}]]}, "Aggregate2": {"main": [[{"node": "insert", "type": "main", "index": 0}, {"node": "retry request1", "type": "main", "index": 0}]]}, "Aggregate3": {"main": [[{"node": "update", "type": "main", "index": 0}, {"node": "retry request2", "type": "main", "index": 0}]]}, "Set Fields": {"main": [[{"node": "Aggregate", "type": "main", "index": 0}]]}, "random data": {"main": [[]]}, "rate limit?": {"main": [[{"node": "Wait 5s", "type": "main", "index": 0}], [{"node": "Wait 0.2s to prevent rate limits", "type": "main", "index": 0}]]}, "Edit Fields1": {"main": [[{"node": "Aggregate1", "type": "main", "index": 0}]]}, "Edit Fields2": {"main": [[{"node": "Aggregate2", "type": "main", "index": 0}]]}, "Edit Fields4": {"main": [[{"node": "Aggregate3", "type": "main", "index": 0}]]}, "rate limit?1": {"main": [[{"node": "Wait 5s1", "type": "main", "index": 0}], [{"node": "Wait 0.2s to prevent rate limits", "type": "main", "index": 0}]]}, "rate limit?2": {"main": [[{"node": "Wait 5s2", "type": "main", "index": 0}], [{"node": "Wait 0.2s to prevent rate limits", "type": "main", "index": 0}]]}, "retry request": {"main": [[{"node": "upsert", "type": "main", "index": 0}]]}, "Airtable Batch": {"main": [[]]}, "retry request1": {"main": [[{"node": "insert", "type": "main", "index": 0}]]}, "retry request2": {"main": [[{"node": "update", "type": "main", "index": 0}]]}, "Airtable Subprocess": {"main": [[{"node": "Split Out", "type": "main", "index": 0}]]}, "Wait 0.2s to prevent rate limits": {"main": [[{"node": "batch 10", "type": "main", "index": 0}]]}, "When clicking \u2018Test workflow\u2019": {"main": [[{"node": "random data", "type": "main", "index": 0}]]}}}