Google Sheets Scraper API - Sheet to JSON, No OAuth avatar

Google Sheets Scraper API - Sheet to JSON, No OAuth

Pricing

from $2.00 / 1,000 sheet rows

Go to Apify Store
Google Sheets Scraper API - Sheet to JSON, No OAuth

Google Sheets Scraper API - Sheet to JSON, No OAuth

Read any Google Sheet as typed JSON rows - no Google sign-in, no OAuth, no Drive prompt. Link-shared sheets work from the URL alone, private ones via your service account. Every tab and many sheets in one run, SQL-like filtering, dates ISO-normalised. On a schedule it bills only rows that changed.

Pricing

from $2.00 / 1,000 sheet rows

Rating

0.0

(0)

Developer

Blackcube

Blackcube

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

6 hours ago

Last modified

Share

Google Sheets Scraper API

The Google Sheets suite

Any Google Sheet, as a typed dataset. Paste a share link and press Start — no Google sign-in, no OAuth screen, no Drive permission prompt and nothing to install in the spreadsheet.

Quick start

  1. Open the sheet → ShareGeneral accessAnyone with the linkViewer.
  2. Click Copy link.
  3. Paste it into the Sheet URL input and press Start.

That is the whole setup for a link-shared sheet. Leave every other input alone and you get every tab of the sheet, typed, up to 1,000 rows.

{ "url": "https://docs.google.com/spreadsheets/d/1BxiMVs.../edit?usp=sharing" }

What people use it for

  • Feed a pipeline from a spreadsheet. Keyword lists, URL lists, ASINs, account lists — the team maintains them in Sheets, and this turns that sheet into the input for the rest of the run.
  • Sheets as an API. Point an app or an agent at a dataset that is always the current contents of a spreadsheet somebody in operations keeps up to date.
  • Watch a living sheet. Put it on a schedule with monitoring on and get a change feed: which rows were added, edited or deleted since last time.
  • Consolidate client sheets. Twelve customers each with their own spreadsheet, one run, one schema, one dataset stamped with which sheet each row came from.
  • Give an LLM clean rows. Typed values and header-keyed cells mean no parsing step between the spreadsheet and the prompt.

Input recipes

One sheet, everything in it

{ "url": "https://docs.google.com/spreadsheets/d/<id>/edit?usp=sharing" }

Only two tabs

{ "url": "https://docs.google.com/spreadsheets/d/<id>/edit", "tabs": ["Leads", "Archive"] }

Filter on Google's side so you never receive rows you did not want

{ "url": "https://docs.google.com/spreadsheets/d/<id>/edit",
"query": "select A, B, C where C > 100 order by C desc limit 200" }

Many sheets in one run

{ "url": "https://docs.google.com/spreadsheets/d/<id-1>/edit",
"urls": ["https://docs.google.com/spreadsheets/d/<id-2>/edit",
"https://docs.google.com/spreadsheets/d/<id-3>/edit"] }

Watch for changes on a schedule

{ "url": "https://docs.google.com/spreadsheets/d/<id>/edit", "monitor": true }

A private sheet, through your own service account

{ "url": "https://docs.google.com/spreadsheets/d/<id>/edit",
"serviceAccountKey": "{ \"type\": \"service_account\", ... }" }

The four things it does that a plain export does not

1. Every tab in one run, one schema

Leave Tabs empty and every tab comes back together, each row carrying its sheetName and gid. Name specific tabs — or their gid numbers — to narrow it.

2. A SQL-like filter Google runs before anything is sent

select A, C where C contains 'traffic' order by A limit 50

Column letters are the sheet's own. Selecting, filtering, ordering, limiting and date functions all work. Because the filtering happens before delivery, filtered-out rows are never billed. In query mode rowNumber is null — a filtered result's true position in the sheet is unknowable, and this Actor returns null rather than inventing a number.

3. Monitoring that does not re-charge you for old data

changeStatusMeaningBilled
addedthe row is newyes
updateda cell in it changedyes
removedthe row (or its tab) is gone — the notice carries its last known contentsno
unchangednothing movedno

State is remembered between runs, per sheet and per tab. The first monitoring run classifies everything as added; after that a quiet sheet costs only its per-sheet sync, which is what makes an hourly schedule rational.

4. Private sheets, through your own service account

Link sharing is not always allowed — internal sheets, client data, finance tabs. For those:

  1. In Google Cloud Console, create a service account and download its JSON key.
  2. In the sheet: Share → add the service account's …@….iam.gserviceaccount.com address as a Viewer.
  3. Paste the whole JSON into Service account JSON and run.

Those rows bill at the private-sheet rate. The key is handled as a secret input and nothing from it is kept between runs.

Copying a share link does not share the sheet. While sharing is still Restricted, Google answers every public request with a sign-in page, and this Actor fails with a message that says exactly that instead of a stack trace. The fix is Share → General access → Anyone with the link → Viewer. A sheet that truly cannot be shared needs the service-account route above. There is no third option — Google enforces this, and anything claiming otherwise will break.

What a row looks like

{
"type": "row",
"spreadsheetId": "1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms",
"spreadsheetTitle": "Example Spreadsheet",
"sheetName": "Class Data",
"gid": 0,
"rowNumber": 2,
"changeStatus": null,
"cells": {
"Student Name": "Alexandra",
"Gender": "Female",
"Class Level": "4. Senior",
"Home State": "CA",
"Major": "English",
"Extracurricular Activity": "Drama Club"
},
"scrapedAt": "2026-09-01T00:00:00.000Z"
}

