Register your interest: Tag @Cody, get an agent
BlogResources

How to connect Google Sheets to Notion, and whether you should

A spreadsheet that has become a tracker is a spreadsheet asking to be a database. What the move buys you, what it costs, and how to sync without creating two sources of truth.

Osman RamadanOsman Ramadan11 min read

Summarize with AI

How to connect Google Sheets to Notion, and whether you should
On this page

The reason people want this is almost always the same: a spreadsheet has become a tracker, several people are updating it, and it has started to strain. Notion databases handle that better — properties with types, views per person, relations between things — and the move is often worth making.

The mistake is doing it as a sync rather than a migration, because a spreadsheet and a database holding the same records, both editable, is a guarantee that they will disagree and that somebody will spend a morning working out which is right.

What we'll cover

Migration or sync

Decide this first, because everything else follows.

A migration moves once and the sheet stops being edited. Clean, and it requires telling people the spreadsheet is retired, which is the part that gets skipped.

A one-way feed keeps the sheet as the source and mirrors it into Notion for reading. Reasonable when the sheet is generated by something else — an export, a form, a report.

A two-way sync is the thing people ask for and the thing that causes the problems described below.

If people edit in both places, you do not have a sync — you have two systems. No automation resolves a genuine conflict about which value is correct, because that is a judgement.

Most requests for two-way sync are satisfied by deciding which one owns the data, which is a conversation rather than a build.

What the move actually buys

Worth being concrete, because it determines whether the effort is justified.

Types. A date property is a date, and a select is a fixed set. A spreadsheet column is whatever somebody typed.

Views per person. Everybody filters and sorts their own way without anybody else's view changing, which is the single most common source of spreadsheet friction.

Relations. A row can link to another database's row, properly, rather than by pasting a name that goes stale.

Comments in place, on a record, rather than in a cell note nobody sees.

Permissions, at the page level, and a history of who changed what.

What it does not buy is calculation. Notion formulas are far more limited than a spreadsheet's, which is the most common regret after migrating.

Building it with CodeWords

CodeWords connects to more than 3,000 integrations, and the connection is made once and reused.

  1. Open CodeWords and start a new automation.
  2. Describe what should happen in plain language to Cody, the automation builder: which sheet, which Notion database, and which direction the data flows.
  3. Authorize the connection to Google and to Notion, then share the target database with the Notion integration — access is granted per page and this manual step is why most first Notion automations find nothing.
  4. Describe the exceptions: a row that matches nothing, a select option that does not exist, a required property left empty.
  5. Run it into a duplicate of the database and check the property types before touching the real one.

You describe the outcome; Cody builds it, connects it, and deploys it. The free plan covers light use, with Pro at $39 per month and Business at $100 per month as usage grows; details are on the pricing page.

Designing the properties before importing

The step that determines whether the database is usable afterwards.

Do not import and fix later. A database created by importing a sheet gets every column as text, and converting types afterwards with data in place is painful.

Create the database first, with the property types you want, then import into it.

Use select for anything with fixed values — status, category, owner type — so views and filters work. This is the main reason to have moved.

Use a relation where a column holds another entity's name, rather than leaving it as text.

Add a property for the source row identifier, which is what makes the import repeatable and the sync possible.

Decide what the title property is. Notion requires one, and a database whose title is the least informative column is unpleasant to use.

Columns that need a decision

Formulas. A calculated column has to become a Notion formula, a static value, or something computed by the automation — and Notion's formula language is more limited, so check before assuming.

Dates in odd formats. Spreadsheets hold dates as text surprisingly often, and Notion will reject what it cannot parse.

Multi-value cells, where somebody has comma-separated several things, need to become a multi-select or a relation.

Empty cells, which mean different things per column — not applicable, not yet known, or zero — and Notion's blank does not distinguish them either.

Duplicate rows, which a spreadsheet tolerates and which become duplicate pages that are harder to spot.

Formatting that carries meaning. Colour-coded rows are information that does not survive the move, so the meaning has to become a property first.

If it genuinely must stay in sync

Sometimes it does — a form writes to the sheet, and the team works in Notion.

Make the direction explicit and say it in both places, in the sheet's header and in the database's description.

Key on a stable identifier, stored as a property, not on the title or the row number.

Upsert, never insert. Match on the identifier and update, so a re-run corrects rather than duplicates.

Decide per property who wins. Some fields come from the sheet; some are added by the team in Notion and must never be overwritten. Write that list down.

