Google Forms Webhook: Send Every Response to Any URL (Free Script, 2026)

Google Forms has no webhook setting. You can send responses to a Google Sheet and get an email notification, and that is where the built-in options end. If you want every new response pushed to your CRM, your help desk, a Slack channel or your own app the second someone hits Submit, you have to add that part yourself.

The good news: it takes about 20 lines of Apps Script, it is free, and it runs on Google’s servers so there is nothing to keep online. This guide gives you the script, the exact trigger setup, the limits you will actually hit, and what to put on the receiving end.

The short answer

Method Cost Setup time Best for
Apps Script + form submit trigger Free 10 minutes Sending the raw response to any URL you control
Automation platform polling the response Sheet Usually priced per task 5 minutes People who never want to see code
Chat app incoming webhook (Slack, Discord) Free 10 minutes A notification in a channel and nothing else

The Apps Script route is the only one that is a real webhook: an HTTP POST fired by the submission itself, with a payload you design. The rest of this post is about that route.

Step 1: Add the script to your form

Open the form in edit mode, click the three-dot menu in the top right, and choose Apps Script. Delete whatever is in the editor and paste this:

const WEBHOOK_URL = 'https://your-endpoint.example.com/webhook/form';

function onFormSubmit(e) {
  const answers = {};
  e.response.getItemResponses().forEach(function (item) {
    answers[item.getItem().getTitle()] = item.getResponse();
  });

  const payload = {
    formId: e.source.getId(),
    formTitle: e.source.getTitle(),
    responseId: e.response.getId(),
    submittedAt: e.response.getTimestamp().toISOString(),
    email: e.response.getRespondentEmail(),
    answers: answers
  };

  const res = UrlFetchApp.fetch(WEBHOOK_URL, {
    method: 'post',
    contentType: 'application/json',
    payload: JSON.stringify(payload),
    muteHttpExceptions: true
  });

  if (res.getResponseCode() >= 300) {
    console.error('Webhook failed: ' + res.getResponseCode());
  }
}

Replace the URL on the first line with your own endpoint and save. The email field is only filled in if the form collects email addresses. Otherwise it arrives as an empty string.

Step 2: Create the trigger

Pasting the function is not enough. Naming it onFormSubmit does not make it run. Google only fires it if you add an installable trigger:

  1. In the Apps Script editor, click the clock icon in the left sidebar (Triggers).
  2. Click Add Trigger.
  3. Function to run: onFormSubmit.
  4. Event source: From form.
  5. Event type: On form submit.
  6. Save, then approve the permission prompt. The script asks for access to the form and permission to connect to an external service. Both are needed.

Submit a test response. If nothing arrives, open Executions in the same sidebar. Every run is listed there with its status and any logged error, which is the fastest way to find out whether the problem is the script or your endpoint.

What your endpoint receives

{
  "formId": "1FAIpQLSd...",
  "formTitle": "Contact us",
  "responseId": "2_ABaOnud...",
  "submittedAt": "2026-10-01T09:14:22.000Z",
  "email": "[email protected]",
  "answers": {
    "Your name": "Sam Lee",
    "What do you need help with?": "Billing",
    "Tell us more": "I was charged twice in September."
  }
}

Answers are keyed by question title, so renaming a question changes the key your receiver sees. If something downstream depends on stable keys, swap item.getItem().getTitle() for item.getItem().getId(). Checkbox questions arrive as an array, and file upload questions arrive as Google Drive file IDs, not the files themselves.

The limits you will actually hit

These are Google’s published Apps Script quotas, checked on October 1, 2026:

Quota Free Gmail account Google Workspace account
URL Fetch calls 20,000 per day 100,000 per day
Total trigger runtime 90 minutes per day 6 hours per day
Runtime per execution 6 minutes 6 minutes

For almost every form, the daily fetch quota is not the problem. A form would need 20,000 submissions in a day to reach it. The limit that bites is the one not in the table: there are no retries. If your endpoint is down or slow when someone submits, that webhook call is gone. The response is still safe in the form and in the linked Sheet, but your CRM never hears about it.

