# Use Google Sheets as a REST API

A Google Sheet makes a surprisingly good backing store for content, config, and reference data. PasteSheet puts a real REST API in front of it — typed JSON, query parameters, caching, and access control — so your app can just fetch it.

*Last updated: 2026-07-22 · Source: <https://pastesheet.com/guides/google-sheets-rest-api>*

## Key facts

- Google's Sheets API is **cell-oriented**: it reads ranges like `A1:D50`, not records. Reassembling those into row objects is work you do yourself. ([source](https://developers.google.com/workspace/sheets/api/limits))
- Google's API requires a **Google Cloud project plus OAuth credentials or a service account** before it will return a single row. ([source](https://developers.google.com/workspace/sheets/api/guides/authorizing))
- The Google Sheets API allows **300 read requests per minute per project** and **60 per minute per user**. Past that it returns `429 RESOURCE_EXHAUSTED`. ([source](https://developers.google.com/workspace/sheets/api/limits))
- A cached endpoint reads the sheet **once per TTL**, not once per request — so 10,000 visitors become one upstream read, and Google's quota stops being your problem.
- Free tiers among hosted sheet-to-API services vary widely — **SheetDB** offers 2 APIs and 500 requests a month free, while **Sheet.best** offers a 30-day trial and no permanently free tier. **PasteSheet's Free plan is 3 endpoints and 2,000 requests a month**, with MCP included. *Vendor limits verified 22 July 2026.* ([source](https://sheetdb.io/pricing))

## How do you turn a Google Sheet into a REST API?

Paste a link-shared sheet URL into PasteSheet, or connect Google and choose a restricted sheet in Picker. Either path publishes the rows at a JSON endpoint you can `GET` immediately. There is no Google Cloud project for you to create, and every edit shows up through the API once the cache refreshes.

There are really only three ways to do this. You can call **Google's own Sheets API**, which is cell-oriented — it reads ranges like `A1:D50` and leaves you to reassemble them into records, after you have stood up a Cloud project and OAuth credentials. You can publish an **Apps Script web app**, which means writing and maintaining a script, and living inside Apps Script's execution quotas. Or you can point a hosted service at the sheet and get an endpoint. This page is about the third path, and about what to check before you pick one.

PasteSheet returns records: one JSON object per row, with your column names as keys and types already applied. No range maths, no header-row bookkeeping, no SDK.

You can see the exact payload without signing up: the free [Google Sheets to JSON converter](https://pastesheet.com/tools/google-sheets-to-json) runs the same conversion in your browser. The difference is that a converter hands you a file, and an endpoint hands you a URL that never goes stale.

## From sheet to live endpoint

The whole conversion is four steps and takes about a minute:

1. Give the sheet a **header row** — those cells become your JSON keys, so name them the way you want your API to read.
2. Choose your source path. For a public source, set sharing to **Anyone with the link → Viewer**. For a restricted source, connect your Google account and select the exact sheet in Google Picker.
3. Paste the public URL, or finish the Picker flow. PasteSheet reads the tabs, previews the rows, and infers each column's type.
4. Save. You now have a permanent URL like `https://pastesheet.com/api/your-endpoint-id` that any app, site, or AI agent can read.

## The endpoint

Every connected sheet gets a JSON endpoint. A GET request returns rows plus pagination metadata:

```bash
curl 'https://pastesheet.com/api/your-endpoint-id?limit=2'

{
  "data": [
    { "id": 1, "name": "Blue Widget", "price": 19.99, "in_stock": true },
    { "id": 2, "name": "Red Widget",  "price": 24.5,  "in_stock": false }
  ],
  "total": 128,
  "limit": 2,
  "offset": 0,
  "meta": { "row_cap": 500, "row_cap_applied": false }
}
```

## What the response tells you

`total` is the match count *before* pagination, so a client always knows how much data exists beyond the window it received. `meta.row_cap_applied` is the honest part: if your plan's row cap trimmed the result, the response says so instead of quietly reading short. That single flag is the difference between a paginated list that is correct and one that silently loses its tail.

A multi-tab sheet addresses each tab as its own path segment — `/api/your-endpoint-id/Products` — so one spreadsheet can back several resources.

## Query without a query language

Every column is queryable with plain URL parameters. There is no DSL to learn and nothing to escape:

- `?status=active` — exact-match filter on any column.
- `?contains[name]=jo` — partial match on a named column.
- `?search=widget` — full-text search across every column.
- `?sort=created&order=desc` — sort by any field.
- `?limit=20&offset=40` — page through results.
- `?group_by=category&count=1` — group rows and count them server-side.
- `?group_by=category&sum=revenue&avg=price` — totals and averages, computed before the response leaves us.

The aggregation parameters matter more than they look. Without them, "what is the total revenue per category?" means downloading every row and doing the arithmetic in your client — on every page load, in every language you support. With them, the client receives the answer. Full-text search and aggregation are Pro-plan features; filtering, sorting, and pagination work on every plan including Free.

## Read it from JavaScript

It is plain JSON over HTTP with CORS enabled, so browser code can fetch it directly — no proxy, no SDK, no key for a public endpoint:

```javascript
const res = await fetch(
  'https://pastesheet.com/api/your-endpoint-id?status=active&sort=created&order=desc&limit=20'
);
const { data, total } = await res.json();

data.forEach(row => console.log(row.name, row.price));
```

## Read it from Python

Nothing Google-specific — `requests` and the standard library are enough:

```python
import requests

res = requests.get(
    "https://pastesheet.com/api/your-endpoint-id",
    params={"status": "active", "limit": 20},
)
rows = res.json()["data"]

for row in rows:
    print(row["name"], row["price"])
```

## Read it from PHP

A private endpoint takes a bearer key in the `Authorization` header; a public one needs no header at all:

```php
$response = Http::withToken('ps_your_api_key')
    ->get('https://pastesheet.com/api/your-endpoint-id', [
        'status' => 'active',
        'limit' => 20,
    ]);

foreach ($response->json('data') as $row) {
    echo $row['name'].' — '.$row['price'].PHP_EOL;
}
```

## Aggregate from the command line

Grouped totals come back already computed, so a dashboard can call one URL instead of paging through the sheet:

```bash
curl 'https://pastesheet.com/api/your-endpoint-id?group_by=category&count=1&sum=revenue'

{
  "data": [
    { "category": "Widgets", "count": 62, "revenue": 12480.5 },
    { "category": "Gadgets", "count": 41, "revenue":  8310.0 }
  ],
  "total": 2
}
```

## The four ways to do this, compared

The trade-offs are not subtle, and they are mostly about setup cost and what the API is allowed to do to your spreadsheet:

|  | PasteSheet | Google Sheets API | Apps Script web app | Typical sheet-to-API SaaS |
| --- | --- | --- | --- | --- |
| Setup | Paste a share URL | Cloud project + OAuth or service account | Write, deploy & maintain a script | Connect a Google account |
| Returns | Typed row objects | Cells and ranges | Whatever you code | Row objects |
| Access model | **Read-only by design** | Whatever you scope | Whatever you code | Usually read-write CRUD |
| Caching | Built in, TTL you set | None — you build it | None — you build it | Varies |
| Rate limits | Per-plan, cache absorbs traffic | 60 reads/min per user | Apps Script quotas | Per-plan request cap |
| Filter, sort, paginate | URL parameters | You implement it | You implement it | Usually yes |
| Server-side aggregation | `group_by`, `count`, `sum`, `avg` | No | You implement it | Rare |
| Schema-drift protection | Snapshot, lock & alerts | No | No | No |
| Also an MCP server | Yes — [same endpoint](https://pastesheet.com/guides/google-sheets-mcp) | No | No | No |
| Free tier | 3 endpoints, 2,000 req/mo | Free within quota | Free within quota | Varies — some are trial-only |

## What happens when the traffic arrives

This is the part almost nobody writes about, and it is where sheet-backed APIs actually break. Google's Sheets API allows **300 read requests per minute per project and 60 per minute per user**; past that it returns `429 RESOURCE_EXHAUSTED`. If your API reads the spreadsheet once per incoming request, a modest traffic spike — a launch, a newsletter, a scraper — takes the whole thing down.

A cache is the entire answer. PasteSheet reads the sheet **once per TTL, not once per request**, so 10,000 visitors in five minutes become a single upstream read and Google's quota stops being your problem. The TTL is 5 minutes on Free and tunable from 30 seconds to 1 hour on paid plans, and you can force a refresh when you want an edit live immediately.

The deep dives: [Google Sheets API rate limits](https://pastesheet.com/guides/google-sheets-api-rate-limits), [fixing 429 quota-exceeded errors](https://pastesheet.com/guides/google-sheets-api-quota-exceeded), and [how to cache a Google Sheets JSON API](https://pastesheet.com/guides/google-sheets-json-cache).

## Read-only, on purpose

Most "sheet to API" tools lead with read-*write* CRUD, and treat that as the headline feature. It is worth asking what it costs. Read-write means a third party holds edit access to your spreadsheet, and it means any credential that leaks — in a client bundle, a repo, a screenshot, a chat log — can rewrite or wipe your data rather than merely read it.

PasteSheet is deliberately **read-only**: it publishes the rows, and nothing can change the sheet through the API. That is what makes an endpoint safe to embed in a public page, ship inside a mobile app, or hand to an AI agent that you do not fully control. A public sheet needs **no API key at all**, because there is nothing a key would protect.

If you genuinely need writes, a sheet is usually the wrong database — see [is Google Sheets good as a database?](https://pastesheet.com/guides/is-google-sheets-good-for-a-database) for where the line sits. Otherwise, read the same endpoint from [React](https://pastesheet.com/guides/read-google-sheet-in-react), [Python](https://pastesheet.com/guides/read-google-sheet-in-python), or [Laravel](https://pastesheet.com/guides/read-google-sheet-in-laravel).

## What happens when someone renames a column

A spreadsheet is a shared document, which means the schema of your API is editable by anyone with access to it. Someone renames `Price` to `Unit Price`, and every consumer that reads `row.price` starts returning `undefined` — with a `200 OK`, so nothing alerts and nothing fails loudly.

PasteSheet snapshots the column schema on every read and compares it against the last known shape. You can **lock** an endpoint's schema, so a drifting sheet returns a clear `409` instead of silently serving the wrong payload, and you can have drift **alerts** sent by email or webhook. It is the failure mode that quietly breaks sheet-backed apps, and no other approach on the table above addresses it.

## The same endpoint is also an MCP server

Once a sheet is a REST API, the interesting next question is whether an AI agent can read it too. It can: every PasteSheet endpoint is simultaneously a [Model Context Protocol server](https://pastesheet.com/guides/google-sheets-mcp), exposing `list_tabs`, `get_schema` and `query_rows` to Claude, ChatGPT, Cursor, and anything else that speaks MCP.

It runs on the same query engine as the REST endpoint, so an agent and your application see identical rows, with identical types, filtered identically — and the read-only guarantee above is exactly why handing that connection to a model is safe. MCP is included on the Free plan for public endpoints.

## Typed, cached, and access-controlled

Column types and aliases are inferred automatically, so numbers, booleans, and dates come back typed rather than as raw strings, and a column called `Order Date` can be exposed as `order_date` without touching the spreadsheet. Responses are cached at the edge with a `Cache-Control` header your CDN and browser will honour, and any endpoint can be locked behind a bearer key.

Prefer to [walk through converting a sheet step by step](https://pastesheet.com/guides/convert-google-sheet-to-rest-api), or see what a sheet-backed API is actually good for in the [use-case library](https://pastesheet.com/use-cases).

## What it costs

You do not need a paid plan to get a working endpoint:

**Free — $0/month.** For side projects and trying things out.

- Endpoints: 3
- Requests: 2,000 / month
- Rows per endpoint: 500
- Tabs per endpoint: 1
- Rate limit: 60 / minute

The **Free plan** covers 3 endpoints and 2,000 requests a month with a 500-row cap, MCP included — no credit card and no expiry. Paid plans start at **Starter ($9/mo)** and add private endpoints with bearer keys, a tunable cache TTL, more tabs, and a higher row cap; full-text search and aggregation arrive on Pro.

Worth knowing when you compare: some hosted sheet-to-API services have no permanently free tier at all, only a trial. The numbers on the cards here are the real limits, read straight from our plan configuration.

## Frequently asked questions

### Does Google Sheets have a REST API?

Google offers the Sheets API, but it is cell-oriented and needs OAuth and code. PasteSheet gives you a row-oriented JSON REST API from a pasted URL, with no setup.

### Do I need a Google Cloud project?

Not your own. Public sources need a share URL. Restricted sources use PasteSheet's Google connection and Picker flow, while Google's own Sheets API still requires a Cloud project plus credentials before it returns a row.

### Do I need a Google Sheets API key?

No. PasteSheet reads published and link-shared sheets, so a public endpoint is keyless. The optional bearer key protects your endpoint, not your Google account.

### What format does the API return?

JSON shaped as { data, total, limit, offset, meta }, where each row is an object keyed by your column aliases with values cast to their inferred types. total is the match count before pagination.

### Can I filter and sort in the URL?

Yes — exact-match filters, contains matches, full-text search, sort/order, and limit/offset are all plain query parameters. No query language to learn.

### Can the API write back to my sheet?

No, and that is deliberate. PasteSheet is read-only by design, so an endpoint is safe to embed in a public page or hand to an AI agent — it can query every row but change none.

### How is this different from an Apps Script web app?

An Apps Script web app is code you write, deploy, and maintain, running inside Apps Script execution quotas. PasteSheet is a hosted endpoint with caching, typed columns, and query parameters already built.

### What happens if my endpoint gets a lot of traffic?

Responses are served from cache, so the sheet is read once per TTL rather than once per request. That keeps you clear of Google's 60-reads-per-minute-per-user limit even under a spike.

### Does my sheet have to be link-shared?

No. A public source can use Anyone with the link → Viewer. For a restricted source, connect your Google account and choose the exact sheet in Picker. The endpoint can separately be public or protected by a bearer key.

### Can I read it from the browser?

Yes. Responses are CORS-enabled, so front-end code can fetch them directly without a proxy or a server-side shim.

### What if someone renames a column?

You can lock an endpoint's schema so a drifting sheet returns a clear 409 instead of silently serving the wrong shape, and have drift alerts sent by email or webhook.

### Is it free?

The Free plan covers 3 endpoints and 2,000 requests a month with MCP included, no credit card and no expiry. Paid plans start at $9/mo for private endpoints, a tunable cache TTL, and higher limits.

## Sources

- [Usage limits — Google Sheets API](https://developers.google.com/workspace/sheets/api/limits) — Google
- [Authorize requests — Google Sheets API](https://developers.google.com/workspace/sheets/api/guides/authorizing) — Google
- [Web Apps — Apps Script](https://developers.google.com/apps-script/guides/web) — Google
- [Model Context Protocol specification](https://modelcontextprotocol.io/specification/2025-06-18) — Model Context Protocol
- [SheetDB pricing](https://sheetdb.io/pricing) — SheetDB
- [Sheet.best pricing](https://sheetbest.com/pricing) — Sheet.best

## Related guides

- [Convert a Google Sheet to a REST API](https://pastesheet.com/guides/convert-google-sheet-to-rest-api) — A step-by-step guide to converting a Google Sheet into a JSON REST API: share the sheet, paste the URL, and call it with filters, sorting, and paging.
- [Google Sheets API Pricing: Is It Free?](https://pastesheet.com/guides/is-google-sheets-api-free) — Google Sheets API pricing, straight: standard use costs nothing, but quotas and setup do. What is actually free, what is capped, and where the real cost lands.
- [Google Sheets API Rate Limits (60/min)](https://pastesheet.com/guides/google-sheets-api-rate-limits) — Google Sheets API rate limits are 300 reads per minute per project and 60 per user. Here is why read-heavy apps hit a 429 — and how caching removes the wall.
- [How to Use Google Sheets as a Database](https://pastesheet.com/guides/google-sheets-as-a-database) — Use Google Sheets as a database: query rows over a cached REST API with filters, sorting, and paging — no backend. When it fits, when it doesn't, and how.
- [Google Sheets MCP Server for Claude & ChatGPT](https://pastesheet.com/guides/google-sheets-mcp) — Turn any public or private Google Sheet into an MCP server so Claude, Cursor, and ChatGPT can read and query it in plain English. No code, no backend.

---

[PasteSheet](https://pastesheet.com) turns any Google Sheet into a live REST API and MCP server for AI agents — no backend, no code. Canonical HTML version of this page: <https://pastesheet.com/guides/google-sheets-rest-api>
