Sending WordPress Form Entries to Google Sheets
Three ways to get form entries into a spreadsheet: Apps Script, the Sheets API or a connector, and the one that keeps working when volume grows.

In this article
Many small businesses want every website enquiry in a spreadsheet: the sales team already lives in Google Sheets, it is easy to share and it needs no new software. Getting a WordPress form to write a row is not difficult. Getting it to write exactly one row per enquiry, never lose one when email fails and never let a visitor's text run as a spreadsheet formula takes a little more care.
This guide is written from a working implementation: the enquiry form on this site posts to a Google Apps Script web app that writes to a sheet and then sends the notification emails. It compares the three common routes, then walks through the details that matter in production.
Three routes, and why Apps Script wins for low volume
The first route is a connector service such as Zapier or Make, or a form plugin's built-in Sheets add-on. It is the quickest to set up and needs no code, but each submission passes through a third party, usage is billed by task and debugging a failed run means reading someone else's logs. It suits teams that already pay for the connector and accept those trade-offs.
The second route is calling the Google Sheets API directly from WordPress with a service account. It is the most flexible and scales well, but it means storing service-account credentials on the WordPress server, handling OAuth tokens and writing more code than most enquiry forms deserve.
The third route is a Google Apps Script web app bound to the spreadsheet. WordPress, or the browser, sends the form data as a POST request to the script's URL, and the script validates it, appends the row and sends emails. There are no credentials on the WordPress side, the code lives next to the sheet, and the free quotas comfortably cover a typical business enquiry form. For most sites, this is the one I recommend.
The web app endpoint: deployment, access and the URL that must stay stable
An Apps Script web app is published with Deploy, then New deployment, as a web app. Two settings matter. Execute as should be the account that owns the sheet, so the script can write to it. Who has access must be Anyone, because the request comes from website visitors who are not signed in to Google.
Each deployment has its own /exec URL. When you change the code later, edit the existing deployment and publish a new version rather than creating a new deployment; otherwise the URL changes and the website keeps posting to the old one. Keep the URL in one place in your WordPress configuration, and keep the spreadsheet ID in the script's properties instead of hard-coding it, so the same code can point at a test sheet.
Because the endpoint is public, treat every request as untrusted. The script should validate types and lengths, reject malformed email addresses and return a simple JSON response that tells the form whether to show success or an error.
Posting from WordPress, and validating on both ends
There are two ways to send the data. The form can post directly from the browser to the Apps Script URL with fetch, which keeps WordPress out of the request and works well with static or headless front ends. Or WordPress can receive the submission and forward it server-side with wp_remote_post, which keeps the script URL out of the page source and lets you log failures in WordPress.
Validate in both places. Browser validation gives the visitor quick feedback; server validation is the one that counts, because anyone can call the endpoint directly. Apply the same length limits on both sides, trim whitespace, normalise email addresses to lower case and reject anything that does not fit. A shared list of limits, documented in both codebases, stops them drifting apart.
Formula injection: why a leading = must be escaped
Spreadsheets treat a cell that begins with = as a formula, and some also evaluate cells that begin with +, - or @. If a visitor types =HYPERLINK(...) or worse into a message field and you write it as-is, the sheet may evaluate it when someone opens it. This is known as CSV or formula injection, and it turns a harmless form into a way to put active content in front of your staff.
The fix is simple: before writing a value, check whether it starts with one of those characters and, if it does, prefix it with an apostrophe so the sheet stores it as plain text. Apply it to every user-supplied field, not only the message. The visible value stays the same for the person reading the sheet.
Duplicates: idempotency for a double-click and a browser retry
Real forms produce duplicates. People double-click Send, mobile networks drop a response and the browser retries, and some form scripts retry automatically on a timeout. Without protection, each of those becomes another row and another notification email.
A practical guard is to compute a short hash of the fields that identify an enquiry, such as the email address and the message, and store it in the Apps Script cache for a few minutes. If the same hash arrives again within that window, return success without writing a second row. The visitor still sees a confirmation, and the sheet stays clean. If the cache itself fails, let the enquiry through; a rare duplicate is better than a lost lead.
Two simultaneous submissions can also collide while appending. Wrap the write in the script's lock service so only one request writes at a time, and release the lock in a finally block so an error cannot leave it held.
Save first, then send email
Most enquiry scripts send the notification email and then write the row, or do both inside one try block. If the mail service fails or the daily quota runs out, the whole request fails and the visitor is told to try again, even though nothing was wrong with their enquiry.
Reverse the order. Append the row, release the lock, then send the notification and any acknowledgement email inside their own error handling. If an email fails, log it and still return success, because the enquiry is safely in the sheet. Someone checking the sheet will see it, and the failure is visible in the script's execution log.
Quotas, and the point at which you move to the Sheets API
Apps Script has daily quotas for email recipients, script runtime and service calls, and they differ between consumer and Workspace accounts. For a business receiving tens of enquiries a day they are rarely a problem. Check the current numbers in Google's quota documentation, and expose the remaining email quota in a simple health check if the form is important.
Consider moving to the Sheets API or a proper CRM when submissions reach hundreds a day, when several systems need the data, or when you need reliable retries and audit logs. At that point a spreadsheet is usually the wrong destination anyway. The CRM integration guide, the webhooks guide and the third-party API integration guide cover those next steps, and the third-party API integrations service explains how I scope this work.
Frequently asked questions
How do I send WordPress form submissions to Google Sheets?
The simplest reliable method is a Google Apps Script web app attached to the sheet. The form posts its fields to the script URL, and the script validates them, appends a row and sends any notification emails.
Is Google Apps Script free for this?
Yes, it runs on a Google account at no extra cost, within daily quotas for email, runtime and service calls. A typical business enquiry form stays well inside them; check Google's current quota page for exact figures.
What happens if the Sheets request fails?
The form should show a clear error and keep the visitor's text so they can try again. On the script side, save the row before sending email, and treat email failures as logged warnings rather than failures of the whole submission.
Can I stop a spreadsheet treating an entry as a formula?
Yes. Before writing each value, check whether it starts with an equals sign, plus, minus or at sign, and if so prefix it with an apostrophe. The sheet then stores it as plain text.
Topics
- WordPress Google Sheets integration
- send form entries to Google Sheets WordPress
- WordPress Apps Script webhook
- Contact Form 7 Google Sheets