Google Sheets Import & Export - No OAuth, API & AI Agents
Pricing
from $2.00 / 1,000 sheet synceds
Google Sheets Import & Export - No OAuth, API & AI Agents
Append, replace, upsert or read Google Sheets with a service account: no OAuth popup, works from the API, schedules and AI agents (MCP).
Pricing
from $2.00 / 1,000 sheet synceds
Rating
0.0
(0)
Developer
TRUONG VAN HOA
Maintained by CommunityActor stats
0
Bookmarked
1
Total users
0
Monthly active users
4 hours ago
Last modified
Categories
Share
Google Sheets Import & Export — no OAuth, works with the API & AI agents
Sync any data into Google Sheets, or export a tab out of it, using a Google service account — no interactive OAuth login, no popup that can fail or expire. That makes it work reliably from the Apify API, from Scheduled runs, from Integrations, and from AI agents through the Apify MCP server, none of which can click through a browser consent screen.
- append — add rows below the existing data, mapping incoming keys onto existing columns by name.
- replace — clear the tab and rewrite it (with an automatic backup of the old values).
- upsert — update rows that match a key, insert the rest.
- read — export a tab to the dataset as clean JSON.
Why a service account instead of "Login with Google"?
An OAuth login button only works when a human is present in a browser to click "Allow". It cannot run unattended on a schedule, cannot be called from the Apify API, and stops working the moment a token expires or a permission screen changes. A service account is a robot Google identity that you create once, in your own Google Cloud project: it authenticates with a signed key that never expires and never shows a popup, so this Actor works identically whether you start it by hand, on a schedule, through an Integration, or from an AI agent.
Setup: create a service account (5 minutes, one-time)
- Create a Google Cloud project — go to console.cloud.google.com, click the project dropdown (top bar) → New Project → give it a name → Create.
- Enable the Google Sheets API — with your project selected, go to APIs & Services → Library, search for "Google Sheets API", open it, click Enable.
- Create a service account — go to APIs & Services → Credentials → Create Credentials → Service account, give it a name (e.g. "sheets-sync"), click Create and Continue, then Done (no roles needed).
- Create a JSON key — open the service account you just created → tab Keys → Add Key → Create new key → type JSON → Create. A
.jsonfile downloads — open it and copy the whole content. - Share the spreadsheet — open your Google Sheet → Share → paste the service account's
client_email(looks likesheets-sync@your-project.iam.gserviceaccount.com, also visible inside the JSON key) → give it Editor access → Share.
Paste the full JSON file content into the Service account key input. That's it — no further authorization step, ever, for this key.
Modes
| Mode | Before | After |
|---|---|---|
append | Tab has 100 rows, columns id, name, email | Your new rows are added below; a new key like phone becomes a new column on the right, existing rows get a blank phone cell |
replace | Tab has last week's export | Tab is cleared and rewritten with this run's data; last week's values are saved as JSON to the key-value store record BACKUP |
upsert (keyColumns: ["id"]) | Row with id=42 exists | That row's columns are updated in place (columns not present in the incoming item are left untouched); rows with unmatched ids are appended |
read | Tab has data | Every row becomes one dataset item, keyed by the header row |
Input
| Field | Example | Notes |
|---|---|---|
serviceAccountKey | (paste JSON) | Secret input, encrypted by Apify, never logged |
spreadsheet | "https://docs.google.com/spreadsheets/d/1AbC.../edit#gid=0" or a bare ID | Any ID length; a URL's gid selects a tab unless sheetName is set |
sheetName | "Leads" | Default: first tab (or the URL's gid tab). Created automatically for write modes if missing |
mode | "append" | "replace" | "upsert" | "read" | Default append |
keyColumns | ["id"] | Required for upsert |
backupBeforeReplace | true | replace only, default true |
datasetId | "{{resource.defaultDatasetId}}" | One of datasetId/rawData, write modes only |
rawData | [{"id":1,"name":"Ann"}] | Array of objects, or array of arrays with a header row first |
offset, limit | 0, 5000 | datasetId paging |
fields | ["id", "address.city"] | Keep only these columns, dotted paths reach into nested objects |
omitFields | ["password"] | Drop these columns |
flatten | true | Nested objects → a.b.c columns; arrays of primitives joined with , ; arrays of objects → JSON string |
deduplicateBy | ["email"] | Drop incoming duplicates before writing, keep the last |
valueInputOption | "RAW" | "USER_ENTERED" | USER_ENTERED lets Sheets parse dates/formulas like typing |
allowFormulas | false | With USER_ENTERED, values starting =, +, -, @ are escaped unless this is true (formula-injection guard) |
readRange | "Sheet1!A1:F200" | read only |
maxItems | 5000 | read only |
dryRun | false | Validate + compute the plan without touching the sheet; not charged |
Example: append a scraper's output
Chain after any Actor via Integrations, setting datasetId to {{resource.defaultDatasetId}}:
{"serviceAccountKey": "{{secrets.GOOGLE_SA_KEY}}","spreadsheet": "1AbCDefGhIjKlmNoPQRstuVWxyz0123456789ABCDEfg","mode": "append","datasetId": "{{resource.defaultDatasetId}}"}
Example: upsert from an API call
curl "https://api.apify.com/v2/acts/YOUR_USERNAME~google-sheets-sync/run-sync-get-dataset-items?token=$APIFY_TOKEN" \-H "Content-Type: application/json" \-d '{"serviceAccountKey": "...","spreadsheet": "1AbCDefGhIjKlmNoPQRstuVWxyz0123456789ABCDEfg","mode": "upsert","keyColumns": ["order_id"],"rawData": [{"order_id": "A1002", "status": "shipped"}]}'
Example: from an AI agent (Apify MCP)
An agent that already has data in hand (no dataset to point to) can pass it directly as rawData:
{ "spreadsheet": "1AbC...", "mode": "append", "rawData": [{"task": "Follow up", "done": false}] }
Output
OUTPUT (key-value store record):
{"mode": "upsert","spreadsheetId": "1AbCDefGhIjKlmNoPQRstuVWxyz0123456789ABCDEfg","spreadsheetUrl": "https://docs.google.com/spreadsheets/d/1AbCDefGhIjKlmNoPQRstuVWxyz0123456789ABCDEfg","sheetName": "Leads","rowsRead": 0,"rowsAppended": 3,"rowsUpdated": 12,"rowsSkippedDuplicate": 1,"newColumns": ["phone"],"totalCellsAfter": 4200,"warnings": [],"dryRun": false}
For mode: "read", the exported rows are pushed to the dataset (one row = one item). For write
modes, one summary item matching OUTPUT is also pushed to the dataset, so calling
run-sync-get-dataset-items returns the result directly.
Pricing
Pay per event, charged only once the sheet write/read actually succeeds — a failed run costs nothing:
| Event | Price | When |
|---|---|---|
sheet-sync | $0.002 | Once per successful run |
rows-1000 | $0.005 | Per started block of 1,000 rows written or read |
Examples: a daily sync of 500 rows costs $0.007 per run, about $0.21 a month. An hourly sync of 500 rows is about $5 a month. A one-off import of 50,000 rows costs $0.252.
If a run would exceed Max total charge, it writes only the rows it can afford and says so in
warnings — it never leaves your spreadsheet half-written past the point your budget allows further
charges.
FAQ
Is my key safe? serviceAccountKey is an Apify secret input: encrypted at rest, never shown in
logs, and used only in memory for the duration of the run. Nothing is stored by this Actor. Revoke
the key any time from Google Cloud Console → your service account → Keys, with zero code changes
needed here.
What stops someone injecting a formula into my sheet through the data? With the default
valueInputOption: "RAW", Sheets never interprets cell content as a formula. If you opt into
USER_ENTERED (to get automatic date/number parsing), any incoming string starting with =, +,
- or @ is automatically prefixed with an apostrophe so it is stored as text, not evaluated —
unless you explicitly set allowFormulas: true.
What are the limits? Google Sheets caps a spreadsheet at 10,000,000 cells total (across every
tab) and a cell at 50,000 characters; this Actor checks the cell limit before writing and fails
with the exact row count that would fit, and truncates any single oversized cell with a warning
rather than a silent data-loss error. Sheets' write quota is 60 requests/minute/user; this Actor
paces itself under that automatically and retries 429/5xx responses with backoff.
Can I use any spreadsheet ID length? Yes — this Actor accepts an ID of any length, or a full
Sheets URL (the ID and gid are parsed out for you).
Is this affiliated with Google? No. This is an independent tool that uses the official public Google Sheets API with your own service-account credentials in your own Google Cloud project.
Something wrong or missing? Open an issue on the Actor's Issues tab.