# Google Sheets Sync (`rsiddiq95/google-sheets-sync`) Actor

Send any Apify dataset to Google Sheets: append, replace, or upsert by key without duplicates. New fields become new columns, quota limits are retried automatically, and sheets can be read back into datasets.

- **URL**: https://apify.com/rsiddiq95/google-sheets-sync.md
- **Developed by:** [Rafiullah Siddiq](https://apify.com/rsiddiq95) (community)
- **Categories:** Integrations, Automation, AI
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.05 / 1,000 rows

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 Sync

Send the results of any Apify Actor straight to Google Sheets, and keep them up to date without duplicates.

- ➕ **Append** new rows at the bottom.
- 🔁 **Replace** the whole tab with fresh data.
- 🔑 **Upsert by key.** Update rows that already exist (matched by a column such as `url` or `id`) and add only the new ones. Perfect for daily price trackers and lead lists.
- 📥 **Read** a sheet back into an Apify dataset, including public sheets without any setup.

### Why use this Google Sheets integration?

- **No duplicates.** Upsert mode updates existing rows instead of adding copies, and keeps the columns you added by hand (notes, statuses) untouched.
- **Your layout is kept.** Existing columns never move. New fields are added as new columns on the right.
- **Built for large datasets.** Rows are added in batches under Google's recommended request size, instead of rewriting the whole sheet each run.
- **Doesn't break on Google's limits.** When Google's per-minute quota is reached, the Actor waits and retries automatically using Google's recommended backoff.
- **Safe with scraped data.** Values are written as plain text by default, so text that starts with `=` can never run as a formula in your sheet.
- **Clear errors.** If something is wrong, for example the sheet isn't shared, the run tells you exactly what to fix.

### How to send Apify data to Google Sheets

#### 1. Create a Google service account key (one time, about 3 minutes)

1. Open [Google Cloud Console](https://console.cloud.google.com/) and create a project (or pick one).
2. Go to **APIs & Services → Library**, search for **Google Sheets API**, and click **Enable**.
3. Go to **APIs & Services → Credentials → Create credentials → Service account**. Give it any name and click **Done**.
4. Open the new service account, go to **Keys → Add key → Create new key → JSON**. A `.json` file downloads.
5. Open the file in a text editor and copy everything.

Your key is stored encrypted on Apify and is only used to talk to Google.

#### 2. Share your spreadsheet with the service account

In Google Sheets, click **Share** and add the service account's email (it looks like `name@project.iam.gserviceaccount.com`, shown in the key file as `client_email`) as an **Editor**.

#### 3. Run the Actor

1. Paste the **spreadsheet URL** and the **service account key**.
2. Choose a **mode**.
3. Enter the **dataset ID** you want to send (shown on any run's Storage tab), or paste **raw JSON data**.
4. Click **Start**.

#### Run it automatically after another Actor

Open the Actor whose results you want in Sheets, go to **Integrations → Add integration → Apify Actor**, choose **Google Sheets Sync**, and set **Dataset ID** to `{{resource.defaultDatasetId}}`. Every time that Actor finishes, its results go to your sheet.

### Input example

```json
{
    "mode": "upsert",
    "spreadsheet": "https://docs.google.com/spreadsheets/d/your-sheet-id/edit",
    "sheetName": "Prices",
    "datasetId": "{{resource.defaultDatasetId}}",
    "keyColumns": ["url"],
    "omitFields": ["html"],
    "serviceAccountKey": "{ ...contents of your key file... }"
}
```

### Output

After each run, the **Output** tab shows a summary:

```json
{
    "mode": "upsert",
    "spreadsheetTitle": "Competitor prices",
    "sheetName": "Prices",
    "appended": 12,
    "updated": 188,
    "columns": 9,
    "duplicatesInInput": 0,
    "apiRequests": 6,
    "spreadsheetUrl": "https://docs.google.com/spreadsheets/d/your-sheet-id/edit#gid=0"
}
```

In **Read** mode, each sheet row becomes a dataset item, using the header row as field names.

### How nested data is written

Nested objects become columns joined with `/`, the same as Apify's CSV export (`address/city`). Lists of plain values are joined with commas (`tags: "red, blue"`), and lists of objects are written as JSON text.

### Use it from AI agents (Claude, ChatGPT, Cursor)

AI assistants can run this Actor as a tool through [Apify's MCP server](https://docs.apify.com/platform/integrations/mcp). Add this server URL to any MCP client:

```
https://mcp.apify.com?tools=rsiddiq95/google-sheets-sync
```

Then just ask, for example: *"Read all rows from this public Google Sheet and summarise them: <sheet URL>."* Agent runs cost the same per result as runs from the console.

### Pricing

You pay a small fee per row written or read. See the **Pricing** tab for current prices.

### Limits

- Google allows up to 20 million cells per spreadsheet, and 50,000 characters per cell. Longer values are shortened and marked `[truncated]`.
- Google allows 60 write requests per minute per service account. The Actor batches rows, so most runs need only a few requests, and it waits automatically when the quota is reached.
- Upsert reads the whole tab to find matching rows, so it works best for tabs under a few hundred thousand rows.

### Works well with

- **[Google Ads Transparency Scraper](https://apify.com/rsiddiq95/google-ads-transparency-scraper):** keep a live sheet of competitors' Google ads and ad copy (upsert by `creativeId`).
- **[Google Trends Scraper](https://apify.com/rsiddiq95/google-trends-scraper):** log search interest for your keywords to a sheet every week.

### FAQ

**Can I write formulas or dates?**
Turn on **Let Google interpret values**. Text like `2026-01-31` becomes a date and `=SUM(A:A)` becomes a formula. Leave it off for scraped data.

**Can I read a sheet without a key?**
Yes. If the sheet is shared as **Anyone with the link**, choose **Read** mode and turn on **Public sheet**.

**My run says it has no access.**
Share the spreadsheet with the `client_email` from your key file as an Editor, then run again.

# Actor input Schema

## `mode` (type: `string`):

Append adds rows at the bottom. Replace clears the tab first. Upsert updates rows with a matching key and adds the rest. Read copies the sheet into the dataset.

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

The Google Sheets URL from your browser, or just the spreadsheet ID.

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

Name of the tab to use. Leave empty for the first tab.

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

Paste the full contents of a service account JSON key, then share the spreadsheet with the service account's email as an Editor. Stored encrypted. Not needed for reading a public sheet.

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

Apify dataset to send, for example from another Actor's run. In an integration, use {{resource.defaultDatasetId}}.

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

Alternatively, a JSON array of objects to send, for example \[{"name": "A", "price": 10}].

## `keyColumns` (type: `array`):

Columns that identify a row, for example url or id. Rows with the same key values are updated instead of duplicated.

## `fields` (type: `array`):

Send only these fields. Nested fields are included with their parent (address includes address/city).

## `omitFields` (type: `array`):

Fields to leave out.

## `columnsOrder` (type: `array`):

New columns listed here are placed first, in this order. Existing columns never move.

## `offset` (type: `integer`):

Number of dataset items to skip.

## `limit` (type: `integer`):

Maximum number of items to send. 0 means all.

## `keepColumnOrder` (type: `boolean`):

In replace mode, keep the tab's current columns and their order instead of starting fresh.

## `parseValues` (type: `boolean`):

When on, Google turns text like 2026-01-31 or 10% into dates and numbers, and text starting with = into formulas. Leave off for scraped data, so nothing in it can run as a formula.

## `createSheetIfMissing` (type: `boolean`):

Create the named tab when it does not exist yet.

## `publicSheet` (type: `boolean`):

Read a sheet shared as 'Anyone with the link' without a service account key.

## Actor input object example

```json
{
  "mode": "read",
  "spreadsheet": "https://docs.google.com/spreadsheets/d/1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms/edit",
  "sheetName": "",
  "datasetId": "",
  "keyColumns": [],
  "fields": [],
  "omitFields": [],
  "columnsOrder": [],
  "offset": 0,
  "limit": 0,
  "keepColumnOrder": true,
  "parseValues": false,
  "createSheetIfMissing": true,
  "publicSheet": true
}
```

# Actor output Schema

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

No description

## `rows` (type: `string`):

No description

# 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 = {
    "mode": "read",
    "spreadsheet": "https://docs.google.com/spreadsheets/d/1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms/edit",
    "publicSheet": true
};

// Run the Actor and wait for it to finish
const run = await client.actor("rsiddiq95/google-sheets-sync").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 = {
    "mode": "read",
    "spreadsheet": "https://docs.google.com/spreadsheets/d/1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms/edit",
    "publicSheet": True,
}

# Run the Actor and wait for it to finish
run = client.actor("rsiddiq95/google-sheets-sync").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 '{
  "mode": "read",
  "spreadsheet": "https://docs.google.com/spreadsheets/d/1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms/edit",
  "publicSheet": true
}' |
apify call rsiddiq95/google-sheets-sync --silent --output-dataset

```

## MCP server setup

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

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/8ewbXqu37jpVu0RkA/builds/vfsPBOxgJQZJqDqM3/openapi.json
