Google Sheets Sync: Two-Way With Your CRM

Google Sheets Sync: Two-Way With Your CRM

Google Sheets Sync: Two-Way With Your CRM

Photo of Utku Zihnioglu

Utku Zihnioglu

CEO & Co-founder

A RevOps lead opens the prospect sheet on Monday morning, deletes 12 closed-lost rows, marks 4 others as "do not contact," and pastes in 30 new accounts from a conference list. By Tuesday, the closed-lost rows are back, the do-not-contact flags have been overwritten, and the new accounts haven't appeared in HubSpot. The Zap is firing. The Zap is doing exactly what it was built to do, which happens to be the wrong thing.

This is what most people get when they search for "google sheets sync" and end up with an append-only integration. Rows go in. Nothing comes back. Edits in the sheet are silently destroyed by the next sync run. Deletes are ignored. The sheet becomes a write-only log instead of the working surface it was supposed to be.

A real two-way integration is different. A hand-typed edit lands in your CRM, and a CRM update reflects in the sheet within minutes. That kind of integration is rare in the market. Most vendors call themselves "Google Sheets integrations" while supporting only insert. The good news is that the architecture isn't complicated, and once you understand what's actually required, picking a tool that does it right gets a lot easier.

Why teams reach for Google Sheets sync before a real database

Sheets is the universal scratchpad for small RevOps teams, founders, and anyone who needs to coordinate a list of records without filing a Jira ticket. It has properties no real database has:

  • Anyone on the team can edit it without a login or a permission grant beyond a share link.

  • Filters, sorts, and notes work without a query language.

  • Comments, conditional formatting, and formulas turn a flat list into a workflow.

  • The history is visible, so reverting a mistake takes one click.

A CRM has none of those properties for ad-hoc lists. HubSpot lets you build a static list, but editing 50 rows of custom fields by hand is miserable. Salesforce reports are read-only. Attio is closer, but a sheet is still faster for the case where someone wants to look at 200 prospects, mark which ones to contact, and hand the result back to ops.

The pattern repeats: sheet as the working layer, database or CRM as the system of record. According to the W3C tabular data primer, this kind of human-editable tabular interface is the most widely deployed data format on the web for a reason. It works.

Where it breaks is the handoff. Once the RevOps lead is done editing, somebody has to get those changes back into the source. That somebody is usually a Zap, a Python script, or, most often, a copy-paste session at the end of the week. A real two-way integration is the architecture that closes that loop.

What two-way Google Sheets sync actually requires

Reading from a sheet is easy. Appending rows is easy. The hard parts of two-way Google Sheets sync are the parts that turn a sheet into a real record store:

  1. A stable primary key column. Every row needs an identifier that doesn't change when the row is sorted, filtered, or rearranged. Row number is not stable. The ID column has to be explicit, named, and present on every row, including new ones a human types in.

  2. Upsert semantics by primary key. When the sync runs, it has to look at the sheet, look at the destination, and decide row-by-row whether to insert (no match in destination), update (match exists, fields differ), or skip (match exists, fields identical). Without this, the sync either creates duplicates or overwrites things it shouldn't.

  3. Delete propagation. A row removed from the sheet has to either delete the record in the destination, or be flagged so an operator can decide. Most "Google Sheets bidirectional sync" features stop short of this and quietly leave deletes orphaned.

  4. Field-level change tracking. A row updated in the sheet should write only the changed fields back to the destination, not blast every field over again. This matters because the destination usually has fields the sheet doesn't (notes added by sales, tags from marketing automation), and a full overwrite wipes them.

  5. Conflict handling. What happens when the same row was edited in the sheet and in the destination since the last sync? A real engine has a rule (last-write-wins by default, source-wins or sheet-wins as overrides). Tools without this either silently lose one side's changes or refuse to sync the row and leave the operator guessing.

Most existing "Google Sheets integration" features do step 1 (sometimes), step 2 (rarely), and skip 3, 4, and 5 entirely. That's why the sheet drifts from the CRM over time even when both are technically connected.

Append-only Google Sheets integrations vs. full CRUD sync

The market splits cleanly. On one side: hundreds of "Google Sheets integration" listings that, when you read the docs, only support insert. They are useful for logging, dashboards, and one-way exports. On the other side: a much smaller set of tools that treat the sheet as a real destination with read, write, update, and delete.

Here is the practical difference for a RevOps team:

Capability

Append-only integration

Full CRUD Google Sheets sync

Add rows from CRM to sheet

Yes

Yes

Update a sheet row when the CRM record changes

Sometimes (often appends a duplicate)

Yes, by primary key

Push a hand-edited sheet row back to the CRM

No

Yes

Delete a CRM record when the sheet row is removed

