DEV Community

Pirate Prentice
Pirate Prentice

Posted on

n8n Google Sheets node: read, write, and update rows automatically (free workflow JSON)

If you've been using n8n for a while, you've probably hit a point where you need to push data into a Google Sheet — or pull rows out of one to trigger something else.

The n8n Google Sheets node makes this surprisingly easy. In this post I'll walk you through the three operations you'll use 90% of the time: reading rows, appending new rows, and updating existing rows.

Free workflow JSON at the end.


Why Google Sheets instead of a database?

For small automations, Sheets is often the right call:

  • Your client or teammate already lives in Sheets
  • You want a human-readable audit log of what happened
  • You're prototyping and don't want to spin up a database
  • You need non-technical people to add/edit the source data

n8n's Google Sheets node handles all of this without any code.


Prerequisites

Before you start, you'll need:

  1. A Google account with a spreadsheet ready
  2. A Google OAuth2 credential configured in n8n (Settings → Credentials → New → Google OAuth2)
  3. n8n running (self-hosted or cloud)

If you haven't set up the Google OAuth2 credential yet, go to Settings → Credentials → New → Google OAuth2 and follow the on-screen steps. You'll need to create an OAuth app in Google Cloud Console — n8n's docs walk through it step by step.


Operation 1: Read rows from a sheet

Use case: pull a list of leads, product SKUs, customer IDs — anything stored in Sheets.

Setup:

  1. Add a Google Sheets node
  2. Set Operation to Read Rows
  3. Select your spreadsheet (by URL or ID) and the sheet tab name
  4. Set Return All to true, or specify a range like A2:D100

The node outputs one item per row, with column headers as keys. So if your sheet has Name / Email / Status columns, each item looks like:

{
  "Name": "Alice Chen",
  "Email": "alice@example.com",
  "Status": "pending"
}
Enter fullscreen mode Exit fullscreen mode

Tip: Use a Filter node immediately after to work with only the rows you care about (e.g., Status = "pending").


Operation 2: Append a new row

Use case: log form submissions, save webhook payloads, record completed automation runs.

Setup:

  1. Add a Google Sheets node
  2. Set Operation to Append Row
  3. Pick your spreadsheet and sheet
  4. Under Columns, map your data fields to the column headers

n8n will add a new row at the bottom of your existing data. Column names must match the headers in row 1 exactly (case-sensitive).

Example mapping:

Column Value
Name {{ $json.name }}
Email {{ $json.email }}
Created {{ $now.toISO() }}
Status pending

Operation 3: Update an existing row

Use case: mark a row as "processed," update a status field, write back a result.

Setup:

  1. Add a Google Sheets node
  2. Set Operation to Update Row
  3. Pick your spreadsheet and sheet
  4. Set the Row Number (you usually get this from a prior Read Rows step — n8n returns it as _rowNumber)
  5. Map the columns you want to update

Getting the row number: When you use Read Rows, n8n adds a _rowNumber field to each item automatically. Pipe that into your Update Row node as the row number and you're set.


Full workflow: form → Sheets → Slack notification

Here's a pattern I use constantly:

Webhook (form submission)
  → Google Sheets (Append Row — log the submission)
  → IF node (check if value > threshold)
    → [true] Slack (notify team)
    → [false] (do nothing)
Enter fullscreen mode Exit fullscreen mode

Every submission gets logged. High-value ones get a Slack ping. The whole thing runs in about 300ms.


Free workflow JSON

Here's the workflow JSON for a Webhook → Google Sheets → Slack notification flow. Import it in n8n (New Workflow → Import from JSON):

{
  "name": "Webhook to Google Sheets to Slack",
  "nodes": [
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "sheet-intake",
        "responseMode": "lastNode",
        "options": {}
      },
      "name": "Webhook",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 1,
      "position": [240, 300]
    },
    {
      "parameters": {
        "operation": "append",
        "documentId": { "value": "YOUR_SHEET_ID", "mode": "id" },
        "sheetName": { "value": "Sheet1", "mode": "name" },
        "columns": {
          "mappingMode": "autoMapInputData",
          "value": {}
        },
        "options": {}
      },
      "name": "Google Sheets",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4,
      "position": [460, 300]
    },
    {
      "parameters": {
        "conditions": {
          "number": [
            {
              "value1": "={{ $json.amount }}",
              "operation": "larger",
              "value2": 100
            }
          ]
        }
      },
      "name": "IF",
      "type": "n8n-nodes-base.if",
      "typeVersion": 1,
      "position": [680, 300]
    },
    {
      "parameters": {
        "channel": "#alerts",
        "text": "=New high-value submission: {{ $(\"Webhook\").item.json.name }} — ${{ $(\"Webhook\").item.json.amount }}",
        "otherOptions": {}
      },
      "name": "Slack",
      "type": "n8n-nodes-base.slack",
      "typeVersion": 2,
      "position": [900, 200]
    }
  ],
  "connections": {
    "Webhook": { "main": [[{ "node": "Google Sheets", "type": "main", "index": 0 }]] },
    "Google Sheets": { "main": [[{ "node": "IF", "type": "main", "index": 0 }]] },
    "IF": { "main": [[{ "node": "Slack", "type": "main", "index": 0 }], []] }
  }
}
Enter fullscreen mode Exit fullscreen mode

Replace YOUR_SHEET_ID with your actual spreadsheet ID (the long string in the sheet URL).


Common gotchas

Authentication keeps failing: Make sure your Google OAuth2 credential has Sheets API scope enabled. Re-authenticate if you added the scope after initial setup.

Columns not mapping: Column headers in row 1 must match exactly what you put in the node mapping. Watch for trailing spaces.

_rowNumber is off: Google Sheets nodes count from row 1 (headers), so row 2 in your sheet = _rowNumber 2. Account for this if you're doing math.

Rate limits: Google Sheets API has a limit of 100 requests per 100 seconds per user. For high-volume automations, add a small Wait node or batch your appends.


Want more workflows like this?

I packaged 10 of the most common n8n workflows — including a full Sheets-based CRM sync, a multi-step form intake pipeline, and an automated invoice logger — into a single starter pack.

👉 n8n Workflow Starter Pack ($29) — import-ready JSON files, plain-English setup instructions, tested on n8n 1.x.

If you have questions about this workflow or want to see a specific use case covered, drop it in the comments — I read everything.

Top comments (1)

Collapse
 
pirateprentice profile image
Pirate Prentice

What are you connecting Google Sheets to in your n8n workflows? I find it most useful as a lightweight audit log — every automation run writes a row so you have a human-readable trail without spinning up a database. Curious what patterns others are using.