> ## Documentation Index
> Fetch the complete documentation index at: https://formpaste.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Google Sheets

> Auto-append every submission as a row in a Google Sheet you own - Pro.

<Info>**Pro** - connecting a sheet requires Pro.</Info>

Formpaste can append every non-spam submission as a new row in a Google Sheet you own.
No Zapier in between, no scripts, no OAuth screen: you share a sheet with an address we
give you, paste the sheet's link, and rows start arriving.

## Connecting a sheet

1. Open a form in the [dashboard](https://app.formpaste.com) → **Automations** →
   **Google Sheets**.
2. Copy the **service-account address** shown on the card. It looks like
   `formpaste-sheets@…iam.gserviceaccount.com`.
3. In Google Sheets, open your sheet → **Share** → paste that address → give it **Editor**
   access → Share.
4. Back in Formpaste, paste the sheet's URL (or just its ID), turn the toggle on, and save.

<Warning>
  **Editor, not Viewer.** We append rows and add a column when your form gains a new field,
  so read-only access is not enough. Sharing with the wrong permission is the most common
  reason a connected sheet stays empty.
</Warning>

You can paste the whole URL straight from your browser's address bar -
`https://docs.google.com/spreadsheets/d/<ID>/edit#gid=0` - and we extract the ID for you.

## What a row looks like

The first submission writes the header row: a `_submitted_at` column, then one column per
form field, using your field `name` attributes.

| `_submitted_at`          | name       | email                                       | message |
| ------------------------ | ---------- | ------------------------------------------- | ------- |
| 2026-07-26T09:14:22.031Z | John Smith | [john@example.com](mailto:john@example.com) | Hello!  |

`_submitted_at` is when the **submission** was received (ISO 8601, UTC), not when the row
was written - so the timestamp still means what you expect if a row arrives after a retry.

## When a form gains a new field

Say you add a `phone` field to your form after the sheet already exists. Formpaste **adds a
`phone` column** to the right of your existing headers and fills it, rather than silently
dropping the value.

This is deliberate, and it is the main way we differ from other form backends: a submission
should never be quietly lost because a column did not exist yet. The same principle is why
spam goes to a [quarantine folder](/docs/features/quarantine) instead of being deleted.

A few consequences worth knowing:

* **New columns are appended at the end**, never inserted in the middle - your existing
  column layout is never shifted.
* **You can reorder or rename columns** in the sheet. We read row 1 before every append and
  match your field names to whatever headers are there now.
* **A header with no matching field** in a given submission gets an empty cell, so rows stay
  aligned.
* **Renaming a header to something your form does not send** means we treat it as a new
  field next time and add the column back. Rename the form field instead if you want the
  column to follow.

## When it fires

A row is appended for each **delivered, non-spam** submission - the same ones that generate
a notification email. Quarantined (spam) submissions do not reach your sheet.

Appending is best-effort and independent of email: a Sheets failure never blocks, delays, or
retries your email notification.

## Values are never formulas

Submitted values are written as **literal text**. If a visitor submits `=IMPORTRANGE(...)`
or `=HYPERLINK(...)`, the cell shows that text; it does not execute inside your spreadsheet.
This is deliberate - a form is an untrusted input, and a formula running in your sheet on a
stranger's say-so would be a real security problem.

## When something goes wrong

The card tells you what to fix, in plain language:

| What you see                           | What happened                                                               | Fix                                                  |
| -------------------------------------- | --------------------------------------------------------------------------- | ---------------------------------------------------- |
| Share the sheet with the address above | The sheet is not shared with our service account, or it is shared read-only | Share it with the address on the card, as **Editor** |
| That sheet could not be found          | Wrong ID, or the sheet was deleted                                          | Re-paste the sheet URL                               |
| That spreadsheet ID is not valid       | The pasted value is not a Google Sheets link or ID                          | Paste the full URL from your browser                 |

These three are **configuration problems**, so we do not retry them - retrying cannot fix a
permission setting. Fix the cause and submit again; the warning clears as soon as you save a
new configuration.

Temporary problems (Google rate-limiting us, a Sheets outage) are retried automatically and
never show an error, because there is nothing for you to fix.

## Limits & behaviour

* **One sheet per form.** Several forms can point at the same sheet if you want them
  combined, though a sheet per form is usually easier to read.
* **Appends are serialized**, so two submissions arriving at the same moment cannot
  interleave and corrupt the header row.
* **Rows normally appear within seconds** of the notification email.
* **We only ever read row 1** (your headers) and append rows. We never read the rest of
  your sheet's contents.
* Google's own limit of **10 million cells** per spreadsheet applies - roughly two million
  submissions at five columns.

## Free vs. Pro

Free forms cannot connect a sheet. Attempting to enable it on Free returns:

| Code               | HTTP | Meaning                           |
| ------------------ | ---- | --------------------------------- |
| `upgrade_required` | 403  | This feature requires a Pro plan. |

Turning the integration **off** always works, on any plan - so downgrading never traps you
with an integration you cannot disable.

## Zero-config

Nothing is sent to any sheet until you connect one on a Pro form. A new form writes nowhere.

<Note>
  Prefer to route submissions somewhere else - a CRM, a database, Slack? Use
  [webhooks](/docs/features/webhooks) with an automation platform. Sheets is the one destination
  we built natively, because "where do my submissions go" is answered by a spreadsheet more
  often than by anything else.
</Note>
