n8n Google Sheets Integration: Connect, Read, and Write Rows (2026)
Connect Google Sheets to n8n step by step — OAuth credentials that actually work, appending and updating rows, reading with filters, and triggering on new rows.
By Ali Ilyas · · Updated July 28, 2026
Google Sheets is the integration most people wire up first in n8n, and the node itself is easy. Almost all of the difficulty is in the ten minutes before it: getting Google to hand n8n a working credential. That step has more places to go wrong than the entire rest of the workflow, and most guides skip past it in two sentences.
This one doesn't. We'll set up the credential properly, then build the three things you'll actually use — appending rows, reading rows with a filter, and updating a row that already exists — and finish with the gotchas that quietly cost people an afternoon.
What's on this page
- What you'll need
- Connecting Google Sheets to n8n
- Setting up custom OAuth2, step by step
- When to use a service account instead
- The operations, and which one you want
- Build 1: append incoming data to a sheet
- Build 2: read rows and filter them
- Build 3: update a row instead of duplicating it
- Triggering a workflow when a row is added
- Gotchas that cost people hours
- Frequently asked questions
What you'll need
- A running n8n instance. Either n8n Cloud or a self-hosted one — the difference matters a lot for credentials, and we'll cover it below. If you don't have one yet, the quickest local option is n8n in Docker.
- A Google account.
- A spreadsheet with a header row. n8n reads your column names from the first row, so a sheet whose first row is data rather than headers will behave strangely from the start.
Everything below was checked against n8n 2.x. Where a detail changed between major versions, it's called out.
Connecting Google Sheets to n8n
n8n supports two ways to authenticate with Google: OAuth2 and a Service Account. Both work with Google Sheets. n8n recommends OAuth2 as the default, and for most people that's the right call.
The important fork is where your n8n runs:
On n8n Cloud, Google Sheets is one of the nodes covered by managed OAuth2. You pick the credential, click through Google's consent screen, and you're done. No Google Cloud Console, no client ID. If you're on Cloud, skip to the operations.
Self-hosted, managed OAuth2 isn't available. You have to create your own OAuth client in Google Cloud Console, or use a service account. This is the step people get stuck on, so it gets its own section.
That gap is worth understanding rather than resenting. The managed credential works because n8n owns a verified OAuth app registered against its own domain. A self-hosted instance is a different application at a different address as far as Google is concerned, so Google needs you to register it.
Setting up custom OAuth2, step by step
Budget ten minutes the first time. Once the Google Cloud project exists, adding more Google services to it takes about one minute each.
1. Create a Google Cloud project
In the Google Cloud Console, open the project dropdown in the top bar and choose New project. Name it something you'll recognize — n8n-automation is fine. Make sure it's selected in the top bar before continuing.
2. Enable both APIs
Go to APIs & Services → Library. Enable:
- Google Sheets API
- Google Drive API
Enabling only the Sheets API is the single most common mistake here. n8n uses the Drive API to list your spreadsheets in the Document dropdown, so without it the node authenticates fine and then shows you an empty list — which looks like a permissions bug and isn't one. Google Docs and Slides have the same requirement.
3. Configure the OAuth consent screen
Go to APIs & Services → OAuth consent screen and select Get started.
- Enter an app name and user support email.
- Choose your audience: Internal limits access to accounts in your Google Workspace organization. External allows any Google account, and is what you'll pick if you're on a personal Gmail account.
- Add contact email addresses and accept the User Data Policy.
- Under Branding → Authorized domains, add
n8n.cloudif you're on Cloud, or your own domain if you're self-hosting.
Read this part before you continue. If you choose External, your app starts in Testing mode. Two consequences follow:
- Only Google accounts you explicitly add as test users can complete the OAuth flow. Add yours under Audience.
- Consent and tokens expire after seven days. Your workflow runs perfectly all week and then fails on day eight with an auth error, for no reason you changed.
That second one is the highest-frustration item on this page, because the failure arrives long after the setup that caused it. If this is a workflow you intend to keep, publish the app rather than leaving it in Testing.
4. Create the OAuth client
Go to APIs & Services → Credentials → + Create credentials → OAuth client ID, and choose Web application as the application type.
Now switch to n8n. Create a new Google Sheets OAuth2 API credential, and copy the OAuth Redirect URL it displays. It follows this shape:
https://your-n8n-domain.com/rest/oauth2-credential/callback
Paste that into Authorized redirect URIs in Google Cloud Console and select Create. Google shows you the Client ID and Client Secret in a modal — copy both now, because the secret isn't fully visible again later.
If you're developing locally, this works without SSL or port forwarding:
http://localhost:5678/rest/oauth2-credential/callback
Copy the URL exactly as n8n gives it to you, including the protocol and port number. A redirect_uri_mismatch error almost always means one of those three differs — http against https, a missing :5678, or a trailing slash. Google matches this string literally.
One consequence for self-hosters: a public n8n instance needs a stable HTTPS address before this credential is worth creating, because the redirect URI is registered against a fixed URL. If your instance is still on a bare IP or a tunnel URL that changes, sort out a real domain and certificate first — otherwise you'll be re-registering the redirect URI every time the address moves.
5. Finish in n8n
Paste the Client ID and Client Secret into the n8n credential, click Sign in with Google, complete the consent flow, and save. If your app is External and in Testing, you'll see an "unverified app" warning — expected, and you can proceed through it for an app you created yourself.
The scopes n8n requests for Sheets are:
https://www.googleapis.com/auth/spreadsheets
https://www.googleapis.com/auth/drive.file
drive.file is deliberately narrow: it grants access only to files opened or created through the app, not your whole Drive.
When to use a service account instead
A service account is a robot Google account with its own email address. Instead of a consent flow, you share the spreadsheet with that address the same way you'd share it with a colleague.
Use one when:
- The workflow belongs to a team or a server rather than to you personally. Service account access doesn't disappear when someone leaves.
- You want to avoid the seven-day token expiry entirely.
- The instance runs unattended and nobody will be around to re-authorize it.
Stay with OAuth2 when the sheets are your own and you want the fastest setup. One caveat worth knowing: Google supports service accounts with Gmail only through domain-wide delegation, which Google discourages and which behaves inconsistently — so n8n recommends OAuth2 for Gmail specifically. For Sheets, both are fully supported.
The thing people forget with service accounts: share the sheet with the service account's email address. Nothing works until you do, and the resulting permission error doesn't say so.
The operations, and which one you want
The Google Sheets node has two resources. Document operates on the spreadsheet file (Create, Delete). Sheet Within Document operates on a tab inside it, which is where nearly all real work happens:
| Operation | Use it when |
|---|---|
| Append Row | Adding new records. Never touches existing rows. |
| Append or Update Row | You don't know whether the record exists yet — the upsert. |
| Update Row | The row definitely exists and you're changing it. |
| Get Row(s) | Reading data out, optionally filtered. |
| Clear | Wiping a sheet's contents before a fresh load. |
| Delete Rows or Columns | Removing rows or columns by position. |
| Create / Delete | Adding or removing a tab. |
If you're unsure between the first three: use Append Row for logs and event streams where every entry is new, and Append or Update Row for anything keyed on an identifier — a customer email, an order ID — where running the workflow twice shouldn't produce two rows.
Every operation shares the same two selectors. Document and Sheet can each be picked From list, By URL, or By ID (Sheet also offers By Name). Use From list while building, since it confirms your credential can actually see the file. Switch to By ID or an expression when the sheet is chosen dynamically at runtime.
Build 1: append incoming data to a sheet
The most common workflow in n8n: something happens, write a row about it.
- Add a trigger. A Webhook node works for form submissions; a Schedule Trigger works for periodic jobs.
- Add a Google Sheets node. Set Resource to Sheet Within Document and Operation to Append Row.
- Select your Document and Sheet.
- Set Mapping Column Mode.
That last setting is the one to understand. It has three options:
- Map Automatically — n8n matches incoming field names to column headers. Clean and fast when your data's keys already match your headers exactly.
- Map Each Column Manually — n8n lists your sheet's columns and you drag or type an expression into each. More clicks, far more predictable.
- Nothing — pass the raw data through untouched, for cases where you're shaping it upstream.
Start with Map Each Column Manually while you're learning. Automatic mapping is silent when it fails: a field whose name doesn't match a header simply doesn't get written, and you get a row with blank cells rather than an error.
Three options are worth setting under Options:
Cell Format — Let Google Sheets format (the default) means Google interprets your values the way it would if you typed them, so 1/2 may become a date and a leading + may become a formula. Let n8n format writes values as-is. If you're writing product codes, phone numbers, or anything with leading zeros, choose Let n8n format.
Data Location on Sheet — sets which row holds your headers and which row your data starts on. Change these if your sheet has a title block above the table.
Handling extra fields in input — what happens when your data contains a key with no matching column: Insert in New Column(s), Ignore Them, or Error. For a workflow you want to trust, Error is the honest choice — it surfaces schema drift instead of hiding it.
Build 2: read rows and filter them
Set Operation to Get Row(s). With no filters, it returns every row as an n8n item.
Add a Filter to narrow it: pick a Column, give it a Value, and only matching rows come back. Under Options, When Filter Has Multiple Matches decides whether you get the first match or all of them — set it deliberately, because the default behavior surprises people building "look up this customer" workflows.
Also under Options, Output Formatting controls what you actually receive:
- Values unformatted — the raw underlying value. A currency cell comes through as
1234.5, not$1,234.50. - Values formatted — the display string, as shown in the sheet.
- Formulas — the formula text rather than its result.
Choose unformatted when the number feeds a calculation, and formatted when it feeds a message someone will read. Getting this backwards produces the classic bug where a total is off because $1,234.50 was parsed as a string.
Build 3: update a row instead of duplicating it
Append or Update Row is the operation that makes a workflow safe to re-run.
Set Mapping Column Mode to Map Each Column Manually, and a Column to Match On appears. That column is the identity key. n8n looks for a row whose value in that column matches your incoming data — if it finds one, it updates that row; if not, it appends a new one.
Pick something genuinely unique. Email addresses and order IDs work. Names don't.
Two limits to plan around:
You can only match on one column. There's no composite key. If your identity is genuinely two fields, build a combined key column in the sheet — email|date, say — and match on that.
The "Use Append" option is a performance trade. Enabling it uses Google's append endpoint, which means fewer API calls and better throughput on large runs. The documented cost: if your existing sheet data has gaps or breaks between rows and columns, n8n can place new data in the wrong location. Leave it off on a messy sheet; turn it on for a clean, densely-populated one where you're writing a lot.
Triggering a workflow when a row is added
The Google Sheets Trigger is a separate node from the one above, and it starts a workflow when the sheet changes. Three events are available:
- Row added
- Row updated
- Row added or updated
This is a polling trigger, not a push. Google doesn't notify n8n; n8n checks the sheet on the interval you configure. Two things follow. Detection is not instant — it's bounded by your polling interval. And a row added and then deleted between two polls is never seen at all.
For anything needing true real-time behavior, have the upstream system call an n8n Webhook node directly rather than routing it through a spreadsheet. That needs a publicly reachable instance, which is the self-hosting setup. The Telegram Trigger is a good example of the push model for comparison — updates arrive the instant they happen, at the cost of requiring a public HTTPS address.
Two documented issues are worth knowing before they bite:
Dates arrive as numbers like 45678.5. That's Google's serial format — the whole number is days since December 30, 1899, and the decimal is the fraction of a 24-hour day. Fix it in the node: Add option → DateTime Render → Formatted String. This applies when Trigger On is set to Row Added. The resulting format follows your spreadsheet's locale, which you can change under File → Settings → Locale.
The execution appears stuck waiting for a trigger event. On self-hosted instances this is usually a reverse proxy without websocket support. Enable websocket proxying in Nginx, Caddy, Apache, or Traefik — whichever sits in front of n8n. The self-hosting guide covers a configuration that has this set correctly.
Gotchas that cost people hours
The Document dropdown is empty even though the credential connected. The Google Drive API isn't enabled on your Google Cloud project. Sheets API alone authenticates but can't list files.
redirect_uri_mismatch when signing in.
The redirect URI in Google Cloud Console doesn't match n8n's byte for byte. Check protocol, port, and trailing slash.
It worked for a week, then stopped. Your OAuth app is External and still in Testing mode, where consent and tokens expire after seven days. Publish the app, or move to a service account.
"Column names were updated after the node was set up." You changed headers in the sheet after configuring the node. n8n cached the old ones. Re-select the Mapping Column Mode option to refresh the column list, then fix any parameters that referenced the old names.
An array field writes as [object Object] or errors.
Google Sheets cells hold single values, so an array has to be flattened into key/value pairs first. Use a Split Out node to turn each array element into its own item and its own row, or a Code node to join them into a string if one cell is what you want.
One row goes in, but you expected many. n8n runs the node once per input item. If your data arrived as a single item containing an array of records, you get one row. Split it first — same Split Out node.
Leading zeros vanish and codes turn into dates. Google is interpreting your values. Set Cell Format to Let n8n format.
A permission error with a service account. You didn't share the spreadsheet with the service account's email address. Open the sheet, hit Share, paste that address, give it Editor.
Frequently asked questions
Do I need a paid Google Workspace account? No. A free personal Google account works. Workspace only matters if you want the Internal audience option, which avoids the Testing-mode expiry.
Is there a limit on how much data I can write? Google's Sheets API enforces per-minute quotas per project and per user, so very high-frequency writes will hit rate limits. Batch your writes rather than calling the node once per record, and be aware that a spreadsheet is a poor substitute for a database above a few tens of thousands of rows.
Can n8n read a sheet shared with me by someone else?
Yes, provided your Google account has access. With OAuth2 the drive.file scope covers files you open through the app. With a service account, the owner has to share the sheet with the service account address.
Why does my number come through as a string? Output Formatting is set to Values formatted. Switch to Values unformatted for the raw value.
Can I use one credential for Sheets, Drive, and Gmail? You can reuse one Google Cloud project for all of them, but n8n uses a separate credential type per service. Enable each API in the same project and create each credential against the same OAuth client.
Does this work the same on n8n Cloud and self-hosted? The node is identical. Only the credential setup differs — Cloud gives you managed OAuth2, self-hosted requires the custom client above.
Official references worth bookmarking: n8n's Google Sheets node documentation and Google's Sheets API authorization scopes.
Want this as a working workflow? Importable Google Sheets templates — append, upsert, and scheduled sync — are on our templates page. Running n8n yourself and still setting things up? Start with n8n in Docker, then put it on a real domain with HTTPS so your OAuth redirect URI stops moving.