No

Yes (mirror mode)

Backfill an existing sheet without duplicating rows

No

Yes

Detect conflicts between sheet and CRM edits

No

Yes

Vendors in the first column include most of the workflow automation tools that fire on a Sheets trigger and most of the connector marketplaces that list "Google Sheets" without specifying capabilities. Vendors in the second column are rare. Native CDPs and reverse-ETL tools designed around bidirectional sync as a first-class behavior are where to look.

The way to tell which camp a tool is in: read the docs for "delete" and "primary key." If neither word appears, it's append-only. If both appear with examples, it's a real two-way sync.

Common Google Sheets sync patterns in RevOps

A few specific workflows show up over and over once two-way sync is on the table. None of them work with append-only integrations.

The CRM scratchpad. Sales pulls a list of 300 prospects from HubSpot into a sheet. They mark each row with a status (interested, not interested, do-not-contact), add notes, and re-prioritize. By Friday, those statuses and notes need to be written back to HubSpot as custom field updates. Two-way sync makes the sheet the editing surface; HubSpot stays the system of record.

Manual overrides on enriched data. Marketing runs an enrichment pipeline that sets industry, company_size, and employee_count on every contact. The enrichment is wrong about 8% of the time. Ops keeps a sheet where they correct the wrong values, and those corrections sync back to the CRM with sheet-wins conflict resolution. Without two-way sync, every nightly enrichment run wipes the corrections.

Hand-curated segments. A growth team builds a sheet of "VIP accounts to invite to the beta." The list lives in the sheet because the criteria are squishy and the team wants to debate inclusion. Once consensus is reached, the sheet syncs into a HubSpot static list, an Attio collection, or a Mailchimp tag. The list stays in sync as the team adds and removes rows.

Vendor or partner data exchange. A partner sends a weekly sheet of new leads. The sheet drops into a shared Drive folder. Two-way sync ingests new rows into the CRM and writes a partner_status field back to the sheet so the partner can see which leads were accepted. This replaces the email-and-CSV ritual that used to take an hour each Monday.

In every one of these patterns, the sheet is not a destination or a source. It's both. That's the workflow that append-only sync can't model.

How to set up two-way Google Sheets sync without scripts or Zapier

The DIY path is well-trodden. You enable the Google Sheets API, create a service account, pull credentials, install a client library, write a script that reads from your source (Postgres, most often), opens the sheet, and overwrites the data. It works for the first run.

It stops working when you need any of the things from the previous section. Reading edits back from the sheet means a second script. Handling deletes means tracking row state across runs. Avoiding duplicates on re-import means an upsert layer. Each addition is half a day of engineering, and the resulting code becomes nobody's favorite thing to maintain.

The non-DIY path collapses all of that into a config:

  1. Connect Google Sheets and the destination. Authenticate both. The destination might be HubSpot, Salesforce, Attio, Postgres, or another sheet. The model is the same in every case.

  2. Pick a record type and a primary key column. The sheet's first row becomes the field schema; one column is designated as the primary key (usually id or email). New rows added by hand need a value in that column.

  3. Pick a sync direction and a mode. Two-way means both directions, and the mode determines what happens to extra rows. Mirror mode keeps the sheet and destination as exact copies; update-or-create mode adds new rows in both directions but never deletes; create-only mode appends without overwriting.

  4. Map fields. The sheet columns map to record properties. Most tools auto-detect on first run.

  5. Set a schedule. Realtime sync runs as soon as a change is detected. A 5-minute schedule is usually enough for sheet workflows.

The piece that matters most is step 3, the sync mode. Most append-only integrations don't expose a mode at all. They have one behavior, and it is "append." Tools that treat sheets as a real destination expose four modes (update, update-or-create, create-only, mirror) and let you pick per sync.

A small caveat on Google Sheets as infrastructure: it has limits. The Google Sheets API quotas cap reads and writes per minute, and a single spreadsheet maxes out at 10 million cells. For a 300-row prospect sheet that's irrelevant. For a sync that tries to mirror a 500,000-row Postgres table into a sheet, it's a wall. Use sheets for the human-edited surface; keep the database as the source of truth for everything else.

The result, when set up correctly, is the workflow people thought they were getting when they first connected Google Sheets to anything: edit in the sheet, see it in the CRM, edit in the CRM, see it in the sheet, delete on either side and have the other side notice. Boring on purpose.

What does two-way Google Sheets sync actually mean?

Do I need a primary key column to sync Google Sheets two ways?

Can Zapier or Make do two-way Google Sheets sync?

How does two-way Google Sheets sync handle deletes?

Is Google Sheets a good database for production data?

Ready to get started?

No credit card required

Free 100k syncs every month