# Google Sheets Import & Export: Dataset to Sheet, Sheet to JSON (`swillz/google-sheets-import-export`) Actor

Write any Apify dataset or JSON rows to Google Sheets (append or replace, header merge, dedup), or read a sheet tab or range as JSON rows. No OAuth: share the sheet with our service-account email once. Built for scraper → sheet pipelines and AI agents. $0.01 per 1,000 rows.

- **URL**: https://apify.com/swillz/google-sheets-import-export.md
- **Developed by:** [William Jin](https://apify.com/swillz) (community)
- **Categories:** Integrations, Developer tools, Automation
- **Stats:** 2 total users, 1 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.01 / 1,000 row writtens

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

**Google Sheets Import & Export** moves data between **Apify datasets and Google Sheets** in both directions. **Export any scraper's results to a Google Sheet** (append new rows or replace everything, with header merging and **deduplication**), or **import a Google Sheet tab or range into a dataset** as JSON, CSV or Excel. There's no OAuth pop-up and no Google login: you share your sheet with our service account email once, and it just works on schedules and integrations.

Try it free: Apify's free plan ($5/month credit) covers **hundreds of thousands of rows a month**.

**When to use:** an AI agent or pipeline needs to put rows into a Google Sheet, or read a sheet as JSON. No OAuth.
**Input:** a sheet URL plus a dataset ID or JSON rows, e.g. `{"spreadsheet":"https://docs.google.com/spreadsheets/d/<ID>/edit","rawData":[{"name":"A"}]}`.
**Output:** rows written to the sheet (summary in OUTPUT), or one JSON row per sheet row when `operation` is `"read"`. $0.01 per 1,000 rows.

### What can Google Sheets Import & Export do?

- 📤 **Export a dataset to Google Sheets.** Send the output of any actor run (Google Maps, Amazon, jobs, sitemaps…) to a sheet. Nested fields are flattened (`address.city`) and arrays are joined, stored as JSON or expanded.
- ➕ **Append or replace.** *Append* adds rows under your existing data and adds any new columns to the header. *Replace* overwrites the tab, and the old rows are cleared only **after** the new data is written, so a failed run never leaves you with an empty sheet.
- 🧹 **Deduplicate.** Skip rows whose `url`, `id` or any other columns already exist in the sheet. That makes daily scheduled exports safe to re-run, and skipped rows are free.
- 📥 **Import a sheet into a dataset.** Read a whole tab or an A1 range (`A1:F500`, `B:D`) with formatted or raw values, a header row or column letters, and optional dedup. Use it to feed URLs, keywords or product IDs into other actors.
- 🔐 **Sheet lock.** A spreadsheet shared with our service account can only be used by **your** Apify account. On first use the actor tags the sheet with your account ID (hidden metadata only our Google project can see), so nobody else can read or write it through this actor, even with the URL.
- 🛡️ **Reliable by design.** Automatic retries with backoff on Google rate limits (429) and 5xx errors, grid auto-resize, 10M-cell and 50k-characters-per-cell checks, and clear error messages that tell you exactly what to fix.

### How to export an Apify dataset to Google Sheets (step by step)

1. Open your Google Sheet and click **Share**. Paste **`sheets-tool@apify-tools-512508.iam.gserviceaccount.com`**, choose **Editor**, untick *Notify people*, and click **Share**.
2. Copy the sheet's URL from the address bar.
3. In this actor, choose **Operation: Write**, paste the URL, and paste the **Dataset ID** (open any run → **Storage** → *Dataset ID*).
4. Pick **Append** or **Replace**, and optionally add *Deduplicate by* columns such as `url`.
5. Click **Start**. The rows appear in your sheet within seconds, and the run's **OUTPUT** tells you how many rows were written and how many duplicates were skipped.

**To import a sheet:** do step 1 (Editor is recommended, Viewer also works for reading), choose **Operation: Read**, paste the URL, and optionally set a tab and a range. Then download the dataset as JSON, CSV or Excel, or use it via the API.

**Prefer your own Google credentials?** Create a service account in your Google Cloud project, enable the Sheets and Drive APIs, and paste its JSON key into *Your own service account key* (a secret, encrypted input). Then share the sheet with **your** service account's email instead.

### How much does it cost?

This actor uses **pay-per-event pricing**: **$0.01 per 1,000 rows** read or written, plus $0.00005 per run start. Duplicates that are skipped and failed runs cost nothing for rows. There are no monthly fees.

| Rows per run | Price |
|---|---|
| 1,000 | $0.01 |
| 100,000 | $1.00 |
| 1,000,000 | $10.00 |

A daily export of 2,000 new rows costs about **$0.60 a month**. You can set a maximum cost per run, and the actor stops cleanly when it reaches it.

### Input example

Export a dataset, appending only rows with new URLs:

```json
{
  "operation": "write",
  "spreadsheet": "https://docs.google.com/spreadsheets/d/1AbC.../edit#gid=0",
  "sheetName": "Leads",
  "datasetId": "WkzbQMuFYuamGv3YF",
  "writeMode": "append",
  "deduplicateBy": ["url"]
}
```

Import a range:

```json
{
  "operation": "read",
  "spreadsheet": "https://docs.google.com/spreadsheets/d/1AbC.../edit",
  "sheetName": "Keywords",
  "range": "A1:C500"
}
```

### Output example

Read operation, one dataset item per row (keys come from your header row):

```json
{ "sku": "A-001", "name": "Blue mug", "price": "12.5", "inStock": "TRUE" }
```

Every run also stores a summary in the key-value store record `OUTPUT`:

```json
{
  "operation": "write",
  "spreadsheetUrl": "https://docs.google.com/spreadsheets/d/1AbC.../edit",
  "sheetName": "Leads",
  "writeMode": "append",
  "inputRows": 250,
  "rowsWritten": 212,
  "duplicatesSkipped": 38,
  "columns": ["title", "url", "price", "address.city"]
}
```

### Use cases

- **Live lead lists:** run a Google Maps or contact scraper daily and append only new businesses to your team's sheet.
- **Price and stock tracking:** append each day's prices to a log tab, or replace a "current prices" tab.
- **Inputs from a sheet:** keep keywords, URLs or ASINs in a sheet that non-technical teammates edit, and read them at the start of a workflow.
- **Reporting:** keep Looker Studio dashboards and pivot tables fed from scraped data.
- **Backups:** copy a sheet into a dataset on a schedule (versioned JSON/CSV exports).

### Use with AI agents (MCP, ChatGPT, Claude, Cursor, n8n)

AI agents can find and run this Actor through the [Apify MCP server](https://mcp.apify.com). Add it to Claude Desktop, Cursor, VS Code or any MCP client:

```json
{"mcpServers":{"apify":{"url":"https://mcp.apify.com?tools=swillz/google-sheets-import-export"}}}
```

Or run it locally: `npx -y @apify/actors-mcp-server --tools swillz/google-sheets-import-export` with `APIFY_TOKEN` set. In n8n or Make, use the Apify node with Actor `swillz/google-sheets-import-export`.

Example prompts:

- "Run the Workday scraper for NVIDIA and put the jobs in my Google Sheet"
- "Read my leads sheet and enrich each domain"
- "Append these 50 rows to the "Leads" tab and skip duplicate emails"

Minimal input:

```json
{"spreadsheet": "https://docs.google.com/spreadsheets/d/<ID>/edit", "datasetId": "<datasetId from the previous run>"}
```

**Keep it fast:** `operation` defaults to `"write"`. The sheet must be shared with `sheets-tool@apify-tools-512508.iam.gserviceaccount.com` first: if you get a permission error, ask the user to share the sheet with that email, then run again.

**Cap the cost:** pass `maxTotalChargeUsd` ("Maximum cost per run") when you call the Actor. The run stops cleanly at the cap, keeps the results so far, and says why in the status message and the OUTPUT record.

### Integrations & API

- **After another actor finishes:** in that actor's **Integrations** tab, add *Run an actor* → this actor, with input `{"operation":"write","spreadsheet":"…","datasetId":"{{resource.defaultDatasetId}}","writeMode":"append","deduplicateBy":["url"]}`. Every run then lands in your sheet automatically.
- **Schedules:** run imports and exports hourly, daily or weekly from Apify Schedules.
- **Make, n8n and Zapier:** use the official Apify app or node, pick *Run actor* → `swillz/google-sheets-import-export`.
- **API and clients:** start runs from the Apify API or the Python and JavaScript clients, and read the `OUTPUT` record for the result.
- **AI agents (MCP):** `https://mcp.apify.com?tools=swillz/google-sheets-import-export`.

### Google Sheets Import & Export vs alternatives

| | This actor | Typical Sheets actors |
|---|---|---|
| Authorization | Share with one email, works on schedules | OAuth token that expires or needs a first interactive run |
| Append + new columns | Header auto-merged | Often fails or misaligns columns |
| Deduplication | Against existing sheet rows | Within the run only, or none |
| Replace safety | Old data cleared after the new data is written | Clear first (empty sheet on failure) |
| Access control | Sheet lock to your Apify account | – |
| Price | $0.01 per 1,000 rows | Free to $5 per 1,000 |

### FAQ

**Is my data safe?** The actor only opens spreadsheets you explicitly share with the service account, and the sheet lock stops other Apify accounts from using a sheet you shared. You can revoke access at any time by removing the email in Google Sheets → Share. Want full control? Use your own service account key.

**Why does it say "Can't open spreadsheet"?** The sheet isn't shared with `sheets-tool@apify-tools-512508.iam.gserviceaccount.com`, or it's shared as Viewer and you're writing. Share it as **Editor**, and double-check the URL.

**"Locked to another Apify account"?** Someone else's Apify account used this sheet through the actor first. Make a copy of the sheet, or use your own service account key.

**Limits?** Google Sheets allows 10 million cells per spreadsheet and 50,000 characters per cell. The actor checks both before writing and truncates over-long cells with a warning. Large exports are written in chunks with automatic retries.

**Formulas?** By default values are written as **RAW** text, so scraped text starting with `=` can't run as a formula. Choose `USER_ENTERED` if you want Google to parse formulas, dates and numbers.

**Need a feature or found a bug?** Open an issue on the **Issues** tab. We usually reply within a day.

### Related tools

More tools from the same developer, built to work together:

- [Career Site Jobs Scraper](https://apify.com/swillz/career-site-jobs-scraper): jobs from Greenhouse, Lever, Ashby, Workable and Recruitee boards, with salary fields, as a jobs API or CSV.
- [Workday Jobs Scraper](https://apify.com/swillz/workday-jobs-scraper): every job from any myworkdayjobs.com career site, past the 2,000-result cap.
- [Company Website Enrichment](https://apify.com/swillz/company-website-enrichment): turn a list of domains into company name, emails, phones, social links and tech stack.
- [XML Sitemap URL Extractor & Sitemap to CSV](https://apify.com/swillz/sitemap-url-extractor): every URL from a website's sitemap, with lastmod, HTTP status and new-page monitoring.

**Popular workflows**

- **Hiring-signal leads:** scrape open roles with [Career Site Jobs Scraper](https://apify.com/swillz/career-site-jobs-scraper), enrich each hiring company's domain with [Company Website Enrichment](https://apify.com/swillz/company-website-enrichment), and append new rows daily with this Actor.
- **Enterprise hiring tracker:** pull Workday jobs with [Workday Jobs Scraper](https://apify.com/swillz/workday-jobs-scraper), add company contacts with [Company Website Enrichment](https://apify.com/swillz/company-website-enrichment), and keep a live sheet with this Actor.

Source code: https://github.com/williamjin02/apify-actors/tree/main/actors/google-sheets-import-export

# Actor input Schema

## `operation` (type: `string`):

"write" (default): export an Apify dataset or JSON rows to the sheet. "read": import a sheet tab or range into this run's dataset as JSON rows. With no datasetId or rawData, the run reads the sheet instead.

## `spreadsheet` (type: `string`):

Required. Full Google Sheets URL, e.g. "https://docs.google.com/spreadsheets/d/SPREADSHEET_ID/edit". The sheet must first be shared (Editor for write, Viewer for read) with sheets-tool@apify-tools-512508.iam.gserviceaccount.com (Share button, paste the email, untick "Notify people"). AI agents: ask the user to share it if you get a permission error. Not needed with your own service account key. The prefilled URL is Google's public sample sheet, so a first run with no other input reads it into a dataset; replace it with your own sheet.

## `sheetName` (type: `string`):

Tab to read or write. Leave empty for the tab in the URL (gid) or the first tab. When writing, a missing tab is created for you.

## `datasetId` (type: `string`):

String: the Apify dataset ID to export, e.g. "aBcD1234efGH5678" from a run's Storage tab. In an integration (run this after another actor finishes) use {{resource.defaultDatasetId}}. Leave empty when you use rawData.

## `writeMode` (type: `string`):

"append" (default): add rows below the existing data; new columns are added to the header. "replace": overwrite the tab with the new data; old rows are cleared only after the new data is written.

## `deduplicateBy` (type: `array`):

Array of column names, e.g. \["url"] or \["id"]. Skips rows whose values in these columns already exist (in the sheet when appending, and within the data). Skipped rows are free. Works for read too.

## `columns` (type: `array`):

Array of column names to keep, in this order, e.g. \["name","price","address.city"]. Nested fields use dots. Leave empty for all fields.

## `arrayHandling` (type: `string`):

How to put arrays into cells: join simple lists with commas, store as JSON, or expand into numbered columns (tags/0, tags/1…).

## `valueInputOption` (type: `string`):

"RAW" (default) stores text exactly as-is (safe: values starting with = are not run as formulas). "USER_ENTERED" lets Google parse numbers, dates and formulas as if typed.

## `rawData` (type: `array`):

Optional array of objects to write instead of a dataset, e.g. \[{"name":"A","price":1},{"name":"B","price":2}].

## `range` (type: `string`):

Optional A1 range within the tab, e.g. "A1:F500" or "B:D". Leave empty for the whole tab.

## `valueRender` (type: `string`):

Read only. "FORMATTED_VALUE" (default): what you see in the sheet, e.g. "$1.50". "UNFORMATTED_VALUE": raw numbers and booleans.

## `skipEmptyRows` (type: `boolean`):

Read only: ignore fully empty rows (default true).

## `headerRow` (type: `boolean`):

Read: use row 1 as field names. Write: write/merge column names into row 1.

## `maxRows` (type: `integer`):

Stop after this many rows (0 = no limit).

## `serviceAccountKey` (type: `string`):

Advanced: paste the full JSON key of your own Google Cloud service account (with Sheets and Drive APIs enabled). Then share your sheet with that account's email instead of ours. Stored encrypted as a secret input.

## Actor input object example

```json
{
  "operation": "write",
  "spreadsheet": "https://docs.google.com/spreadsheets/d/1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms/edit",
  "writeMode": "append",
  "arrayHandling": "join",
  "valueInputOption": "RAW",
  "valueRender": "FORMATTED_VALUE",
  "skipEmptyRows": true,
  "headerRow": true,
  "maxRows": 0
}
```

# Actor output Schema

## `summary` (type: `string`):

JSON. Write example: {"operation":"write","spreadsheetUrl":"https://docs.google.com/spreadsheets/d/…","sheetName":"Sheet1","rowsWritten":250,"duplicatesSkipped":3,"columns":\["name","price"],"zeroResultsReason":null}. Read adds rowsRead and range. Errors: {"error":"Can't open spreadsheet …: share the sheet with …"}.

## `results` (type: `string`):

Read operation: one JSON row per sheet row, e.g. {"Student Name":"Alexandra","Home State":"CA","Major":"English"}. 0 rows read gives one free row {"status":"no_results","reason":"…"}.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "spreadsheet": "https://docs.google.com/spreadsheets/d/1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms/edit"
};

// Run the Actor and wait for it to finish
const run = await client.actor("swillz/google-sheets-import-export").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = { "spreadsheet": "https://docs.google.com/spreadsheets/d/1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms/edit" }

# Run the Actor and wait for it to finish
run = client.actor("swillz/google-sheets-import-export").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "spreadsheet": "https://docs.google.com/spreadsheets/d/1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms/edit"
}' |
apify call swillz/google-sheets-import-export --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,swillz/google-sheets-import-export"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/b9lXUGTpUzzroTxhD/builds/lolCw4YDbeCmyE3Kh/openapi.json
