# Google Sheets Scraper (`mlg14/google-scraper`) Actor

Export rows from publicly readable Google Sheets worksheets to structured dataset items. Select tabs and cell ranges, preserve column headers, and download CSV or JSON.

- **URL**: https://apify.com/mlg14/google-scraper.md
- **Developed by:** [MLG Data](https://apify.com/mlg14) (community)
- **Categories:** Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$1.00 / 1,000 results

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 Scraper

Scrape publicly readable Google Sheets worksheets and export their rows to CSV or JSON. This Google Sheets scraper turns a shared worksheet into structured records with the original column labels, source row numbers, and a link back to the selected tab.

Paste one or more public spreadsheet URLs, choose a worksheet tab with its `gid`, and optionally restrict the export to an A1 cell range. Each data row becomes one result. The actor reads public content; it does not change the source spreadsheet.

### What data can you extract from Google Sheets?

The output uses the same five top-level fields for every spreadsheet. Worksheet columns appear under `data`, where each key comes from the selected header row and each value is exported cell text. This keeps the source labels intact even when two spreadsheets use different column names.

| Field | Description | Example |
| --- | --- | --- |
| `spreadsheetId` | Spreadsheet identifier taken from the public URL. | `1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms` |
| `sheetGid` | Numeric identifier of the selected worksheet tab, stored as text. | `"0"` |
| `sheetUrl` | Direct URL back to the spreadsheet and tab. | `https://docs.google.com/spreadsheets/d/…/edit#gid=0` |
| `rowNumber` | One-based row number in the source worksheet. | `2` |
| `data` | Object mapping source column headings to displayed cell text. | `{"Student Name":"Alexandra","Gender":"Female"}` |

The keys inside `data` depend on the sheet. In the tested worksheet, they include `Student Name`, `Gender`, `Class Level`, `Home State`, `Major`, and `Extracurricular Activity`. An unnamed column receives a label such as `Column 7`. If a heading repeats, the second occurrence receives `(2)`, then `(3)`, so no value overwrites another. Empty cells are included as empty strings. Cell text stays as text; a displayed date or number is not silently converted to a different type.

### How to scrape Google Sheets

1. Open a spreadsheet that anyone with the link can view. Copy its browser URL into `sheetUrls`. A URL ending in `#gid=…` selects that worksheet tab. Without a tab identifier, the actor uses tab `0`.
2. If you need only part of the worksheet, set `cellRange` to an A1 range such as `A1:F100`. Keep the heading row inside that range.
3. Set `headerRow` to the heading row's position **within the exported selection**. The default is `1`, which treats the first exported row as column names.
4. Set `maxItems` to cap the number of data rows across all URLs. Run the actor and open its dataset when it finishes.
5. Download the dataset as JSON or CSV. JSON preserves the `data` object. When using CSV, review how the destination handles that object before importing it into another table.

For another tab in the same spreadsheet, add its URL as another entry with the desired `gid`. Each URL is handled as a separate export. This makes it possible to gather several tabs in one run while retaining the tab identifier on every result.

### Input

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `sheetUrls` | Array of strings | Required | Public spreadsheet URLs. Include `#gid=…` or `?gid=…` to choose a tab. |
| `headerRow` | Integer | `1` | One-based position of the header row in the exported selection. Earlier rows are skipped. |
| `cellRange` | String | Empty | Optional A1 range, for example `A1:F100`. Applies to every URL in the run. |
| `includeEmptyRows` | Boolean | `false` | Include entirely blank rows if the export contains them. |
| `maxItems` | Integer | `1000` | Maximum number of data rows across all URLs. `0` removes the actor's row cap. |
| `proxyConfiguration` | Object | Network default | Proxy settings for fetching public spreadsheet content. |

A realistic input for the public sample worksheet is:

```json
{
  "sheetUrls": [
    "https://docs.google.com/spreadsheets/d/1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms/edit#gid=0"
  ],
  "headerRow": 1,
  "maxItems": 30
}
```

That input produced 30 data rows in the verified run. The first line of the sheet supplied six headings, and the following 30 lines supplied results. The final `maxItems` value also provides a clear upper bound if the public worksheet grows later.

To export a narrower selection, set `cellRange` to `A1:C5`. The sample sheet then yields headings in columns A through C and four data rows. If you set a range beginning at row 10, the reported `rowNumber` starts at the actual worksheet row, not at 1. Put the header row first in the range unless you deliberately use a larger `headerRow` value.

### Output example

The following item is the first result of the verified 30-row run:

```json
{
  "spreadsheetId": "1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms",
  "sheetGid": "0",
  "sheetUrl": "https://docs.google.com/spreadsheets/d/1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms/edit#gid=0",
  "rowNumber": 2,
  "data": {
    "Student Name": "Alexandra",
    "Gender": "Female",
    "Class Level": "4. Senior",
    "Home State": "CA",
    "Major": "English",
    "Extracurricular Activity": "Drama Club"
  }
}
```

The row number is `2` because row `1` contains headings. The output retains the source headings with spaces and capitalization. A later worksheet can have entirely different keys under `data` while keeping the same top-level structure.

### Use cases

- **Public research tables:** Export a shared table for analysis while retaining row provenance. `sheetUrl` and `rowNumber` make it easier to inspect a surprising value in its original context.
- **Published directories:** Collect rows from a public directory whose maintainers update a spreadsheet. Schedule repeated runs and compare results using a stable record key from the source columns when one exists.
- **Event and program listings:** Read public schedules, session lists, or resource catalogs from worksheet tabs. Put each tab URL in `sheetUrls` and use `sheetGid` to separate the results later.
- **Data migration checks:** Export a source worksheet before loading records into another system. The preserved heading names help map columns and spot empty values.
- **Quality review:** Narrow a large sheet to a range and inspect that slice. The output makes blank cells explicit instead of dropping their columns.
- **Change tracking:** Run on a schedule, retain each dataset, and compare the source columns over time. The scraper reports what is publicly visible at run time; it does not maintain a built-in history.

These uses assume a public worksheet and a purpose consistent with the source's terms and the rules that apply to the data. Avoid treating a shared link as permission to process sensitive personal information.

### How much does it cost to scrape Google Sheets?

The configured result price is **$0.001 per row**, or **$1.00 per 1,000 rows**. The final charge can also reflect the run's platform billing details and any limits on the account. Check the run's usage record for the exact billed amount. The verified 30-row run completed in about five seconds and its run record displayed $0.001 usage; that observed usage should not be mistaken for a guaranteed price for every 30-row run.

Worked result-price examples:

| Rows exported | Result-price calculation |
| ---: | ---: |
| 30 | 30 × $0.001 = $0.03 |
| 1,000 | 1,000 × $0.001 = $1.00 |
| 5,000 | 5,000 × $0.001 = $5.00 |

`maxItems` is the direct way to bound the number of emitted results. A value of `1000` stops after 1,000 data rows even if the input URLs contain more. A value of `0` removes this actor-level cap, so choose it only when the size of the public sheet is known or the run's spending limit is appropriate. Header rows do not become dataset items.

### Tips for best results

Use the URL from the exact tab you want. A spreadsheet can contain many worksheets, but a single URL only selects one `gid`. To collect several tabs, add each tab URL separately. The exported rows include `sheetGid` so you can distinguish tabs after combining the dataset.

Check the first row of the chosen range. If the sheet has a title or notes above the data table, either start `cellRange` at the real header or increase `headerRow`. A wrong header row still produces records, but the keys in `data` will be misleading. A one-row preview is often enough to confirm headings before requesting a larger range.

Use a bounded `cellRange` when a sheet has unrelated notes or many formatting-only columns. The range is applied on the source export, which reduces the amount of content returned. The same range applies to every URL in one run. If tabs have different layouts, run them separately or choose a range common to all.

Treat numbers, dates, and formula results as exported display text. For calculations, parse the relevant columns in your downstream workflow with the locale and format you expect. The actor does not expose formula expressions, cell colors, comments, protected ranges, or edit history.

If a column title is blank, the output key states its position as `Column N`. If a title is repeated, the suffix distinguishes its appearances. These names are deterministic for a given header row and selection. When a source owner renames or moves columns, downstream mappings may need an update.

### Limits

Only spreadsheets that can be read without signing in are supported. A private sheet or a link that opens a sign-in page will not yield records. The actor does not accept credentials, request access, edit cells, append rows, create spreadsheets, or import a dataset into a spreadsheet. This scope keeps exports tied to public source data.

One URL exports one worksheet tab. The default tab identifier is `0`; some spreadsheets use other identifiers, which must be supplied in the URL. An invalid `gid` can return an error from the source. Worksheet names are not included because the chosen export does not consistently provide them with the row data.

The source can change between runs. Rows may move, disappear, or gain new headings. `rowNumber` identifies a position in the worksheet at export time, not a permanent record ID. For long-term deduplication, use a unique source column if the worksheet has one.

The export returns the worksheet's used region or the requested range. It may omit blank trailing rows. `includeEmptyRows` only retains blank rows that are actually present in the returned CSV. A very large or restricted document may fail, and source-side rate limits can change. The verified sample had 30 data rows; no claim is made about every possible sheet size.

If every URL fails, the run fails with an error explaining that no rows were exported. If several URLs are supplied and at least one succeeds, successful rows remain available and failures are logged. Review the run log when the result count is lower than expected.

### Use with connected workflows

A connected workflow can request: “Export the public worksheet at this URL, keep the first row as headings, and return at most 500 records.” Another useful request is: “Read columns A through F and rows 1 through 100 from this public tab, then flag records with a blank Major value.” Both requests translate directly to `sheetUrls`, `headerRow`, `cellRange`, and `maxItems`.

When a workflow passes several URLs, keep the `sheetUrl` and `rowNumber` fields in its output. That lets a reviewer verify any transformed record. Do not infer access to another tab from one public URL; add that tab explicitly.

### FAQ

#### Is it legal to scrape a public Google Sheets document?

Public access does not remove restrictions on how the data may be used. Check the sheet owner's terms, applicable law, and any privacy obligations before collecting or redistributing data. Use this actor for public information and avoid personal-data misuse.

#### Do I need to configure a proxy?

The default network configuration is designed for public sheet requests. The tested sample exported successfully without a source challenge. If your environment requires different routing, use `proxyConfiguration`; it does not grant access to a private document.

#### How fast is an export?

The verified 30-row run finished in about five seconds. Runtime depends on sheet size, source response time, retries, and the number of URL entries. Multiple tabs are processed one after another, so a large list will take longer.

#### Can I schedule runs and monitor changes?

Yes. Schedule the same input and compare the resulting datasets. Changes to public rows will appear in later exports. Use a stable source column as an identifier when possible; `rowNumber` alone may change when rows are inserted or sorted.

#### Can I export to CSV or open the results in a spreadsheet?

The dataset can be downloaded as CSV or JSON. JSON preserves the nested `data` object directly. For a flat spreadsheet table, expand the `data` keys into columns in the destination workflow.

#### What if a field is empty?

A blank source cell becomes an empty string under its header. A wholly blank row is skipped by default. Turn on `includeEmptyRows` to retain blank rows present in the export. A missing expected heading may mean that `headerRow` or `cellRange` points to the wrong location.

#### Why does a shared link return no rows?

Confirm that the link opens in a signed-out browser, that it refers to a spreadsheet, and that its `gid` points to an existing tab. A link requiring sign-in cannot be exported by this actor. A narrow range containing only a header also produces no data items.

### Integrations

Use the dataset URL from the output to read records through an API or send them to a webhook. Scheduled runs can feed a table, report, or transformation flow. CSV and JSON downloads support manual imports, while automation services can map the fixed provenance fields and the dynamic `data` columns into their destination schema.

### Support

Open an issue on the Issues tab; we reply within 24h and add fields on request.

# Actor input Schema

## `sheetUrls` (type: `array`):

Public Google Sheets URLs. Add #gid=... to select a worksheet tab; each URL exports one tab.

## `headerRow` (type: `integer`):

One-based row within the selected range containing column names. Rows before it are skipped.

## `cellRange` (type: `string`):

Optional A1 range such as A1:F100. Leave empty to export the worksheet's used rows and columns.

## `includeEmptyRows` (type: `boolean`):

Include fully blank rows when they appear in the exported range.

## `maxItems` (type: `integer`):

Maximum data rows across all URLs. Set to 0 for no cap.

## `proxyConfiguration` (type: `object`):

Network proxy settings for public spreadsheet requests.

## Actor input object example

```json
{
  "sheetUrls": [
    "https://docs.google.com/spreadsheets/d/1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms/edit#gid=0"
  ],
  "headerRow": 1,
  "cellRange": "",
  "includeEmptyRows": false,
  "maxItems": 1000,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

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

Exported worksheet rows in the default dataset.

# 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 = {
    "sheetUrls": [
        "https://docs.google.com/spreadsheets/d/1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms/edit#gid=0"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("mlg14/google-scraper").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 = { "sheetUrls": ["https://docs.google.com/spreadsheets/d/1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms/edit#gid=0"] }

# Run the Actor and wait for it to finish
run = client.actor("mlg14/google-scraper").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 '{
  "sheetUrls": [
    "https://docs.google.com/spreadsheets/d/1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms/edit#gid=0"
  ]
}' |
apify call mlg14/google-scraper --silent --output-dataset

```

## MCP server setup

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

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/8AX1RZyvpc8QxaqYZ/builds/2DGazlcbx6mkWgNIj/openapi.json
