Google Sheets Sync: Import, Export & Upsert Datasets avatar

Google Sheets Sync: Import, Export & Upsert Datasets

Under maintenance

Pricing

from $0.08 / 1,000 row writtens

Go to Apify Store
Google Sheets Sync: Import, Export & Upsert Datasets

Google Sheets Sync: Import, Export & Upsert Datasets

Under maintenance

Export any Apify dataset or actor run to Google Sheets, or import a sheet into a dataset: append, replace, upsert by key or mirror sync, retried through Google's rate limits. Runs after any actor as an integration. Share the sheet once; no Google sign-in. $0.10 per 1,000 rows.

Pricing

from $0.08 / 1,000 row writtens

Rating

0.0

(0)

Developer

CleanPull

CleanPull

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

5 hours ago

Last modified

Share

Send any Apify dataset to Google Sheets, or read a sheet into a dataset, in every way you need it: append, replace, upsert by key (update rows in place, add new ones), or mirror sync (also remove rows that are gone). Runs wait out Google's rate limits instead of failing, read datasets of any size page by page, and write 30,000 rows in under 10 seconds. Share the spreadsheet once with our address; no Google sign-in, nothing expires. $0.10 per 1,000 rows.

What you can do with it

  • "Put every run's results in my sheet." Run it after any actor through Apify integrations; the finished run's dataset is written automatically.
  • "Keep one row per product and update its price." Upsert by sku: changed cells are updated in place, new products added, nothing duplicated. Columns you added by hand are left alone.
  • "Mirror this dataset exactly." Sync adds, updates, and deletes, moves or marks rows that are gone.
  • "A new tab every day," or "one tab per country."
  • "Use my sheet as the input of another actor." Read mode turns a tab into a dataset with typed numbers, booleans and dates.
  • "Only send rows I haven't sent before."

Set up in 30 seconds

  1. Open your spreadsheet → Share → add cleanpull-sheets-1@cleanpull-sheets.iam.gserviceaccount.com as Editor (untick "Notify people").
  2. Paste the spreadsheet's URL into Spreadsheet and choose what to write.

The first run links the spreadsheet to your Apify account with a small hidden tab (_cleanpull, holding only your Apify user id). Runs from other Apify accounts are then refused, so your spreadsheet's URL alone is never enough to write to it.

For heavy use, also share with cleanpull-sheets-2@… and cleanpull-sheets-3@… (same domain) to triple the throughput. Prefer your own credentials? Paste your own service account key, or OAuth client and refresh token, in the optional fields; nothing is then shared with us.

Run it after any actor

In the actor or task whose results you want in Sheets: Integrations → Connect Actor or Task → Google Sheets Sync, with this input:

{
"spreadsheet": "https://docs.google.com/spreadsheets/d/…/edit",
"datasetId": "{{resource.defaultDatasetId}}",
"mode": "upsert",
"keyFields": ["url"]
}

Every time that actor finishes, its results land in your sheet.

Modes

ModeWhat it does
appendAdds rows below the existing data; new fields become new columns at the end
replaceClears the tab and writes the data
upsertMatches rows by keyFields; updates only the cells that changed, adds new keys
syncUpsert, and rows whose key is gone are deleted, moved to a removed tab, or marked with removedAt
readThe tab's rows into the dataset (and the OUTPUT record); header row → field names, values typed
modifyCleans up the tab's own rows (dedupe, columns) in place; backs the tab up first, never clears a tab it can't fully rewrite
backup / restoreSaves a tab's values and formulas to the actor's store; restores them by key
clearEmpties the tab

Bring your existing inputs

Inputs, tasks and integrations you already use for Sheets imports keep working: paste the same input and run.

  • Keys it understands:
    • spreadsheetId, datasetId, rawData (objects, or a table with the header row first), limit, offset;
    • deduplicateByField, deduplicateByEquality, columnsOrder, range (its tab is used), createBackup, publicSpreadsheet;
    • the modes append, replace, modify, read and load backup (restores the latest backup of the tab).
  • Your sheets keep their layout: with those inputs, nested fields keep hobbies/0 style column names, and read also saves the rows as the OUTPUT record.
  • No Google sign-in: share the spreadsheet with this actor's address as Editor, once. There are no tokens to expire and nothing to reconnect.
  • Good to know:
    • transformFunction is not run, because custom code is not executed. Use dedupe, columns, columnsOrder and limit instead.
    • Dedupe removes duplicates among the rows being sent. To skip rows already in the sheet, use upsert with keyFields.
    • Rows always start at the tab's first column and row.
    • columnsOrder applies when the header is created; an existing header keeps its order.

Input

