All posts
Workflow9 min read

Extract questions and responses from Google Forms (API, Sheets, Apps Script)

Export Google Forms responses to Excel/CSV, dump the full question schema as JSON via the Forms API, and automate with Apps Script or Python — including forms you don't own.

By · Updated

To extract Google Forms responses, link the form to Google Sheets and export the sheet as CSV or Excel. To extract the questions and answer options, use the Google Forms API or Apps Script because the form structure is separate from the response spreadsheet.

Google Forms data lives in two places: the form itself (questions, answer options, settings) and the responses (one row per submission). Each has its own export path.

Responses to a spreadsheet

From the form's Responses tab, click the green Sheets icon to link a new or existing Google Sheet. Every future submission writes a row automatically. From the sheet, File → Download → Excel/CSV gives you a static export.

If the form is collecting now and you need responses later, link the sheet first — historical responses fill in only after linking.

Just the questions, not the responses

There's no built-in export for the form structure (questions, multiple-choice options, section breaks). Two options:

  • Make a copy of the form (File → Make a copy), open the copy, and visually catalog each question. Tedious but works.
  • Use the Google Forms API to dump the form schema as JSON.

Forms API for automation

GET https://forms.googleapis.com/v1/forms/{formId} returns the full form definition: every question, type, options, validation rules. GET .../responses returns submissions. Authentication is the standard OAuth2 flow with the forms.responses.readonly scope.

Use this when you're integrating Google Forms into another system — a CRM, a data warehouse, an analytics pipeline.

When you only have the published form (no edit access)

If you're cataloging someone else's published form, you can't access the API or the response sheet. Take screenshots of each page, drop them into ExtractFox's image data extractor with a prompt like "extract every question, its type (short answer, multiple choice, checkbox), and its options as a flat table." Useful for competitive research or for documenting forms in legacy systems where the original Google account is gone.

What to watch for

  • Conditional questions ("go to section based on answer") only show up in the API response, not the response sheet — the sheet flattens them.
  • Uploaded files in form responses are stored in the form owner's Drive; the response sheet shows links, not the files themselves.
  • Timestamps in the response sheet are in the form owner's time zone, not the respondent's.

Apps Script: dump schema and responses in one run

If you're already in the Google ecosystem, Apps Script avoids OAuth setup for a one-off. Open Extensions → Apps Script on the linked response sheet:

function exportFormSchema() { const form = FormApp.openByUrl("YOUR_FORM_EDIT_URL"); const items = form.getItems().map(item => ({ title: item.getTitle(), type: item.getType().toString(), id: item.getId(), })); const sheet = SpreadsheetApp.getActiveSpreadsheet().insertSheet("Schema"); sheet.getRange(1, 1, items.length, 3).setValues( items.map(i => [i.title, i.type, i.id]) ); }

For multiple-choice items, call item.asMultipleChoiceItem().getChoices() inside the map. Grid items need asGridItem() — the type string alone doesn't give you column headers.

Python: Forms API with service account

For a scheduled ETL job, use the REST API with a service account that has access to the form (share the form with the SA email as Editor):

from google.oauth2 import service_account from googleapiclient.discovery import build creds = service_account.Credentials.from_service_account_file( "sa.json", scopes=["https://www.googleapis.com/auth/forms.body.readonly", "https://www.googleapis.com/auth/forms.responses.readonly"]) forms = build("forms", "v1", credentials=creds) form = forms.forms().get(formId="FORM_ID").execute() for item in form.get("items", []): print(item["title"], item.get("questionItem", {}).get("question", {}).get("choiceQuestion"))

Paginate responses with forms.forms().responses().list(formId=..., pageToken=...). Each response has answers keyed by questionId — join back to the schema dump to get human-readable column names.

Quiz vs survey: different export shape

Quiz forms store correct answers and point values in the schema that never appear in the response sheet. If you're migrating a training quiz, you need the API — the linked spreadsheet only has what respondents submitted, not the answer key.

File upload questions

When a form accepts file uploads, the response sheet contains Drive URLs, not the files. To bulk-download attachments, iterate responses via API, extract fileUploadAnswers[].files[].driveFileId, and call the Drive API files().get_media(). Store them before the form owner deletes the form — uploaded files live in their Drive quota.

Push vs pull: webhooks and Apps Script triggers

Polling responses.list every five minutes works for low-volume forms. For real-time pipelines, install an Apps Script onFormSubmit trigger that POSTs each response to your webhook as JSON. The trigger fires within seconds of submission and includes every answer keyed by questionId — map IDs to titles from a cached schema dump.

Google Cloud Pub/Sub push subscriptions for Forms are not a first-class feature — Apps Script or a middle-tier that polls with exponential backoff is still the standard pattern for most teams.

Cleaning response sheet column headers

Linked Sheets auto-generate headers from question text — which breaks when you rename a question mid-collection. The API returns stable questionId keys. Build your warehouse schema on questionId, maintain a lookup table for human-readable titles, and never trust sheet column letters as permanent identifiers.

PII and retention in exports

  • Email and name fields are PII — restrict Sheet sharing and API service account scopes.
  • File upload answers may contain ID scans or medical forms — treat Drive folders as sensitive.
  • Set a retention policy: archive responses to encrypted storage, then delete the linked Sheet.
  • EU respondents: document lawful basis before exporting to a non-Google warehouse.

Frequently asked questions

Can I export Google Forms responses without linking to Sheets?+

Yes — use the Forms API responses.list endpoint or download as CSV from the Responses tab in the form editor (limited to what's visible in the UI). The API is the only path for automation.

How do I get the form ID from the URL?+

The edit URL looks like docs.google.com/forms/d/FORM_ID/edit. The published URL uses the same ID. The response spreadsheet URL has a different ID — that's the Sheet ID, not the Form ID.

Why are some columns missing from my response export?+

Conditional sections only appear when triggered — unfilled branches leave blank cells. File uploads show as links. Grid questions flatten to multiple columns with auto-generated headers that don't match your question text.

Related reading

Stop reading, start extracting

Drop a PDF or image into ExtractFox and get structured data back in seconds.

Try a free extraction →