AutomationFlowsData & Sheets › Enrich Google Sheets Rows via REST API

Enrich Google Sheets Rows via REST API

Original n8n title: Enrich Google Sheets Rows via Any REST API in Rate-limited Batches

ByPatrick Siewert @patrickn8n on n8n.io

• Reads all rows from a Google Sheet • Splits them into configurable batches (default: 5 rows per batch) • Calls any external REST API once per row (GET or POST, Header Auth) • Waits 1 second between batches to stay within API rate limits • Routes responses: writes enriched data…

Event trigger★★★★☆ complexity12 nodesGoogle SheetsHTTP Request
Data & Sheets Trigger: Event Nodes: 12 Complexity: ★★★★☆ Added:

This workflow corresponds to n8n.io template #14205 — we link there as the canonical source.

This workflow follows the Google Sheets → HTTP Request recipe pattern — see all workflows that pair these two integrations.

The workflow JSON

Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →

Download .json
{
  "nodes": [
    {
      "id": "a2b55cbc-dbef-4d25-8261-d08d4c230542",
      "name": "Run Enrichment",
      "type": "n8n-nodes-base.manualTrigger",
      "position": [
        0,
        624
      ],
      "parameters": {},
      "typeVersion": 1
    },
    {
      "id": "d3d2ab75-19ec-4bb5-b257-315dcdf998b7",
      "name": "Template Overview",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        0,
        -224
      ],
      "parameters": {
        "color": 4,
        "width": 560,
        "height": 816,
        "content": "## Enrich Google Sheets via API \u2014 Rate-Limited Batches\n\nThis template reads rows from a Google Sheet, calls any external API to enrich each row, and writes results back, in rate-limited batches to avoid hitting API rate limits.\n\n## Who it's for\nDevelopers, marketers, and operations teams who need to enrich spreadsheet data (leads, products, URLs) via any REST API without triggering rate-limit errors.\n\n## How it works\n1. **Read rows** from your input Google Sheet tab\n2. **Split into batches** (default: 5 rows per batch)\n3. **Call your API** for each row in the batch\n4. **Wait 1 second** between batches (rate-limit pause)\n5. **Check the response** route success and errors separately\n6. **Write enriched fields** back to the matching row on success\n7. **Capture errors** with row identifier on failure\n\n## Requirements\n- Google Sheets OAuth2 credential\n- HTTP Header Auth credential (for your API key)\n\n## How to set up\n1. Connect your **Google Sheets OAuth2** credential to both Sheet nodes\n2. Set your **Sheet ID** and **Tab name** in the Read Rows node\n3. Set your **API endpoint URL** in the Call API node\n4. Connect your **Header Auth** credential in the Call API node\n5. Update the **Update Row** node with the column(s) to write back\n6. Adjust **batch size** and **wait duration** using the sticky notes below"
      },
      "typeVersion": 1
    },
    {
      "id": "b45cc002-3be0-4516-a28e-4ef8056692a9",
      "name": "Batch Config Instructions",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        640,
        0
      ],
      "parameters": {
        "color": 7,
        "width": 380,
        "height": 364,
        "content": "## Batch Size & Rate Limit Config\n\n**SplitInBatches node** (`Batch Rows`)\n\u2192 Open the node and set `Batch Size`\n\u2192 Default: **5 rows per batch**\n\u2192 Increase for fast APIs (e.g. 20)\n\u2192 Decrease for strict rate limits (e.g. 1)\n\n**Wait node** (`Rate Limit Pause`)\n\u2192 Default: **1 second** between batches\n\u2192 Change `Amount` to match your API's rate limit\n\u2192 Example: 2s for 30 req/min APIs"
      },
      "typeVersion": 1
    },
    {
      "id": "049bb248-f6ad-4540-9cd1-6e865abfa1cf",
      "name": "API Credential Instructions",
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        1040,
        0
      ],
      "parameters": {
        "color": 6,
        "width": 588,
        "height": 364,
        "content": "## API Credential Setup\n\n**Call API node** (`Call API`)\n1. Open the node\n2. Set `URL` to your API endpoint\n   Example: `https://api.example.com/enrich`\n3. Set `Method` (GET or POST)\n4. Under **Authentication**, select `Header Auth`\n5. Create a new **Header Auth** credential:\n   - Name: `X-API-Key` (or whatever header your API requires)\n   - Value: your API key\n\n**Google Sheets nodes**\n- Both Sheet nodes need the same **Google Sheets OAuth2** credential\n- Grant access to the spreadsheet in Google Drive"
      },
      "typeVersion": 1
    },
    {
      "id": "0bb2bc15-b3bd-4839-996b-bae7f494daee",
      "name": "Read Rows",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        240,
        624
      ],
      "parameters": {
        "options": {},
        "sheetName": {
          "__rl": true,
          "mode": "name",
          "value": "Sheet1"
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "YOUR_GOOGLE_SHEET_ID"
        }
      },
      "typeVersion": 4.5
    },
    {
      "id": "b397055b-fc8f-4931-9ff5-72117ed053e0",
      "name": "Batch Rows",
      "type": "n8n-nodes-base.splitInBatches",
      "position": [
        480,
        624
      ],
      "parameters": {
        "options": {},
        "batchSize": 5
      },
      "typeVersion": 3
    },
    {
      "id": "b6e027b5-2ce6-4a76-bdb5-38c408ef23d9",
      "name": "Call API",
      "type": "n8n-nodes-base.httpRequest",
      "position": [
        720,
        624
      ],
      "parameters": {
        "url": "https://api.example.com/enrich",
        "options": {
          "response": {
            "response": {
              "fullResponse": true
            }
          }
        },
        "sendQuery": true,
        "authentication": "predefinedCredentialType",
        "queryParameters": {
          "parameters": [
            {
              "name": "id",
              "value": "={{ $json.id }}"
            }
          ]
        },
        "nodeCredentialType": "httpHeaderAuth"
      },
      "typeVersion": 4.3
    },
    {
      "id": "deede1f3-59ea-4d10-bce6-b452a0eb05c9",
      "name": "Rate Limit Pause",
      "type": "n8n-nodes-base.wait",
      "position": [
        960,
        624
      ],
      "parameters": {
        "amount": 1
      },
      "typeVersion": 1.1
    },
    {
      "id": "ceb03f84-025e-4d4a-8da8-293e0d987bc9",
      "name": "API Success?",
      "type": "n8n-nodes-base.if",
      "position": [
        1200,
        624
      ],
      "parameters": {
        "options": {},
        "conditions": {
          "options": {
            "leftValue": "",
            "caseSensitive": true,
            "typeValidation": "strict"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "cond-0001",
              "operator": {
                "type": "number",
                "operation": "equals"
              },
              "leftValue": "={{ $json.statusCode }}",
              "rightValue": 200
            }
          ]
        }
      },
      "typeVersion": 2
    },
    {
      "id": "6cf9dc4c-579f-4b21-9be1-51bd8d41c043",
      "name": "Update Row",
      "type": "n8n-nodes-base.googleSheets",
      "position": [
        1440,
        512
      ],
      "parameters": {
        "columns": {
          "value": {
            "row_number": "={{ $('Batch Rows').item.json.row_number }}",
            "enriched_field": "={{ $json.body.result }}",
            "enrichment_status": "success"
          },
          "schema": [],
          "mappingMode": "defineBelow",
          "matchingColumns": [
            "row_number"
          ]
        },
        "options": {},
        "operation": "update",
        "sheetName": {
          "__rl": true,
          "mode": "name",
          "value": "Sheet1"
        },
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "YOUR_GOOGLE_SHEET_ID"
        }
      },
      "typeVersion": 4.5
    },
    {
      "id": "ba0f3f22-39f4-4ccb-96d9-b5960eb9d8aa",
      "name": "Capture Error",
      "type": "n8n-nodes-base.set",
      "position": [
        1440,
        752
      ],
      "parameters": {
        "options": {},
        "assignments": {
          "assignments": [
            {
              "id": "err-0001",
              "name": "row_id",
              "type": "string",
              "value": "={{ $('Batch Rows').item.json.id }}"
            },
            {
              "id": "err-0002",
              "name": "error_status",
              "type": "number",
              "value": "={{ $json.statusCode }}"
            },
            {
              "id": "err-0003",
              "name": "error_message",
              "type": "string",
              "value": "API call failed \u2014 status {{ $json.statusCode }}"
            }
          ]
        }
      },
      "typeVersion": 3.4
    },
    {
      "id": "15ccf54b-cebb-4fb7-8cd7-1cf044ab2e3b",
      "name": "Merge Results",
      "type": "n8n-nodes-base.merge",
      "position": [
        1680,
        624
      ],
      "parameters": {},
      "typeVersion": 3
    }
  ],
  "connections": {
    "Call API": {
      "main": [
        [
          {
            "node": "Rate Limit Pause",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Read Rows": {
      "main": [
        [
          {
            "node": "Batch Rows",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Batch Rows": {
      "main": [
        [],
        [
          {
            "node": "Call API",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Update Row": {
      "main": [
        [
          {
            "node": "Merge Results",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "API Success?": {
      "main": [
        [
          {
            "node": "Update Row",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Capture Error",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Capture Error": {
      "main": [
        [
          {
            "node": "Merge Results",
            "type": "main",
            "index": 1
          }
        ]
      ]
    },
    "Merge Results": {
      "main": [
        [
          {
            "node": "Batch Rows",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Run Enrichment": {
      "main": [
        [
          {
            "node": "Read Rows",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Rate Limit Pause": {
      "main": [
        [
          {
            "node": "API Success?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}
Pro

For the full experience including quality scoring and batch install features for each workflow upgrade to Pro

About this workflow

• Reads all rows from a Google Sheet • Splits them into configurable batches (default: 5 rows per batch) • Calls any external REST API once per row (GET or POST, Header Auth) • Waits 1 second between batches to stay within API rate limits • Routes responses: writes enriched data…

Source: https://n8n.io/workflows/14205/ — original creator credit. Request a take-down →

More Data & Sheets workflows → · Browse all categories →

Related workflows

Workflows that share integrations, category, or trigger type with this one. All free to copy and import.

Data & Sheets

This template is ideal for solo store owners, eCommerce marketers, automation beginners, or anyone using Shopify and Gmail who wants to recover lost revenue without coding.

HTTP Request, Gmail, Twilio +3
Data & Sheets

PCN. Uses googleSheets, httpRequest, @n-octo-n/n8n-nodes-json-database, itemLists. Event-driven trigger; 60 nodes.

Google Sheets, HTTP Request, @N Octo N/N8N Nodes Json Database +3
Data & Sheets

The workflow automates the process of gathering extensive keyword data for a "Main Keyword." It starts by reading initial parameters from a Google Sheets template, creates a new dedicated Google Sheet

Google Sheets, Google Drive, HTTP Request
Data & Sheets

🔥 March Sale – n8n Community Members Get ideoGener8r for Just $27! (Reg. $47) Use Coupon Code: (Valid until 3/31/2025 for n8n community members)

HTTP Request, Google Drive, Google Sheets
Data & Sheets

📄 Documentation: Notion Guide

Google Sheets, Google Drive, HTTP Request +2