Never overwrite a property the team owns, which is the failure that destroys trust in a sync permanently.

Reconcile periodically and report differences rather than resolving them silently.

What Notion will not do that Sheets does

Worth knowing before committing.

Real formulas. Anything beyond modest computation is harder, and array-style work is not comparable.

Large row counts. A database with tens of thousands of rows is slow to query and slow to use.

Arbitrary cell references. There is no equivalent of pointing one cell at another.

Charts. Notion's visualisation is limited compared with a spreadsheet's.

Fast bulk editing. A spreadsheet's fill-down and paste-a-column are genuinely faster for bulk changes.

If the sheet is mostly calculation, it should stay a sheet. If it is mostly a list of things people update, the database is better.

Telling people the spreadsheet is retired

The step that decides whether the migration worked, and the one that gets skipped because it is not technical.

Announce a date rather than letting the two coexist indefinitely, because coexistence is the state everything above warns about.

Make the sheet read-only on that date rather than deleting it, so anybody who goes looking finds it and sees why.

Put a link to the database in the sheet's first row, in a colour nobody can miss.

Rename the file to say it is retired, since a search result showing the old name is how people end up back in it.

Keep it for a while. Somebody will need to check what a value was before the move, and a retired sheet is the cheapest possible archive.

Watch for edits. If people are still changing the sheet a month later, the database is missing something they need, and that is worth finding out rather than enforcing.

Making it survive

Upsert on a stored identifier, never insert blindly.

Read the database's property types and validate values before writing, rather than discovering a rejection mid-import.

Handle pagination and pace requests, since Notion's rate limits are modest and an import of a few thousand rows takes a while.

Report rows that could not be written, with the reason, rather than skipping them silently.

Report the outcome. Rows read, pages created, pages updated, and anything rejected.

Limits worth knowing about

Notion access is per page. The integration sees only what has been shared with it, which is the most common reason an automation finds nothing.

Rate limits are modest, roughly a few requests per second, so a large import is a slow job rather than a fast one.

Property types are strict, and writing a value of the wrong shape fails, with shapes differing per type.

Select options can be created on write in some cases, which is convenient and can quietly produce forty variants of a status.

Large databases degrade, so a sheet with tens of thousands of rows is not obviously better off as a Notion database.

What to set up first

Decide the direction, then build a one-way import into a database whose properties you created deliberately, keyed on a stored row identifier. Do it into a duplicate database first and look at the result before pointing anything at something people use.

Two habits make the difference. Create the properties before importing rather than letting the import create text columns, since converting types with data in place is the worst part of this whole exercise. And write the direction down in both the sheet and the database, because the failure here is not technical — it is two people editing the same record in different places.

Frequently asked questions

Should this be a migration or a sync?

A migration, usually, with the spreadsheet retired. A one-way feed when the sheet is generated by something else. A two-way sync only when you can say per property which side owns it, and most requests for one are really a question about who owns the data.

Why does my Notion automation find nothing?

Because access is granted per page and the database has not been shared with the integration. It is a manual step inside Notion and it is why nearly every first Notion automation returns empty.

Should I import first and fix the types afterwards?

No. Create the database with the property types you want, then import into it. An import into an empty database makes everything text, and converting types with data already in place is painful.

What gets lost in the move?

Formulas beyond modest computation, colour-coded formatting that carried meaning, charts, and fast bulk editing. If the sheet is mostly calculation, it should probably stay a sheet.

How do I avoid duplicate pages?

Store the source row identifier as a property and upsert on it. Matching on the title breaks as soon as somebody edits one, and matching on row number breaks as soon as anybody sorts the sheet.

Can both sides be edited?

Only if you decide per property which side wins, and write that list down. Never overwrite a property the team maintains in Notion — that is the failure that ends trust in a sync permanently.

Is a Notion database a good system of record?

For lightweight internal tracking, yes. For anything operational — money, obligations, customer commitments — the absence of constraints and referential integrity matters, and it is worth being honest about which situation you are in.

How do I stop people using the old spreadsheet?

Announce a date, then make it read-only rather than deleting it, rename it to say it is retired, and put a link to the database in the first row. Keep it as an archive — somebody will need to check a pre-migration value.

People keep editing the sheet anyway — what does that mean?

That the database is missing something they need. It is worth finding out what rather than enforcing the move, because the workaround will simply appear somewhere else.

Get started today

Your first workflow is free to build.

Describe what you need. Cody handles the build, the connections, and the deployment.