Typeform, Jotform all charge a monthly fee to collect form submissions. WordPress contact form plugins (WPForms, Contact Form 7, Gravity Forms) are free but have terrible UI. Form submissions data is stuck inside WordPress database. Neither is a great option for your small business.
So I built an alternative using Google Apps Script. Copy & Paste this into your WordPress or any other website to display a contact form & capture leads in Google spreadsheets. Forever Free. You could even customize the look and feel to match your own website.

The WordPress Problem
WordPress contact form plugins have two fundamental issues.
- The UI is not yours. You get their fields, their styling, their stylesheet fighting your theme. Customizing it means paying for pro or writing CSS overrides that break on the next update.
- Submissions sit in the WordPress database and exporting it to your own CRM or Email Marketing software is a painful.
This solution fixes both. Plain HTML form, you control the design. Submissions go directly to Google Sheets. Drop it into any WordPress page via a Custom HTML block.
How It Works
User submits form
β
HTML sends POST to Apps Script URL
β
Script writes row to Google Sheets
β
Script emails you instantly
β
User sees confirmation
No database. No monthly subscription to pay for. Just free Google spreadsheet.
Here’s how to create one
- Open Google Spreadsheet. File β New β Spreadsheet. Save it.
- Click Extensions β Apps Script β Paste the script given below
- Replace name@email.com in “NOTIFY_EMAIL” at the top.
- Click Deploy β New Deployments
- Select Type (Gear icon) β Web App (Execute as: Me; Access: Anyone)
- Authorize Access β Google verification β Advanced
- Click on “Go to untitled project” β Permissions “Select All” β Continue
- Copy the Web App URL
- Open contact-form.html and paste above Web App URL in “SCRIPT_URL”
- Send test email: Run β Run function β sendTestEmail
Change the Colors to match your website

What It Does Not Cover
File uploads, payments, high volume (thousands/day), or GDPR storage outside Google. For those use-cases, use a proper backend. For a contact or lead capture form on a small business site, this is everything you need, without paying monthly subscription fee.
Google App Script & HTML code
Includes apps-script.js, contact-form.html, and a README with full setup instructions.
/**
* escVelocity Contact Form - Google Apps Script
*
* Receives form submissions, writes to Google Sheets,
* sends email notifications, and rate limits by email.
*
* Setup:
* 1. Open your Google Sheet β Extensions β Apps Script
* 2. Paste this script and configure the constants below
* 3. Deploy as Web App (Execute as: Me, Access: Anyone)
* 4. Run sendTestEmail() once to authorize Gmail permissions
* 5. Copy the Web App URL into your HTML form's fetch() call
*/
// βββ CONFIGURATION βββββββββββββββββββββββββββββββββββββββββββ
const NOTIFY_EMAIL = 'your@email.com'; // Email to receive notifications
const RATE_LIMIT_WINDOW_MINUTES = 60; // Time window for rate limiting
const RATE_LIMIT_MAX_SUBMISSIONS = 5; // Max submissions per email per window
const FORM_NAME = 'Contact Form'; // Used in email subject line
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
function doPost(e) {
try {
var data = JSON.parse(e.postData.contents);
data.phone = data.phone ? data.phone.toString().trim() : '';
if (isRateLimited(data.email)) {
return ContentService
.createTextOutput(JSON.stringify({ result: 'error', message: 'Rate limit exceeded' }))
.setMimeType(ContentService.MimeType.JSON);
}
writeToSheet(data);
sendNotification(data);
return ContentService
.createTextOutput(JSON.stringify({ result: 'success' }))
.setMimeType(ContentService.MimeType.JSON);
} catch(err) {
return ContentService
.createTextOutput(JSON.stringify({ result: 'error', message: err.message }))
.setMimeType(ContentService.MimeType.JSON);
}
}
function writeToSheet(data) {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var newRow = sheet.getLastRow() + 1;
// Timestamp gets its own format, rest forced to plain text to avoid formula errors
sheet.getRange(newRow, 1).setValue(new Date());
sheet.getRange(newRow, 2).setNumberFormat('@').setValue(data.name || '');
sheet.getRange(newRow, 3).setNumberFormat('@').setValue(data.email || '');
sheet.getRange(newRow, 4).setNumberFormat('@').setValue(data.phone || '');
sheet.getRange(newRow, 5).setNumberFormat('@').setValue(data.website || '');
sheet.getRange(newRow, 6).setNumberFormat('@').setValue(data.description || '');
}
function sendNotification(data) {
var subject = 'π₯ New ' + FORM_NAME + ' submission - ' + data.name;
var body = [
'A new form submission just came in.',
'',
'Name: ' + (data.name || '-'),
'Email: ' + (data.email || '-'),
'Phone: ' + (data.phone || '-'),
'Website: ' + (data.website || '-'),
'',
'Message:',
(data.description || '-'),
'',
'Submitted at ' + new Date().toLocaleString()
].join('\n');
MailApp.sendEmail(NOTIFY_EMAIL, subject, body);
}
function isRateLimited(email) {
var cache = CacheService.getScriptCache();
var key = 'rl_' + email.replace(/[^a-zA-Z0-9]/g, '_');
var current = cache.get(key);
var count = current ? parseInt(current) : 0;
if (count >= RATE_LIMIT_MAX_SUBMISSIONS) {
return true;
}
cache.put(key, count + 1, RATE_LIMIT_WINDOW_MINUTES * 60);
return false;
}
/**
* Run this function once manually to authorize Gmail permissions.
* Delete or keep it β it won't affect live form submissions.
*/
function sendTestEmail() {
sendNotification({
name: 'Test User',
email: 'test@example.com',
phone: '+1 555 000 0000',
website: 'https://example.com',
description: 'This is a test submission to verify email notifications are working.'
});
}
Entire source code available on my Github repository: WordPress Contact Form using Google Sheets
If this saved you a monthly subscription fee or a few hours of time, like, repost or comment here and also star the Github repository. Much appreciated.
