Google Sheets Import & Export: Append, No OAuth avatar

Google Sheets Import & Export: Append, No OAuth

Pricing

from $0.15 / 1,000 rows

Go to Apify Store
Google Sheets Import & Export: Append, No OAuth

Google Sheets Import & Export: Append, No OAuth

Export an Apify dataset to Google Sheets (append under existing rows or replace a tab) or import a Google Sheet to JSON. Uses your own service account, so no OAuth sign-in; public sheets import with no key. Append keeps your column order. Rows that fail are never charged.

Pricing

from $0.15 / 1,000 rows

Rating

0.0

(0)

Developer

Altronite

Altronite

Maintained by Community

Actor stats

0

Bookmarked

1

Total users

0

Monthly active users

an hour ago

Last modified

Share

Export an Apify dataset to Google Sheets, appending rows under your existing data or replacing a tab, or import a Google Sheet into a dataset. It uses the official Google Sheets API and your own Google service account, or a public sheet’s CSV export for imports without a key. No Google sign-in or OAuth consent screen: you share the sheet with your own service account's email, and you can revoke access any time by unsharing it.

Failed rows are never charged. Append keeps your header order and puts new fields in new columns on the right.

Who it's for

  • Scraper users who want results in a team sheet
  • Growing logs (append) and rebuilt report tabs (replace)
  • URL or keyword lists kept in a sheet and fed to other Actors

Export a dataset to Google Sheets, or import a sheet

Three modes cover it: export with append, export with replace, and import.

Export, append: adds rows under the existing data. Your header order is kept, and new fields become new columns on the right.

{
"mode": "export",
"writeMode": "append",
"spreadsheet": "https://docs.google.com/spreadsheets/d/1EXAMPLE-SHEET-ID-000000000000000/edit",
"sheetName": "Leads",
"datasetId": "EXAMPLEdatasetId00",
"serviceAccountJson": "<paste the whole JSON key file>"
}

Export, replace: clears the tab, then writes a fresh header and the rows. columns picks the fields and their order.

{
"mode": "export",
"writeMode": "replace",
"spreadsheet": "1EXAMPLE_SHEET_ID_0000000000000000",
"sheetName": "Daily report",
"datasetId": "EXAMPLEdatasetId00",
"columns": ["title", "price", "address.city"],
"serviceAccountJson": "<paste the whole JSON key file>"
}

Import: reads a tab into this run's dataset. The first row is the header, and each row after it becomes one item.

{
"mode": "import",
"spreadsheet": "1EXAMPLE_SHEET_ID_0000000000000000",
"sheetName": "Keywords",
"serviceAccountJson": "<paste the whole JSON key file>"
}

Import a public sheet, no key needed: if the sheet is shared as "Anyone with the link can view", leave serviceAccountJson empty and the Actor reads the tab through the sheet's CSV export. Run it with an empty input and it imports Google's public sample sheet, so you can see the output shape first. Writing (export) always needs a service account.

{
"mode": "import",
"spreadsheet": "https://docs.google.com/spreadsheets/d/1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms/edit",
"maxRows": 10
}

Send one scraper run's results to a sheet: pick that run's dataset in the Dataset field, or pass its ID as datasetId through the API.

Test the connection first: a dry run plans the export without writing rows.

{
"mode": "export",
"spreadsheet": "1EXAMPLE_SHEET_ID_0000000000000000",
"sheetName": "Leads",
"datasetId": "EXAMPLEdatasetId00",
"serviceAccountJson": "<paste the whole JSON key file>",
"dryRun": true,
"maxRows": 1
}

Use it from code or an AI agent

Run this actor with the Apify API, the Apify Python or JavaScript (JS) clients, or as a tool through the Apify MCP server. Pass the same input JSON through any of these interfaces.

Sheets import rows preserve your spreadsheet headers and do not add a status field. Check the run’s key-value store OUTPUT.status (ok, limited or error) instead. Export returns its summary in OUTPUT, not dataset rows.

Minimal input:

{
"mode": "import",
"spreadsheet": "1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms"
}

Setup: your own Google service account (one time)

A service account is a robot Google account you own. The Actor can reach only the sheets you share with it. Most Sheets failures happen here.

  1. In the Google Cloud Console, create a project or pick an existing one.
  2. Go to APIs & Services → Library, find Google Sheets API and click Enable. The Drive API isn't needed.
  3. Go to IAM & Admin → Service accounts → Create service account. It doesn't need any roles.
  4. Open the account, then go to Keys → Add key → Create new key → JSON. A .json file downloads.
  5. In that file, copy the client_email value. It ends in iam.gserviceaccount.com.
  6. Share the sheet with this email. In your spreadsheet, click Share, paste the email and choose Editor. Viewer is enough if you only import.
  7. Paste the entire JSON file into Service account key (JSON) in this Actor's input.

On Google Workspace, an admin policy may block keys or outside sharing. If so, ask your admin.

Input

FieldWhat it doesDefault
modeexport (dataset → sheet) or import (sheet → dataset)import
spreadsheetSheet URL or IDGoogle’s public sample sheet
sheetNameTab to use. Created on export if it's missingSheet1
serviceAccountJsonFull JSON key (secret), required for export or private importsempty (public import)
datasetIdDataset to exportrequired for export
writeModeappend, or replace (clears the tab first)append
columnsExport only these fields, in this order (address.city for nested fields)all
maxRowsStop after this many rows (0 = all)0
skipEmptyRowsImport: skip blank rowstrue
dryRunPlan only: no rows written, no row chargesfalse

