← Docs

Google Sheets forms: send responses to a spreadsheet

Every response becomes a row, as it arrives. Connect Google once, then create a sheet or pick one you already use.

Google Sheets delivery needs Basic or Advanced

Connected apps — Google Sheets, Airtable, Slack and Discord — are on the paid plans. Submissions are still stored and still reach any webhook you configure on the free plan. See plans.

How do I connect a form to Google Sheets?

  1. In the form editor, open Settings Storage, choose Google Sheets, and sign in with your Google account. The app requests drive.file and sheets scopes only.
  2. You can then create a new sheet (it will be named after the form and placed in your Drive root), or pick an existing one from the Google Picker. The picker lets you search and browse — you cannot paste a sheet ID.
  3. Once a sheet is selected, press Check connection. It verifies that the sheet is readable and the first row contains header text. The check succeeds even if the columns are empty; the headers are what matter.
  4. If you added new questions after the sheet was created, press Create missing columns to add them to the sheet.

Understanding the column map

Columns in a Google Sheet are positional — they have no ID like a Notion property or a persistent name match like an Airtable field. So the app stores sheetHeaderMap, which links each question to the text in the sheet’s header row. When a response arrives, the app reads the sheet’s headers live, finds each column’s current position, and writes the answer there.

This design has a practical consequence: if you rename a question in the form, the sheet still has the same header and nothing breaks. If you rename a column in the sheet, the app can no longer find a header with that text, so the answer is skipped rather than misrouted. If you reorder the columns in the sheet — moving columns around, sorting, or hiding and unhiding — the answer goes to whichever column now holds the matching header.

Column positions are cached for five minutes

The header position is re-read from the sheet and cached for five minutes. After that, a delivery finds the new positions on its own. However, rows that arrive in the minutes right after you rearrange columns can land in the wrong ones, and Resync will not move them — it only adds rows that are missing. Press Check connection after rearranging columns to refresh the cache immediately.

Adding new questions later

If a question is added to the form AFTER the sheet is connected, there is no column for it yet. When a response with the new question is submitted, that answer is skipped during delivery and logged as “no matching column.”

To deliver the new question, open the destination settings and press Create missing columns. This reads your sheet, finds all questions with no mapped column, and adds a new one at the right edge of the table for each, named after the question. The operation happens only when you explicitly press the button — delivery never modifies a sheet on its own. A respondent pressing Submit must not be able to change your schema.

After the columns are added, save the form. The new map is stored on the destination, and new responses will deliver to the new columns.

What gets written, and when

One row per submission, appended at the bottom of the sheet as it arrives. Each answer goes into the column matching its question’s header text. Delivery is attempted once with a timeout and there are no retries — Google Sheets is a destination, not the record. Every response is stored in your dashboard first, so a sheet that is unreachable or rate-limited cannot cost you an answer.

The app uses the insertDataOption=INSERT_ROWS parameter, which ensures that new rows are inserted rather than overwriting existing data below the table. Formulas in your sheet are never executed during write — the API uses RAW_VALUES mode, not USER_ENTERED, so the sheet treats everything as plain text.

Why is my row not appearing in the sheet?

What you seeWhat to do
A response arrived in the dashboard, but not in the sheetThe question label does not match any header in your sheet. Open the destination settings and check the column names and their order. Press Create missing columns to add columns for unmatched questions, then press Resync missing rows to deliver the response.
An answer is in the wrong columnThis happens when a question is added before its column is created. The row was already written to the wrong place. Rename a column header to match the question, then press Resync missing rows to redeliver only the missing rows. (Resync does not move rows already written.)
Rate limited (429)Google Sheets has a quota and the row was NOT written. The response is safe in your dashboard. Wait for the quota window to close, then press Resync missing rows.
TimeoutThe sheet did not respond in time. The response is safe in the dashboard. Press Resync missing rows to try again.
401 Unauthorized or 403 ForbiddenYour Google token may have expired or been revoked. Press the Sign in link in the destination settings and re-authorize the app.
Sheet not found (404)The sheet was deleted. Create a new one or pick an existing one through the Google Picker and save.
Nothing at allCheck the form is on a paid plan — connected apps are skipped on free, while everything else about the submission carries on as normal.

Can I send to Google Sheets and somewhere else at once?

Yes. Destinations are independent and delivered in parallel, and one failing never affects another or the submission itself. Every response is stored in your dashboard regardless. If Google Sheets succeeds and Slack fails, both will be logged, and you can retry Slack separately.

Common questions

Why can I rename a question and the delivery still works?
Questions are bound to columns by their HEADER TEXT through a stored map called sheetHeaderMap. When a response is delivered, the app reads the sheet's header row live and looks up each column's current INDEX — so if you rename a question in the form, the sheet still has the same header and nothing breaks. If you rename the header in the sheet itself, the delivery will still try to match the question label, find no column with that text, and skip that answer. But if you reorder the columns in the sheet, each column's position updates in the live read and the answer goes to the right place.
What happens if I add a new question to the form?
When a respondent presses Submit, their answers are written to columns that already exist. A new question has no column yet, so that answer is skipped in the delivery and named in the log. To deliver new questions, open the destination settings and press Create missing columns. That operation reads your sheet, finds questions with no mapped column, and adds a new column for each one — named after the question and placed at the right edge. It only runs when you press the button: delivery never edits the sheet on its own, because a respondent pressing Submit must not be able to change your schema.
Why can't I see or search the rest of my Google Drive?
The app requests the `drive.file` OAuth scope, which grants access ONLY to files we created for you or files you explicitly pick through the Google Picker. You cannot paste a sheet ID and link it directly — you must use the picker to choose an existing sheet. This is intentional: it prevents the app from ever reading files you do not want it to see, and it limits the damage if a token is compromised.
Why does an answer starting with = appear with a leading apostrophe?
A value starting with = is a formula in spreadsheets. If we wrote it directly, it would execute as a formula when someone opens the sheet, or when they export to CSV and open it in Excel. Instead, the app prefixes answers that start with = (or +, -, or @), or with a tab or line break, with an apostrophe, which tells the spreadsheet application to treat the value as plain text. You see the apostrophe in the cell formula bar but not in the cell itself — the cell displays the actual answer.
What does the Resync button fix?
Every response in your dashboard has a unique Response ID. When you press Resync missing rows, the app backfills up to 500 of the most recent rows that are missing from your sheet by comparing the Response ID column. Large forms may need Resync pressed more than once to backfill all missing rows. Re-running Resync is always safe because it never writes a row twice — it diffs on the Response ID column. Resync rebuilds delivery failures caused by network timeouts or rate limits. It does NOT repair a row that was already written to the wrong columns — if a question was not yet mapped when a response was delivered, the answers ended up in the wrong place and Resync cannot move them.
What happens if a delivery fails because Google is rate-limiting me?
The row is not written to the sheet, but the response is still safe in your dashboard. Google Sheets has a quota, and a busy form can exceed it. The response waits with the log marked "rate limited". Press Resync missing rows after the quota window closes, and the row will be delivered then. Nothing is lost.

Keep reading