n8n Google Sheets Integration: Read, Write, and Sync Data
Why I Keep Coming Back to n8n Google Sheets for Client Workflows
If you've spent any time building automations, you've probably noticed that Google Sheets sits at the centre of almost every client's operation. It's the universal spreadsheet — CRM, inventory tracker, lead list, reporting dashboard, all rolled into one. That's exactly why mastering n8n Google Sheets integration is one of the most valuable skills you can add to your automation toolkit. I've used it across dozens of client projects at Esipick, and it genuinely never gets old.
In this post I'm going to walk you through the three core operations — reading, writing, and syncing — with the actual node configurations and a real example from a client we helped last year.
Setting Up the Google Sheets Node in n8n
Before you touch any data, you need credentials. n8n uses OAuth2 to connect to Google, and the setup takes about five minutes if you've done it before.
- Go to Google Cloud Console, create a project, and enable the Google Sheets API and Google Drive API.
- Create an OAuth2 Client ID (Web Application type), add
https://your-n8n-instance.com/rest/oauth2-credential/callbackas an authorised redirect URI. - In n8n, add a new Google Sheets credential, paste your Client ID and Secret, then hit Connect.
- Authorise the scopes — you need at minimum
spreadsheetsanddrive.readonlyfor reading, orspreadsheetsalone for full read/write.
One thing that trips people up: if you're on a self-hosted n8n instance without HTTPS, OAuth will fail. Sort your SSL certificate first. We use Caddy as a reverse proxy — it handles HTTPS automatically and takes about ten minutes to configure.
Reading Data: Pulling Rows Into Your Workflow
The Google Sheets node in n8n has a Read Rows operation that returns every row from a specified sheet as individual JSON items. Each column header becomes a key. Clean, predictable, easy to map downstream.
Here's the configuration I use most often:
- Operation: Read Rows
- Spreadsheet ID: Pull this from the URL — it's the long string between
/d/and/edit. - Sheet Name: Usually
Sheet1unless your client has renamed it (they always rename it). - Return All: Toggle on. By default n8n paginates at 100 rows — fine for small sheets, a problem for anything larger.
A practical tip: use a Filter node immediately after the read to narrow down rows. Don't try to filter inside the Sheets node itself using range notation — it's brittle and breaks when rows are inserted. Filter in n8n where you have full logical control.
Writing Data: Appending and Updating Rows
This is where n8n Google Sheets really earns its keep. Two operations matter here: Append Row and Update Row.
Append Row
Use this when you want to add new records — form submissions, new leads from an API, webhook payloads. Map your fields in the Columns section by typing the column header name and mapping it to the expression from your incoming data. n8n will insert a new row at the bottom of the sheet automatically.
Update Row
Update is trickier. You need a way to identify which row to update — n8n does this via a Row Number, not a unique ID. My workaround: do a Read first, use a Code node to find the row index of the record you want to change, then feed that row number into the Update operation.
For sheets where you control the structure, I always add a hidden column row_id with a sequential number. Makes updates trivial.
Real Example: Syncing a Lead Pipeline for a Property Client
Last year we built a workflow for a property management firm in Birmingham. They were running their entire lettings pipeline in a Google Sheet — about 400 rows, updated by three different team members throughout the day. They wanted automatic Slack alerts when a lead moved to "Viewing Booked" status, plus a weekly summary email.
Here's how we structured it:
- Trigger: Schedule node — runs every 15 minutes during business hours.
- Read: Google Sheets node pulls all rows from the
Leadssheet. - Filter: We check for rows where
StatusequalsViewing BookedAND anotifiedcolumn is empty. - Slack node: Sends a formatted message to the
#lettingschannel with the lead name, property address, and viewing date. - Update Row: Stamps the
notifiedcolumn with a timestamp so we don't double-alert.
"We'd been manually checking the sheet every hour. Now the team just waits for the Slack ping. Saved us probably two hours a day across the team." — Client, Birmingham lettings firm
The whole workflow took about three hours to build and test. The client had zero technical knowledge — they just kept updating their spreadsheet exactly as before. That's the beauty of it.
Syncing: Keeping Two Sheets (or a Sheet and a CRM) in Harmony
Bidirectional sync is the hardest pattern, and honestly I'd caution against trying to do true real-time bidirectional sync with Google Sheets — it's a spreadsheet, not a database, and concurrent writes cause conflicts. What works well instead:
- One source of truth: Designate one system (usually the CRM) as master. Google Sheets only receives updates, never initiates them.
- Timestamp-based sync: Add a
last_modifiedcolumn. In your n8n workflow, only process rows wherelast_modifiedis newer than the last sync run. Store the last run time in a static webhook or n8n's built-in static data. - Upsert pattern: Read from source, check if the record exists in the target sheet, append if new, update if existing. The Code node in n8n handles this logic cleanly with about 20 lines of JavaScript.
For a recent HubSpot-to-Sheets sync we built, we poll HubSpot's contacts API every hour, filter contacts updated in the last hour, then upsert into a master Sheets file that a finance team uses for reporting. Works reliably and the finance team gets live-ish data without needing HubSpot licences.
Common Mistakes to Avoid
- No header row: n8n's Sheets node expects the first row to be column headers. If there's a title merged across the top, your data will be misread.
- Spaces in column names:
First NamebecomesFirst Namein JSON — with a space. Map it with bracket notation in expressions:{{ $json['First Name'] }}. - Rate limits: Google Sheets API allows 300 requests per minute per project. If you're processing hundreds of rows in a loop, add a Wait node (200ms) between iterations.
- Sheet vs Spreadsheet: The Spreadsheet ID is in the URL. The Sheet Name is the tab at the bottom. Mixing these up wastes 20 minutes every time.
Final Thoughts
The n8n Google Sheets integration is genuinely one of the most practical combinations in the no-code/low-code world. It's not glamorous — but it's what clients actually use. If you can read, write, and sync reliably, you can automate an enormous chunk of the manual work that most small businesses do every single day.
Start simple: build a one-way append workflow, get comfortable with the node, then layer in the update and sync patterns once you understand how n8n handles row indexing.
If you'd rather have someone build it for you — or you've hit a wall with a specific workflow — feel free to reach out to us at Esipick. We build this stuff every week.
— Ali, Founder, Esipick
Want this automated for your business?
I build n8n workflows, WhatsApp automations, and AI pipelines — starting from $300. Most go live in under a week.
Get a Free Audit →