Google Sheets Sync: Export Datasets & JSON to Sheets
Pricing
from $0.07 / 1,000 row writtens
Google Sheets Sync: Export Datasets & JSON to Sheets
Export any Apify dataset or JSON to Google Sheets reliably. Append, replace or dedupe/upsert rows by key, batch 100k+ rows, auto-retry Google errors, clear fixes in every error. Official Sheets API, your own service account.
Pricing
from $0.07 / 1,000 row writtens
Rating
0.0
(0)
Developer
Nicholas Powers
Maintained by CommunityActor stats
0
Bookmarked
1
Total users
0
Monthly active users
5 hours ago
Last modified
Categories
Share
Google Sheets Sync: Export Apify Datasets & JSON to Google Sheets
Get scraper results into Google Sheets on every run, without losing rows or babysitting the export. Append new rows, replace a tab, or dedupe and upsert by a key such as url, id or email. Tested live with 100,000 rows in one run, retries Google errors on its own, and every error message tells you how to fix it.
- ⚡ Quick start: paste a sheet URL, a dataset and a Google service account key, then run
- 🔁 Append, replace, dedupe or upsert, with new fields added as new columns
- 🧱 Large datasets: streamed in batches, rate-limited under Google's quota, checked against the 10M-cell limit before writing
- 🛡️ Safe by default: never rewrites existing rows on append, never wipes a tab on an empty scrape, no formula injection
- 🔌 Works as an integration: results go to Sheets each time your scraper finishes
- 💸 $0.10 per 1,000 rows. Skipped duplicates are free, and there's no separate compute charge
Running with empty input (no sheet, credentials or data) exits successfully with a short demo message so Store health checks pass; provide a spreadsheet + credentials + datasetId for a real sync.
Why teams switch to Google Sheets Sync
Many dataset-to-Sheets exports download the whole sheet, merge it in memory, and write all of it back on every run. That gets slower as the sheet grows, hits cell limits, and lets two runs finishing at once overwrite each other. Google Sheets Sync is built differently:
| Google Sheets Sync | |
|---|---|
| Append | Uses Google's append API. Existing rows, formatting and formulas are never rewritten |
| Parallel runs | Two runs writing to one tab at the same time keep all rows (tested live) |
| Dedupe / upsert | Reads only the key column(s) from the sheet, not the whole tab |
| Big datasets | Streams the dataset in pages; 100,000 rows in about 2 minutes (tested live) |
| Login | Your own service account key, so no shared app that Google can block and no expiring browser login |
| Errors | Plain-English fixes, e.g. "Share the spreadsheet with sync@project.iam.gserviceaccount.com as an Editor" |
| Retries | Exponential backoff on Google 429/5xx errors and network drops |
| Permissions | Apify limited permissions: it can read only the dataset you give it, with no approval prompt |
Quick start (about 5 minutes the first time)
- Create a Google service account key. See Setup: service account below.
- Share your Google Sheet with the service account email (
…@….iam.gserviceaccount.com) as Editor. - Fill in the input:
- Google Sheet URL: paste the browser URL.
#gid=picks a tab, or type a Tab name and it's created if missing. - Mode:
append,replaceordedupe. - Apify dataset: pick the dataset from your scraper run.
- Google service account key: paste the whole JSON file. It's stored encrypted as a secret.
- Google Sheet URL: paste the browser URL.
- Run. The log ends with a link to your sheet.
{"spreadsheet": "https://docs.google.com/spreadsheets/d/1AbC.../edit#gid=0","sheetName": "Leads","mode": "dedupe","dedupeOn": ["url"],"updateExisting": true,"datasetId": "aBcD1234efGH","serviceAccountJson": "{ \"type\": \"service_account\", ... }"}
Setup: service account (recommended)
A service account is a robot Google account that never expires and never asks you to log in again.
- Open Google Cloud Console and create a project (any name, no billing needed).
- Enable the Google Sheets API: console.cloud.google.com/apis/library/sheets.googleapis.com.
- Go to IAM & Admin → Service accounts → Create service account. A name is enough; skip the roles.
- Open the account and go to Keys → Add key → Create new key → JSON. A
.jsonfile downloads. - In Google Sheets, click Share and add the account's
client_emailas Editor. - Paste the full contents of the JSON file into Google service account key.
Workspace admins: if key creation is blocked by the
iam.disableServiceAccountKeyCreationpolicy, ask your admin for an exception or use OAuth below.
Setup: OAuth (alternative)
Use this if you can't create service account keys.
- In Google Cloud Console, create APIs & Services → Credentials → OAuth client ID (type Desktop app) and enable the Google Sheets API.
- Get a refresh token with the scope
https://www.googleapis.com/auth/spreadsheets, for example with the OAuth 2.0 Playground using your own client ID and secret ("Use your own OAuth credentials"). - Fill in OAuth client ID, OAuth client secret and OAuth refresh token.
The sheet must belong to, or be shared with, the Google account that authorized the token.
Run it automatically after every scraper run (integration)
- Open your scraper or task in Apify Console and go to Integrations → Connect Actor or task → Google Sheets Sync.
- The Apify dataset field is prefilled with
{{resource.defaultDatasetId}}, the dataset of the run that just finished. Keep it. - Add your sheet URL, mode and key, then save.
Every time the scraper succeeds, its new results go into your sheet. Use dedupe mode with dedupeOn so that re-scraped items update or skip instead of piling up.
Custom webhooks: send {"datasetId": "{{resource.defaultDatasetId}}", ...} as the payload and turn on Interpolate variables in string fields. A dataset ID that appears only inside the raw webhook payload isn't shared with limited-permission Actors, so always map it into the dataset field.
Modes
| Mode | What it does | Good for |
|---|---|---|
append (default) | Adds rows at the bottom. New fields become new columns on the right | Logs, daily exports |
replace | Clears the tab and writes fresh data, keeping your header order. An empty source leaves the tab untouched | Dashboards, "latest snapshot" |
dedupe | Appends only rows whose dedupeOn key isn't in the sheet yet | Lead lists, monitoring |
dedupe + updateExisting | Upsert: updates matching rows in place and appends new ones | Price trackers, CRM syncs, job boards |
Other options:
data: a JSON array instead of a datasetcolumns: which fields to write, and in what orderomitColumnsflatten: nested values becomea.bandlist/0columns, like Apify's CSV exportlimit/offsetmaxRows: a cap on rows written per runbatchSizerequestsPerMinuteclearIfEmptyparseValues: lets Google turn text into dates, numbers and formulas. It's off by default, so values are written as-is
Pricing
Pay only for rows actually written to your sheet. Skipped duplicates are free.
| Rows written | Free plan ($0.10 / 1K) | Bronze ($0.09 / 1K) | Silver ($0.08 / 1K) | Gold+ ($0.07 / 1K) |
|---|---|---|---|---|
| 1,000 | $0.10 | $0.09 | $0.08 | $0.07 |
| 10,000 | $1.00 | $0.90 | $0.80 | $0.70 |
| 100,000 | $10.00 | $9.00 | $8.00 | $7.00 |
Plus Apify's standard start fee of $0.00005 per run. Example: a daily scraper adding about 300 new rows in dedupe mode costs about $0.90 a month. Set Max cost per run in Apify and the Actor stops cleanly before going over it.
Output
Each run saves a summary to the default dataset and to the OUTPUT record in the key-value store:
{ "mode": "dedupe", "sheetTitle": "Leads", "itemsRead": 5000, "rowsAppended": 412, "rowsUpdated": 4588, "duplicatesSkipped": 0, "columns": 14, "url": "https://docs.google.com/spreadsheets/d/.../edit#gid=0" }
Troubleshooting
| Message | Fix |
|---|---|
| Permission denied. Share the spreadsheet with … | Share the sheet with the service account email shown, as Editor |
| Google Sheets API has not been used in project … / is disabled | Enable the Sheets API for the project your key belongs to |
| Google says this service account does not exist | The key or account was deleted. Create a new JSON key |
| Your Google key could not be used | Paste the whole JSON file, including the -----BEGIN PRIVATE KEY----- block |
| Spreadsheet not found | Check the URL, and that the sheet is shared with the service account |
| This run has no access to dataset … | Pick the dataset in Apify dataset, or map {{resource.defaultDatasetId}} into it in the integration |
| …was not filled in ({{…}}) | Your webhook didn't interpolate variables. Turn on Interpolate variables in string fields |
| …over Google's 10,000,000-cell limit | Use a new spreadsheet, delete unused tabs, restrict columns, or use replace |
| Google Sheets quota exceeded | Lower requestsPerMinute, raise batchSize, or avoid many parallel runs on one key |
FAQ
Does it work with Google Maps, Instagram, Amazon or any other Apify scraper? Yes. It reads any Apify dataset.
Will it overwrite my formatting or formulas? Not in append or dedupe mode. Rows are added below, and upserts change only the matched rows. replace clears cell values in the target tab.
Can several scrapers write to the same sheet? Yes. Use one tab per scraper, or the same tab. Parallel appends were tested without losing rows.
What about very large datasets? Rows are streamed and batched; 100,000 rows took about 2 minutes in testing. A Google spreadsheet holds at most 10 million cells, and the Actor checks that limit before writing.
Is my Google key safe? It's a secret input: Apify encrypts it, and it never appears in logs. The Actor runs with limited permissions and can't see the rest of your Apify account.
Can I import from Google Sheets into Apify? This Actor exports datasets and JSON to Sheets.
Keywords: export Apify dataset to Google Sheets, scraper to Google Sheets, JSON to Google Sheets, Google Sheets integration, append rows to Google Sheets, dedupe, upsert, Google Sheets API automation, sync data to spreadsheet.