Skip to main content

Command Palette

Search for a command to run...

How to Build a Contact Form Backend With Google Sheets (No Server Needed)

Updated
5 min read

Every website needs a contact form. But setting one up usually means spinning up a backend, configuring a database, handling email notifications, and deploying somewhere reliable.

What if your contact form could just write directly to a Google Sheet?

With Jsonsheets, you can build a fully functional contact form backend in minutes. Form submissions go straight to a spreadsheet where you or your team can review, respond, and organize them — no server code required.

Why Google Sheets as a Contact Form Backend?

Most contact forms don't need a complex database. You need to:

  • Collect a name, email, and message

  • Store submissions somewhere accessible

  • Let your team review and respond

Google Sheets handles all of this. Your team already knows how to use it. You can sort, filter, add columns for status tracking, and even set up Google Sheets notifications when new rows appear.

Step 1: Create Your Submissions Spreadsheet

Open Google Sheets and create a new spreadsheet called "Contact Form Submissions" with these columns:

nameemailsubjectmessagesubmitted_atstatus
John Doejohn@example.comContact usHi team This is John and i love Jsonsheets.com12-02-2026Replied

Keep the first row as headers — these become your API field names. The submitted_at and status columns are optional but useful for tracking when messages arrived and whether they've been handled.

Step 2: Connect the Sheet to Jsonsheets

  1. Sign in at jsonsheets.com

  2. Click Browse Drive and select your Contact Form Submissions spreadsheet

  3. Jsonsheets creates an API endpoint for your sheet automatically

You'll get an API slug like contact-form-submissions and an endpoint:

POST https://jsonsheets.com/api/v1/contact-form-submissions/Sheet1

Step 3: Create an API Key

Go to the API Keys page in your Jsonsheets dashboard and generate a key. You'll need this to authenticate your form submissions.

Step 4: Build the Contact Form (HTML + JavaScript)

Here's a clean, working contact form you can drop into any website:

<form id="contact-form">
  <input type="text" name="name" placeholder="Your name" required />
  <input type="email" name="email" placeholder="Your email" required />
  <input type="text" name="subject" placeholder="Subject" required />
  <textarea name="message" placeholder="Your message" required></textarea>
  <button type="submit">Send Message</button>
</form>

<script>
document.getElementById("contact-form").addEventListener("submit", async (e) => {
  e.preventDefault();

  const form = e.target;
  const data = {
    name: form.name.value,
    email: form.email.value,
    subject: form.subject.value,
    message: form.message.value,
    submitted_at: new Date().toISOString(),
    status: "new"
  };

  try {
    const response = await fetch(
      "https://jsonsheets.com/api/v1/contact-form-submissions/Sheet1",
      {
        method: "POST",
        headers: {
          "Authorization": "Bearer js_your_api_key_here",
          "Content-Type": "application/json"
        },
        body: JSON.stringify(data)
      }
    );

    if (response.ok) {
      form.reset();
      alert("Message sent! We'll get back to you soon.");
    } else {
      alert("Something went wrong. Please try again.");
    }
  } catch (error) {
    alert("Failed to send message. Please try again later.");
  }
});
</script>

That's it. Every form submission creates a new row in your Google Sheet.

Step 5: Build It in React Instead

If you're working with React, here's a component version:

import { useState } from "react";

function ContactForm() {
  const [sending, setSending] = useState(false);
  const [sent, setSent] = useState(false);

  const handleSubmit = async (e) => {
    e.preventDefault();
    setSending(true);

    const formData = new FormData(e.target);
    const data = {
      name: formData.get("name"),
      email: formData.get("email"),
      subject: formData.get("subject"),
      message: formData.get("message"),
      submitted_at: new Date().toISOString(),
      status: "new"
    };

    try {
      const res = await fetch(
        "https://jsonsheets.com/api/v1/contact-form-submissions/Sheet1",
        {
          method: "POST",
          headers: {
            Authorization: "Bearer js_your_api_key_here",
            "Content-Type": "application/json",
          },
          body: JSON.stringify(data),
        }
      );
      if (res.ok) setSent(true);
    } catch (err) {
      alert("Failed to send. Please try again.");
    } finally {
      setSending(false);
    }
  };

  if (sent) return <p>Thanks! We'll be in touch soon.</p>;

  return (
    <form onSubmit={handleSubmit}>
      <input name="name" placeholder="Your name" required />
      <input name="email" type="email" placeholder="Your email" required />
      <input name="subject" placeholder="Subject" required />
      <textarea name="message" placeholder="Your message" required />
      <button type="submit" disabled={sending}>
        {sending ? "Sending..." : "Send Message"}
      </button>
    </form>
  );
}

Managing Submissions in Google Sheets

Once submissions start coming in, your spreadsheet becomes a simple CRM:

  • Sort by date to see the newest messages first

  • Add a "status" column with values like new, replied, closed

  • Color code rows to prioritize urgent messages

  • Use Google Sheets filters to find messages by email or subject

  • Set up notifications in Google Sheets (Tools > Notification rules) to get an email when new rows are added

Reading Submissions via API

You can also build an admin panel that reads submissions through the API:

Get all new submissions:

GET /api/v1/contact-form-submissions/Sheet1?status=new

Get submissions from a specific person:

GET /api/v1/contact-form-submissions/Sheet1?email=john@example.com

Mark a submission as replied:

curl -X PUT \
  -H "Authorization: Bearer js_your_api_key_here" \
  -H "Content-Type: application/json" \
  -d '{"condition": {"email": "john@example.com"}, "set": {"status": "replied"}}' \
  https://jsonsheets.com/api/v1/contact-form-submissions/Sheet1

Security Considerations

A few things to keep in mind:

  • API key exposure: Your API key will be visible in frontend JavaScript. Jsonsheets API keys are scoped to your account, so only your sheets are accessible. For extra security, create a dedicated API key just for the contact form and revoke it if compromised.

  • Spam protection: Add a honeypot field (a hidden input that bots fill out but humans don't) or integrate with a CAPTCHA service before submitting to the API.

  • Rate limiting: Jsonsheets tracks request usage per account, which naturally limits abuse.

Here's a simple honeypot example:

<!-- Hidden from real users, bots will fill this in -->
<input type="text" name="website" style="display:none" tabindex="-1" />

<script>
// In your submit handler, check:
if (form.website.value !== "") {
  // Bot detected, silently ignore
  return;
}
</script>

Use Cases Beyond Contact Forms

This same pattern works for any kind of form submission:

  • Feedback forms — collect user feedback and feature requests

  • Survey responses — gather answers and analyze them in Sheets

  • Event registration — track signups with name, email, and ticket type

  • Job applications — collect resumes and applicant info

  • Bug reports — let users submit issues directly to a spreadsheet

  • Newsletter signups — store email addresses for your mailing list

Wrapping Up

You don't need a backend server, a database, or a form service subscription to collect contact form submissions. With a Google Sheet and Jsonsheets, you get:

  • A working form backend in under 5 minutes

  • Submissions stored in a spreadsheet your whole team can access

  • Full API access to read, update, and manage submissions

  • Zero infrastructure to maintain

Set up your contact form backend at jsonsheets.com