Nested objects become dotted columns, lists become JSON text, and empties become blank cells. The OUTPUT record summarizes each run: status (ok, limited on import or export spending limits, or error), row counts, spreadsheetUrl and, on export, columns. Import rowsImported counts rows actually delivered, including when the SDK spending limit stops a batch.

Pricing

This actor uses pay-per-event pricing: every run has a small start fee (apify-actor-start), charged once per run (per GB above 1 GB), plus a per-row charge (row) for rows written to the sheet (export) or added to the dataset (import). The rates are on the actor's Pricing tab. Dry runs incur no row charges; the start fee still applies. Failed rows are never charged. If a run stops part-way, row charges apply only to the rows that landed. If you set a maximum total charge on the run, export stops cleanly at that limit and OUTPUT says how many rows were written.

Reliability

  1. Input is checked before Google is contacted. That covers the sheet URL or ID, whether the key is a service-account JSON, and whether an export has a datasetId. Errors name the field and never repeat your input.
  2. A key that can't be read stops the run with "The service-account key could not be read. Paste the whole JSON key file you downloaded."
  3. A wrong ID or missing Editor access stops the run with a message that includes your service account's email and tells you to share the sheet with it as an Editor. If the Google Sheets API isn't enabled for the key's project, or the key was revoked, the message says that instead. No raw error traces.
  4. Rate limits and Google server errors (429, 5xx) are retried with exponential backoff, one 1,000-row chunk at a time, so earlier chunks aren't resent. Before a retry, the sheet is checked: if the failed write had in fact landed, it isn't written again.
  5. If an export fails part-way, OUTPUT records the reason and the count, for example "1000 of 2500 rows were written before this". There's no automatic resume: running again in append mode starts from the first row, so delete the partial rows first or use replace.
  6. A dataset that can't be opened (wrong or expired ID) stops the run with a message saying so, instead of a stack trace. Pick the dataset with the Dataset field's picker, or pass its ID through the API.
  7. A tab name that doesn't exist on import stops the run and lists the sheet's tabs. Nothing is imported or charged. Left at the default Sheet1, the first tab is used.
  8. Close to the run's timeout the export stops starting new chunks and ends with status: limited and a count of rows written, instead of timing out.
  9. A dry run opens the sheet and reports the planned columns and row count without writing rows. On export it still creates a missing tab. It doesn't prove Editor access, because reading needs only Viewer.

For a quick connection test, set dryRun: true and maxRows: 1.

Use cases

  • Lead-gen team: A marketing agency scrapes business listings nightly and appends each run's dataset to a "Leads" tab so the sales team can review new rows without re-downloading a file.
  • Daily price reports: An e-commerce scraper produces a dataset per run, and a scheduled task replaces a "Daily report" tab each morning with only title, price, and address.city in a fixed column order.
  • Shared keyword lists: A content team keeps target keywords in a sheet and imports that tab into a dataset to feed the URLs into another actor that does the actual scraping.
  • Public reference data: A developer pulls a sheet that's shared as "Anyone with the link can view" into a dataset without setting up a service account, using maxRows to test the output shape first.
  • Growing log file: A long-running scraper appends each batch of results to the same tab so past entries are kept, while new fields from later runs appear as extra columns on the right.
  • Connection testing: Before wiring the actor into a schedule, an operator runs it with dryRun: true and maxRows: 1 to confirm the sheet URL and key are correct without writing rows.

FAQ

How do I export an Apify dataset to Google Sheets? Set mode to export, give the sheet URL or ID, a sheetName, the datasetId and your service account key, share the sheet with the service account's email as Editor, then choose append or replace.

Can I import a Google Sheet without a Google account key? Yes, if the sheet is shared as "Anyone with the link can view". Leave serviceAccountJson empty and the actor reads the tab through its CSV export. Private sheets need the service account.

Why do I need a service account? Only to write, or to read a private sheet. Google needs an identity to write. A service account means no password and no broad OAuth grant. It sees only the sheets you share, and you can revoke access by unsharing or deleting the key.

Is my key stored? It's a secret input, so Apify stores it encrypted with the run input. The Actor never logs the key, never writes it to the dataset, OUTPUT or any other storage, and never echoes input in errors. The log shows the service account's email, not the key. Data goes from Apify straight to Google, and the publisher keeps no copy.

Will it reorder my columns? Not when you append with columns left empty: existing columns stay where they are, and new fields go on the right. If you set columns, that list becomes the header exactly, so on a tab that already has data, match its order or use replace. Keep the header row free of blank cells.

Can I schedule it? Yes, with Apify Schedules or tasks. For export, a fixed datasetId with append re-adds the same rows on every run, so use replace, or pass each new scraper run's dataset ID.

What does import return? Every value comes back as text, as the sheet displays it. Blank headers become column_2, column_3 and so on. Repeated headers are kept apart as name, name_2, name_3, so no column is lost.

Are formulas evaluated on export? No, they're written as plain text.

Limits

  • One dataset and one tab per run. There's no upsert or deduplication.
  • Google caps a spreadsheet at 10 million cells and a cell at 50,000 characters. Longer values are cut and marked …[truncated].
  • Google applies per-minute request quotas to each Cloud project. Export makes roughly one write request per 1,000 rows and backs off on 429.
  • Rows are held in memory during a run (2 GB max).
  • Replace with an empty dataset leaves the tab untouched.

Support

Open an issue on the Issues tab with your input (remove the key first) and the error message.