Using Google Sheets as a Form Backend
Collect form submissions into a Google Sheet using SheetsAPI — no third-party form service required.
Most forms on the web funnel into Typeform, Formspree, or some SaaS that charges per submission and stores your data somewhere you can't see. If you already have a Google Sheet and a GKit account, you can collect form submissions directly into a tab you control — no middleman, no per-submission pricing, no export step when you want to use the data.
How it works
SheetsAPI exposes a POST endpoint that appends a row to any connected sheet:
POST https://sheetsapi.gkit.mreshank.com/api/spreadsheets/{userKey}/{sheetName}
The JSON body should be an object whose keys match your sheet's column headers exactly. If your sheet has columns name, email, and message, send:
{
"name": "Alex",
"email": "alex@example.com",
"message": "Would love a demo."
}SheetsAPI appends that as a new row. The first row of your sheet is always treated as the header — it is never overwritten.
Setting up the sheet
Create a tab in your Google Sheet called ContactForm (or whatever name you prefer). Add a header row with your field names in the first row:
| name | message | submitted_at |
|---|
The submitted_at column is optional — you can populate it from the client by including it in the POST body, or leave it blank and fill it with a Google Sheets formula later (=IF(A2<>"", NOW(), "") in D2, dragged down).
Connect the sheet in your GKit dashboard and note your userKey.
A complete HTML form
Here is a self-contained form with a fetch POST, validation, and success/error states:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Contact</title>
</head>
<body>
<form id="contact-form" novalidate>
<label for="name">Name</label>
<input id="name" name="name" type="text" required />
<label for="email">Email</label>
<input id="email" name="email" type="email" required />
<label for="message">Message</label>
<textarea id="message" name="message" required></textarea>
<button type="submit">Send message</button>
<p id="form-status" aria-live="polite"></p>
</form>
<script>
const ENDPOINT =
"https://sheetsapi.gkit.mreshank.com/api/spreadsheets/YOUR_KEY/ContactForm";
const form = document.getElementById("contact-form");
const status = document.getElementById("form-status");
form.addEventListener("submit", async (e) => {
e.preventDefault();
// Client-side validation
const name = form.name.value.trim();
const email = form.email.value.trim();
const message = form.message.value.trim();
if (!name || !email || !message) {
status.textContent = "Please fill in all fields.";
return;
}
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
status.textContent = "Check your email address and try again.";
return;
}
const button = form.querySelector("button[type=submit]");
button.disabled = true;
status.textContent = "Sending…";
try {
const res = await fetch(ENDPOINT, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
name,
email,
message,
submitted_at: new Date().toISOString(),
}),
});
if (!res.ok) {
throw new Error(`Server error: ${res.status}`);
}
status.textContent = "Message sent. We'll be in touch soon.";
form.reset();
} catch (err) {
status.textContent =
"Something went wrong — please try again or email us directly.";
console.error(err);
} finally {
button.disabled = false;
}
});
</script>
</body>
</html>Replace YOUR_KEY with your userKey from the GKit dashboard.
A note on CORS
SheetsAPI sends Access-Control-Allow-Origin: * on all responses by default, so the fetch above works from any origin — a static site on GitHub Pages, a Webflow embed, or a local HTML file. You do not need to configure anything on your end.
If you add an API key to your endpoint (recommended for forms that should not be publicly readable), pass it in the Authorization header:
headers: {
"Content-Type": "application/json",
"Authorization": "Bearer YOUR_API_KEY",
},API keys are created in the GKit dashboard under SheetsAPI → Keys.
What to do with the submissions
Once the rows are in your sheet, everything Google Sheets can do is available to you: filter by date, sort by email domain, use COUNTIF to count submissions by day, or connect to Google Data Studio for a live dashboard. You can also set up a Google Sheets trigger to send yourself an email when a new row is added — no webhook configuration required on GKit's side.
For a form that receives more than a few hundred submissions a day, check the rate limits guide before going live. SheetsAPI batches writes, but very high volume forms should use a dedicated form service or a database with an async queue.