{
  "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
          }
        ]
      ]
    }
  }
}