Skip to main content

WordPress Google Sheets Integration: Send Form Submissions to a Sheet Without Zapier

Set up a WordPress Google Sheets integration that appends every form submission as a row, using a 30-line Apps Script receiver and a shared secret instead of Zapier or OAuth.

A WordPress Google Sheets integration is the one most teams want first, because a sheet is where a sales lead, an RSVP or a booking request gets looked at by the people who never open WordPress. So, if you are looking to send form submissions to a Google Sheet without paying for Zapier or wiring up a Google Cloud project, this guide can surely help.

I build Core Forms, and the Google Sheets action here is the one that ships with it. Every screen comes from a lead form on a test site.

Google Sheets can be connected in several ways, so one guide cannot cover OAuth apps and service accounts as well. But almost every site needs the same thing, a new row per submission with the columns in the right order, and in this one I will try my best to cover that properly, using the route the plugin takes: a small Apps Script receiver in your own Google account and a shared secret.

The receiver script is a download on this site, and it is linked below. Or, if you get stuck halfway, support is a message away. Without further ado, let’s get started.

How the Connection Works

Before the steps, the idea, because it is different from most plugins. There is no Google login inside WordPress and no OAuth consent screen.

Instead, a short Apps Script runs in your own Google account as a web app. Core Forms posts each submission to that script’s URL as JSON, and the script appends a row to the sheet you name. A shared secret, set in both places, is checked before anything is written, so a stranger who finds the URL cannot add rows.

That design has 2 consequences worth knowing. Nothing about your Google account is stored in WordPress, and the script is 30 lines you can read in full before you deploy it.

Deploy the Receiver Script

The maintained receiver is on the Google Sheets integration page, as a file called Code.gs:

  1. Create a new spreadsheet, and in the first row type the form’s field names as headers: NAME, EMAIL, COMPANY, ROLE. Add _form_title and _submitted_at as headers too if you want those columns.
  2. Copy the spreadsheet’s ID from its URL, the long string between /d/ and /edit.
  3. Open Extensions > Apps Script from the spreadsheet, delete the sample code and paste in the receiver.
  4. Under Project Settings > Script Properties, add CF_SHARED_SECRET with a long random value, SPREADSHEET_ID with the ID from step 2 and, if the tab is not the first one, SHEET_NAME.
  5. Click Deploy > New deployment, choose Web app, set Execute as to yourself and Who has access to Anyone, then deploy and copy the URL that ends in /exec.

“Anyone” sounds wrong, and it is correct. The script itself rejects every request that does not carry the secret, so the URL being reachable is not the same as the sheet being writable.

Headers match field names exactly, and matching is case-sensitive. NAME in the sheet and NAME on the form work; Name and NAME do not. Column order does not matter, because the script reads the header row and puts each value under its own heading.

Add the Action to the Form

  1. Open the form and go to the Actions tab.
  2. Under Add Action, find Data and click Add to Google Sheet.
  3. Paste the deployment URL into Web App URL.
  4. Paste the same secret into Shared secret.
  5. In Fields, list the fields to send, or leave it empty to send all of them.
  6. Click Save Changes.

The Add to Google Sheet action with the web app URL, a field list of NAME, EMAIL, COMPANY, ROLE and a shared secret The Fields box limits what leaves your site. A form with a message field can send the name and email and keep the message in WordPress.

The URL has to be the deployed /exec one. The Apps Script editor URL and the /dev URL both look similar and neither one accepts requests from Core Forms.

Test It

The action has no test panel, so submit the form once yourself from a draft page and check the sheet. A row that does not arrive is a mapping or credential problem, and the submission is kept so the action can be retried once it is fixed.

Open the sheet and the sample row should be there. If the reply says unauthorized, the 2 secrets do not match. If it says missing headers, the first row of the sheet is empty or the sheet name is wrong.

What Ends Up in the Sheet

A few details of the receiver are worth knowing before the real submissions start:

  • A value of 0 stays 0 and a checkbox that was left unticked stays false, rather than becoming an empty cell.
  • Checkbox groups and multi-selects arrive as one cell with the values separated by commas.
  • A value that starts with =, +, - or @ is stored as text, so a visitor cannot type a formula into your sheet.
  • The shared secret is removed from the payload before the row is written and never appears in a cell.

That third one is the quiet security point. A sheet that other people open with formulas enabled is a place where a typed =IMPORTRANGE(...) would otherwise run.

Run It in the Background

Apps Script has cold starts, and the first request after a quiet hour can take a few seconds. Tick Run in the background on the action so the visitor sees the confirmation right away and the row is written from the saved submission afterwards, with a retry if the script times out. The request itself waits up to 30 seconds.

The Limits

The receiver appends. It does not update a row when the same person submits twice, so a lead who fills in the form again is a second row. If you need one row per email, that is a formula or a script on the sheet side, or an Airtable action instead, which can hold a unique field.

Delivery is at least once, not exactly once. If the script appends a row and the reply is lost on the way back, a background retry adds a second row. Adding _submitted_at as a column makes those duplicates easy to spot and remove.

Uploaded files are not attached. A file field arrives as the file’s name and URL on your site, not as a file in Drive.

The script runs as you, with access to that one spreadsheet ID. If you leave the company, the deployment goes with your account, so deploy it from a shared or service mailbox on a team site.

What Quietly Ruins a Google Sheets Integration

Deploying with “Only myself” access. It feels safer, and it means every request from your site is turned away at Google’s login page, which the action logs as a failure.

Retyping the headers with spaces or lowercase. The match is exact, so Work email in the sheet and EMAIL on the form leave an empty column and nobody notices for a week.

Sending every field. The message box, the consent checkbox and the honeypot value do not belong in a sheet the whole company can open. List the fields you need.

Using the sheet as the only copy. Saved submissions in WordPress are the record; the sheet is a view of it. Keep both.

Final Remarks

You now have a WordPress form that writes every submission to a Google Sheet through a script you can read, with a secret that keeps strangers out and a field list that keeps private answers in. If you keep one idea from this guide, keep this one: the headers are the mapping, so name them exactly as the form fields and the rest takes care of itself.

The Google Sheets integration page has the receiver download and the short setup, and the Google Sheets to Airtable piece explains when a sheet stops being enough. If something here does not behave the way this guide describes, the support team can help.

I hope the sheet fills up with the right rows.

FAQ

Do I need a Google Cloud project or a service account?

No. The receiver is an Apps Script web app deployed from your own Google account, and the shared secret handles authentication. No OAuth client, no service account key.

Can I send submissions from several forms to one sheet?

Yes, if the forms share field names, or if the sheet has a header for every field across the forms. Empty columns are left blank. Adding the _form_title header tells the rows apart.

Can the sheet update an existing row instead of adding one?

Not with the maintained receiver, which appends. A custom Apps Script can look up a row by email before writing, and the payload gives it everything it needs.

Build the form. Stop reading.

Every note here came out of a real Core Forms setup. Use code CFLAUNCH for 20% off either plan. Ends September 30, 2026 (IST).

Public launch offer

20% off Core Forms

Forms, polls, surveys, payments, and licensing — every feature, unlimited sites, one plugin.

Copy your code

Those are the after-discount prices. Paste CFLAUNCH at checkout to apply it.

Ends September 30, 2026 (IST)