FieldWhat it does
spreadsheetThe sheet URL or id; a URL with #gid= targets that tab
tabTab name, created if missing; {date}, {datetime}, {runId}, {value} placeholders
mode, keyFields, onRemovedSee Modes
datasetId, runId, actorId, taskId, url, rawData, keyValueRecordThe data: a dataset, a run, an actor's or task's last successful run, a JSON or CSV URL, rows in the input, or a stored record
targetsSeveral spreadsheet / tab / mode sets in one run
splitByFieldOne tab per value of a field
incremental, dedupe, limit, offsetOnly rows not sent before; remove duplicates; how many
columnsChoose, order and rename columns (price: Price (USD))
flatten, arrays, arraySeparatorNested fields as columns; lists joined or kept as JSON
addSyncedAtA timestamp column with the run's time
valueInput, formulaColumnsStore values as given, or let Google type them (with formula columns for =IMAGE() and =HYPERLINK())
freezeHeader, boldHeader, autoResizeFormatting, on by default
createBackup, backupKeyBack up before replace or sync; restore a backup
headerRow, publicSheetRead: the header row; read a published sheet without sharing
serviceAccountJson, oauthClientId, oauthClientSecret, oauthRefreshTokenOptional: your own Google credentials

Output

One record per spreadsheet target:

{
"recordType": "target",
"url": "https://docs.google.com/spreadsheets/d/…/edit#gid=123",
"tab": "Products",
"mode": "upsert",
"appended": 200,
"updated": 250,
"removed": 0,
"unchanged": 750,
"columns": 7,
"warnings": []
}

In read mode, the dataset holds the sheet's rows. RUN_SUMMARY in the key-value store gives the outcome (COMPLETE, PARTIAL, EMPTY, FAILED), rows written and read, requests, retries after rate limits, and charges.

Reliability

Measured on a live spreadsheet:

RunResult
Append 30,000 rows × 8 columns8.7 s, 12 requests
Upsert 1,200 rows (250 changed, 200 new)5.6 s; 750 unchanged rows untouched and free
Sync with mark (29,000 rows gone)10.2 s
Read 30,200 rows back1.0 s
90 writes at once against Google's per-minute limitAll 90 succeed: the run waits and retries

How:

  • Few large writes (about 1.5 MB each, under Google's recommended 2 MB), paced under Google's per-minute limit.
  • Retries with exponential backoff that honour Google's Retry-After. A write that is not safe to repeat is never retried blind: after a server error, an append is checked against the sheet before anything is sent again, so rows are never doubled, and a row deletion stops the sync (which is safe to run again).
  • Datasets read in pages, so any size works.
  • Progress kept per tab across platform restarts, so a resumed run continues where it stopped.
  • Access checked before writing, with the exact address to share with.
  • Scraped text that looks like a formula stays text.
  • Cells over Google's 50,000-character limit are cut and counted.

Pricing

EventFree and BronzeSilverGold
Row written (added, updated or removed)$0.10$0.09$0.08
Row read (read mode)$0.05$0.045$0.04

Per 1,000 rows. Unchanged rows in upsert and sync are free; modify rewrites, and is charged for, every row it keeps. Each run also has a $0.00005 start fee. A daily sync of 2,000 rows costs about $0.20 a month.

Limits

  • Google's cell limit counts the whole spreadsheet. When it is reached, the run fills the sheet to the limit, stops cleanly and says so. Write the rest to a new spreadsheet (another target), or use replace or sync so the sheet keeps its size.
  • Our service accounts cannot create new spreadsheets (Google gives service accounts no storage): create the sheet, then share it.
  • Upsert and sync read the tab once to compare, so very large tabs (over about 500,000 rows) take longer.
  • Formatting is limited to the header row and column widths; number formats come from Google when values are typed.
  • "Only rows not sent before" works with append (upsert already skips unchanged rows, for free). Sync takes the whole dataset: no limit or skip, since rows left out would be removed.
  • Rows without a key value are skipped in upsert and sync (they cannot be matched); rows in the sheet without a key are left as they are.
  • The spending limit is respected before each write: a run that reaches it writes what fits, is charged for exactly that, and stops.
  • Try it without a spreadsheet (preview): a run with no spreadsheet writes nothing and charges nothing: the dataset shows the rows it would write (up to 100, marked preview: true), from your data or, with nothing filled in, four example rows. Add your spreadsheet's URL to write them. Your data is only ever written to the spreadsheet you name.
  • Nothing found: when the data or the sheet is not there (a missing run or record, a sheet not shared with us, a missing tab or backup), the run still succeeds and says why: a free recordType: "unreadable" row for a missing source, or the target row's error and errorKind; nothing is charged. A run near its time limit stops starting targets and ends with what it wrote.

Changelog

  • 0.1.x, reliability (2026-10-24): no spreadsheet means a free preview of the rows a run would write (the empty-input example is now previewed too, no longer written to the demo spreadsheet); a missing source, an unshared sheet or a missing tab ends SUCCEEDED with the reason instead of FAILED; runs stop starting targets before their time limit. Prices unchanged.
  • 0.1.7 (October 2026): existing Sheets inputs work unchanged (spreadsheetId and related keys, more modes, table-format raw data, hobbies/0 columns, OUTPUT on read); new modify mode, columnsOrder and flattenSeparator.
  • 0.1.6 (October 2026): an empty input runs an example on the demo spreadsheet instead of failing.
  • 0.1 (October 2026): append, replace, upsert, sync, read, backup, restore and clear; datasets of any size; integration payloads; several targets; tabs per field value and per date; only-new rows; pacing, retries and resume; service-account sharing with per-user linking; your own service account or OAuth.