You have a landing page. You need leads in a spreadsheet. You don't want to pay for Zapier, set up a server, or wire a database.
Good news: Google Sheets + Apps Script can do this for free, in about ten minutes.
Here's the full walkthrough — from blank sheet to working form.
Step 1 – Create the Google Sheet
Make a new sheet to store your leads and add three basic columns: Timestamp, Name, and Phone.
- Open Google Sheets and click Blank to create a new sheet.
- Rename the sheet (for example,
Leads). - In row 1, set your headers:
| A1 | B1 | C1 |
|---|---|---|
| Timestamp | Name | Phone |
You can add more columns later (e.g. Source, Notes). Just remember to also update the script in Step 2.
Step 2 – Add Google Apps Script (with Duplicate Protection)
This script receives submissions from your landing page, checks if the phone number already exists, and only appends a new row if it's unique.
- In your sheet, go to Extensions → Apps Script.
- Delete any starter code in the editor.
- Paste the following script:
function doPost(e) {
try {
const sheet = SpreadsheetApp
.getActiveSpreadsheet()
.getActiveSheet();
const data = JSON.parse(e.postData.contents);
const phone = data.phone.trim();
const phones = sheet
.getRange("C2:C")
.getValues()
.flat();
// Check duplicate by phone number (column C)
if (phones.includes(phone)) {
return ContentService
.createTextOutput(JSON.stringify({
status: "duplicate",
message: "Phone number already exists"
}))
.setMimeType(ContentService.MimeType.JSON);
}
sheet.appendRow([
new Date(),
data.name,
phone
]);
return ContentService
.createTextOutput(JSON.stringify({ status: "success" }))
.setMimeType(ContentService.MimeType.JSON);
} catch (err) {
return ContentService
.createTextOutput(JSON.stringify({
status: "error",
message: err.toString()
}))
.setMimeType(ContentService.MimeType.JSON);
}
}Then click Save (or press Ctrl / ⌘ + S) to save the project.
The script looks down column C (phone numbers) from row 2 onwards and blocks any repeat numbers at the backend. Even if someone bypasses your front-end form, this check still protects your sheet.
Step 3 – Deploy as a Web App
Turn your script into a web endpoint that your landing page can send data to.
- In the Apps Script editor, click Deploy → New deployment.
- Under Select type, choose Web app.
- Set:
- Execute as: Me
- Who has access: Anyone
- Click Deploy and authorize permissions if prompted.
- Copy the generated Web App URL — you'll need it for your form.
Keep this Web App URL safe. You'll paste it into your landing page code as YOUR_SCRIPT_URL. Treat it like a semi-private endpoint — anyone with the URL can submit data to your sheet.
Step 4 – HTML Form for Your Landing Page
Add this simple form and JavaScript to any page — static site, funnel builder, custom app, anything that supports HTML and JS.
Replace YOUR_SCRIPT_URL with the Web App URL you copied in Step 3.
<form id="leadForm">
<input
type="text"
name="name"
placeholder="Your Name"
required
/>
<input
type="tel"
name="phone"
placeholder="Phone Number"
required
/>
<button type="submit">Submit</button>
</form>
<script>
document.getElementById("leadForm")
.addEventListener("submit", function(e) {
e.preventDefault();
const formData = {
name: this.name.value,
phone: this.phone.value
};
fetch("YOUR_SCRIPT_URL", {
method: "POST",
body: JSON.stringify(formData),
headers: {
"Content-Type": "application/json"
}
})
.then(res => res.json())
.then(data => {
if (data.status === "success") {
alert("Thanks! We'll contact you soon.");
this.reset();
} else if (data.status === "duplicate") {
alert("This phone number is already registered.");
} else {
alert("Submission failed. Try again.");
}
})
.catch(err => alert("Error submitting form"));
});
</script>Make sure your page is served over HTTPS (most hosts handle this for you), and that your Web App is deployed with Who has access: Anyone.
Step 5 – Test the Flow
Run through the full journey once to confirm everything is wired up.
- Open your landing page with the form.
- Enter a test name and phone number.
- Click Submit.
Go back to your Google Sheet. Within a moment, you should see a new row with the current timestamp, name, and phone number you entered. If it's there — you're done! 🎉
FAQ & Troubleshooting
My form submits, but nothing shows up in the Google Sheet
Make sure the Web App URL in your HTML exactly matches the one from Apps Script (including https://). Also confirm your deployment is set to Execute as: Me and Who has access: Anyone, then try again.
I changed the script, but the form still behaves like before
After editing the Apps Script, you must deploy again. In the editor, click Deploy → Manage deployments, choose your existing Web App, and deploy a new version so the changes go live.
I'm getting a permissions or "forbidden" error
Double-check that you completed the authorization dialog during deployment, and that Who has access for the Web App is set to Anyone.
How can I see what's going wrong inside the script?
In the Apps Script editor, open Executions in the left sidebar. You'll see recent runs of doPost, any errors, and stack traces that can help you pinpoint what to fix.
Can I capture more fields (email, message, etc.)?
Yes! Add new headers to your sheet (e.g. D1: Email), update the sheet.appendRow(...) line in the script to include the new field, and add the corresponding <input> to your HTML form. Redeploy after saving.
That's it. No backend, no third-party tool, no monthly bill. Just a Google Sheet, a script, and a form.