Connect Google Sheets to PostgreSQL
Pull a range or a whole tab into a pipeline, or write results back into a sheet on a schedule. Works as both a source and a destination.
What you can pull from Google Sheets
A Google Sheets job pulls records into a flat table on your schedule. Nested fields are flattened into ordinary columns, so what lands in PostgreSQL is ready to sort, filter and total rather than needing another cleanup step.
- Any tab or A1 range
- Header-mapped columns
- Append or overwrite
Options you can set
- spreadsheet_id
- Spreadsheet ID, or the full Google Sheets URL - the ID is extracted from it
- sheet_name
- Which tab to read or write — defaults to
Sheet1 - range
- A1 notation range, e.g. A1:Z1000. Omit to read the whole sheet
How it lands in PostgreSQL
PostgreSQL accepts append, replace, upsert writes. Upsert is the one that matters on a schedule: it merges on a key you choose, so matching rows are updated in place and only genuinely new records are created. Append instead, and a daily job multiplies your data.
Upsert uses key_columns as the conflict target.
- Rows are written in chunks of 1,000 by default, configurable up to 50,000.
- Replace truncates the target table before writing.
Authentication
You connect Google Sheets once through OAuth and the credential is stored server-side, never pasted into a cell or a formula. Only the scopes the connector actually calls are requested — the table below lists each one and what it is for.
What Google Sheets is asked for
- spreadsheets sensitive
- Read and write the sheets you point a job at
- userinfo.email non-sensitive
- Label the connection so it is identifiable
What is deliberately not requested
drive.readonly — A *restricted* scope - the tier requiring an independent security assessment - and it would grant read access to every file in your Drive. It existed only so the connection test could list a few spreadsheets. That convenience is not worth the access, so you paste a spreadsheet URL instead of browsing Drive.
Keeping runs incremental
Not for this source: Each run reads the configured range in full. Where that is expensive, narrow the job itself and run it less often.
Pair a narrowed read with an upsert write and a re-run costs almost nothing: the rows it already knows are updated, and nothing is duplicated. That combination is what makes a frequent schedule affordable.
Common gotchas
Most of what goes wrong with a scheduled sync is not a bug — it is a detail of how one side behaves that nobody wrote down. These are the ones that come up for this pair.
An open-ended range beats a padded one
Writing to A2:D is better than A2:D1000 for appends: the padded form reserves rows you may not need and makes the append boundary ambiguous.
Set either table or query, never both
The config rejects both being set, and rejects neither - caught when the job is saved rather than on its first run.
A spreadsheet has no types
Every cell arrives as text. Cast explicitly on the way in, or you will get a table of varchar columns that cannot be compared or summed.
Set it up in four steps
- 1
Connect Google Sheets
Connect your Google account once. Paste a spreadsheet URL or ID.
- 2
Connect PostgreSQL
Host, port, database, user and password, stored encrypted.
- 3
Shape the data
Select the columns you want, filter rows, cast types and add computed fields. Everything else is dropped before it reaches the destination.
- 4
Schedule it
Run once, or on a cron. Every run refreshes PostgreSQL with the latest Google Sheets data.
Do I need an add-on or extension for this?
No. The job runs server-side and writes into PostgreSQL through its API, so there is nothing installed in the destination itself. It keeps running when nobody has the file open, and a copy of the file does not need anything installed to work.
How often does the data refresh?
On whatever schedule you set with a cron expression — hourly, daily, or a specific time on specific days. Each run pulls the latest from Google Sheets.
Do I need to write any code?
No. You connect both sides, map the fields in a wizard and set a schedule. Computed fields accept small expressions — abs, round, min, max, len — but there is nothing to host or maintain.
Do I have to give access to my whole Google Drive?
No. Only the spreadsheets scope is requested. The Drive scope that most spreadsheet tools ask for is a restricted scope granting read access to every file you own; it is deliberately not requested here, which is why you paste a spreadsheet URL rather than browsing Drive.
Will this keep working if I copy the sheet?
The job writes to a specific spreadsheet ID, so a copy will not receive data until you point a job at it. Nothing is installed in the file itself, though, so copying never breaks the original.
Can I run my own SQL rather than reading a table?
Yes. Set a query instead of a table and aggregate in the database, which is usually far cheaper than moving raw rows and aggregating downstream. One statement only - multi-statement queries are rejected.
Does it need a superuser?
No. A user with SELECT on what you are reading is enough for a source; a destination needs INSERT and, for upsert, UPDATE on the target table.
Is the Google Sheets connector free to use?
You can connect Google Sheets and start syncing on the free plan.
Other sources into PostgreSQL
Related reading
Airtable upsert: stop creating duplicate records
Appending on every run turns a tidy base into six copies of every row. Here is how upsert works in Airtable, and how to choose a merge key that holds up.
Export Stripe data to Google Sheets
Stripe's dashboard exports are manual and stale the moment you download them. Here is how to keep customers, invoices and charges fresh in a sheet instead.
Import a CSV from a URL into Airtable
Point a job at a CSV link and it re-reads it on your schedule — no download, no manual import. Here is the setup, and the field-type trap to avoid first.