Every cell is keyed by its column header. With Typed values on (the default) numbers arrive as numbers, booleans as booleans and date cells as ISO-8601 strings, so the data is ready for a database load, a JSON pipeline or an LLM prompt without a cleaning pass. Turn it off and every value is exactly the text the sheet displays. Duplicate headers are de-duplicated (Tags, Tags_2), a headerless sheet names its columns with the sheet's own letters (A, B, …), and the invisible trailing columns Google pads every sheet with are dropped.

Alongside the data rows every run returns free rows you are never billed for: a coverage row per sheet (tabs requested, read and errored; rows read, delivered, unchanged and removed; whether the run was complete) and one summary row with the exact spend.

What it costs

You pay forPrice
Each row delivered$2.00 per 1,000 rows
Each row from a private sheet (service account)$10.00 per 1,000 rows
Each spreadsheet successfully read$0.015

Free, always: a sheet that could not be read, a tab that failed, error rows, coverage rows, removed-row notices, the metadata row, the run summary — and on a monitoring schedule, every row that did not change. A run that reads nothing costs nothing.

Set Cost ceiling (USD) to stop any run before it passes a number you choose; the free summary row reports what the run actually spent.

Reliability

  • One failed tab never fails the run: a nine-tab sheet with one deleted tab delivers eight and returns a free error row naming the failure.
  • One unreadable sheet in a list of twelve costs nothing and does not stop the other eleven.
  • A row is billed once per run however many inputs reach it, and caps are enforced before delivery, so a cap of 100 never delivers 101.
  • Completeness is never claimed for a truncated run — the free coverage row says so plainly.

FAQ

Do I need a Google account, or to log in?

No. A link-shared sheet is read from its URL alone — no Google sign-in, no OAuth consent screen, no Drive permission prompt, nothing to install in the sheet.

Copying a share link does not share the sheet. Open Share → General access and change Restricted to Anyone with the link → Viewer. If the sheet genuinely cannot be shared, use the service-account route below instead.

Can it read a sheet that is not shared publicly?

Yes. Create a Google Cloud service account, share the sheet with its …iam.gserviceaccount.com address as a Viewer, and paste its JSON key into Service account JSON. Those rows bill at the private-sheet rate.

Does my spreadsheet ID have to be a particular length?

No. Google has issued IDs of several lengths over the years and all of them are accepted — paste the whole URL and the ID is taken out of it for you.

Can I read more than one sheet in one run?

Yes. Put the extra share links in More sheet URLs. Every row is stamped with the spreadsheet it came from, and the row budget is split across the sheets so the first one cannot consume it.

Can I filter before I pay for rows?

Yes — the Query input runs a SQL-like filter on Google's side, so rows you filter out are never delivered and never billed: select A, C where C contains 'active' order by A limit 100.

Will a schedule re-charge me for data I already have?

No. Turn Monitor for changes on and only rows that were added, updated or removed are billed. A quiet sheet on an hourly schedule costs only its per-sheet sync.

Do I need a proxy?

No. Everything is handled for you and there is nothing to buy on top.

What happens if one tab is broken?

The other tabs still deliver. The failed tab becomes a free error row that names what went wrong, and the free coverage row tells you exactly how much of the sheet you actually got.

Use it from n8n, MCP, the API or a schedule

Built to be called by a workflow, not only from the Store form. The Actor is vonsensey/google-sheets-scraper-api; every snippet below sends {}, which runs the defaults shown on the form — replace it with your own input.

n8n

Install the Apify community node (@apify/n8n-nodes-apify under Settings → Community Nodes, or search "Apify" on n8n Cloud). Add Apify → Run Actor with Actor vonsensey/google-sheets-scraper-api and your input JSON, then Apify → Get Dataset Items on the run's defaultDatasetId and pipe the rows anywhere. For scheduled runs, the On new Apify Event trigger fires when a run of this Actor finishes.

MCP (Claude, Cursor, VS Code, any MCP client)

{
"mcpServers": {
"apify": {
"url": "https://mcp.apify.com?tools=vonsensey/google-sheets-scraper-api",
"headers": {
"Authorization": "Bearer <YOUR_APIFY_TOKEN>"
}
}
}
}

Your agent then calls vonsensey/google-sheets-scraper-api as a tool with the same input the form takes and reads the dataset back.

REST API (one call, rows in the response)

curl -X POST "https://api.apify.com/v2/acts/vonsensey~google-sheets-scraper-api/run-sync-get-dataset-items?token=$APIFY_TOKEN" \
-H "Content-Type: application/json" -d '{}'

Python

from apify_client import ApifyClient
client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("vonsensey/google-sheets-scraper-api").call(run_input={})
for row in client.dataset(run["defaultDatasetId"]).iterate_items():
print(row)

JavaScript

import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('vonsensey/google-sheets-scraper-api').call({});
const { items } = await client.dataset(run.defaultDatasetId).listItems();

Make, Zapier, LangChain, CrewAI

The Apify app in Make and Zapier has a Run an Actor module: pick vonsensey/google-sheets-scraper-api. In LangChain and CrewAI the Apify tool wrappers take the same Actor id. A daily schedule needs nothing but the Console: Schedules → Create → this Actor → cron, and the dataset fills on its own.