Two cheap fixes:

  • Retry in the script. Wrap the fetch in a loop of three attempts with Utilities.sleep(2000) between them. This covers short blips.
  • Reconcile from the Sheet. Send responseId in every payload (the script above does) and have the receiver store it. A nightly check against the response Sheet then shows exactly which submissions never arrived.

Lock it down

A webhook URL is a public door. Anyone who learns it can post fake submissions. Add a shared secret as a header and have the receiver reject anything without it:

headers: { 'X-Form-Secret': 'a-long-random-string' }

Put that line inside the options object next to method. Use an HTTPS endpoint so the secret and the answers are encrypted in transit, and remember that anyone with edit access to the form can open the script and read the secret.

Test the receiving end before you connect the form

Most “my Google Forms webhook is not working” problems are on the receiving side: a wrong path, a URL that only works in test mode, an endpoint that expects form-encoded data and gets JSON. Debugging that by filling in the form over and over is slow.

Send the sample payload above straight to your endpoint first. A browser-based webhook tester lets you paste the URL, pick POST, add the JSON body and your secret header, and see the status code and response in one click. If your endpoint does not allow cross-origin requests, the browser will hide the response, and the same test with curl -X POST -H "Content-Type: application/json" -d @payload.json from a terminal gets around that. Once the endpoint returns a 200 for the sample, plug the URL into the script.

Where to send it

A chat channel. Slack and Discord both give you an incoming webhook URL, but they expect their own message format, not arbitrary JSON. We covered that setup in how to connect Google Forms to Discord.

A hosted automation platform. Zapier and Make both offer a “catch webhook” step. They are quick to set up and usually priced by the number of tasks or operations you run, so a busy form costs more every month it grows.

n8n. n8n is an open-source workflow tool with a Webhook node that accepts exactly the POST this script sends. From there one submission can create a ticket, add a row to a database, send a templated email and post to Slack in a single workflow, with no per-task fee because you run it yourself. One thing trips people up: the Webhook node shows a test URL and a production URL. The test URL only listens while you have the editor open and are waiting for a test event. Put the production URL in your script and make sure the workflow is active.

The catch with running n8n yourself is that Google has to be able to reach it, which means a public HTTPS address. The old way to get one is a weekend project: provision a server, install the runtime, configure nginx, set up SSL, open the firewall, add a process manager, point a domain and DNS at it. If you would rather skip that, managed n8n hosting on InstaPods is $7/mo flat for 2 vCPU, 2GB RAM and 25GB of storage, with HTTPS and a managed PostgreSQL database included, and it is live in about 60 seconds. You get a webhook URL you can paste into the script straight away, and the price does not move with the number of submissions.

When a webhook is not what you need

A webhook moves data out of the form after someone submits. It cannot change what happens inside the form while they are filling it in. If what you need is a countdown timer, a limit on attempts, or proctoring for a quiz or exam, that is a job for a form add-on, and it is what ExtendedForms was built for. The two work fine together: ExtendedForms controls the session, and the script above ships the finished response wherever you want it.

Frequently asked questions

Does Google Forms support webhooks natively?

No. As of October 2026 there is no webhook option in the Google Forms settings. The supported way to get one is an Apps Script function attached to an installable “On form submit” trigger, as shown above.

Is a Google Forms webhook free?

Yes. Apps Script is included with every Google account. A free Gmail account can make 20,000 outbound URL Fetch calls per day and a Google Workspace account 100,000, which is far more than a typical form needs.

Why is my onFormSubmit function not running?

Almost always because the trigger is missing. The function name alone does nothing. Add an installable trigger with event source “From form” and event type “On form submit”, approve the permissions, then check the Executions panel for errors.

Can I send only some of the answers?

Yes. Build the answers object from just the questions you want, or filter by question title inside the loop. Sending less is also the safer default if the form collects personal data.

Will it send responses that were submitted before I added the script?

No. The trigger only fires for new submissions. To backfill older responses, loop over FormApp.getActiveForm().getResponses() once from the editor and post each one.

Leave a Comment

Your email address will not be published.