Bubble forms to Google Sheets
One API Connector action posts your form inputs as JSON, and each submission lands as a row in Google Sheets or Excel - no automation tool in between.
Bubble workflows are good at moving data around inside Bubble - the trouble starts when the people who work the leads live in a spreadsheet. The usual fix is an automation tool with per-task pricing sitting between your app and the sheet. SheetLink Forms removes that layer: your workflow POSTs JSON to a permanent endpoint at https://sheetlinkforms.com/f/{token}, and an asynchronous worker writes the row directly to the Google Sheets API or Microsoft Graph, with retries at 5 minutes, 30 minutes, and 2 hours and a delivery log for every attempt.
The integration uses the API Connector - Bubble's own free plugin - so there is nothing exotic to install. One call definition, used as an action in any workflow, covers every form in your app that you want to land in the same sheet; add more forms with more endpoints. JSON keys that match your column headers map automatically, and the endpoint answers {"ok":true,"id":"..."} so your workflow can react to the result.
SheetLink Forms is free during beta and invite-gated - join the waitlist. The how it works page shows the full path from POST to row.
The best path for Bubble
The best path in Bubble is an API Connector POST with a JSON body. Configure one call: method POST, your endpoint as the URL, a Content-Type: application/json header, and a JSON body whose keys are your sheet's column headers with Bubble dynamic values (<name>, <email>) as the values. Use it as an Action so it slots into any workflow - typically "When Button Send is clicked".
Because the call runs from Bubble's workflow engine rather than as a bare browser request, there is no CORS to configure and nothing to embed in the page. Include "_slhp": "" in the body - that is the honeypot field, present and empty - and keep payloads under the 256KB cap, which is roomy for any form Bubble will produce.
1. Connect a sheet and get your endpoint
Sign in and connect Google Sheets with one-click OAuth - the drive.file scope restricts access to sheets you pick in the Google picker or create in the product, never the rest of your Drive - or connect Excel Online and choose a table, whose columns define the row shape. You get a permanent endpoint at https://sheetlinkforms.com/f/{token}. On an empty sheet the header row is seeded on first delivery.
2. Install the API Connector
In your Bubble editor, open Plugins and install the API Connector if it is not already there - it is Bubble's own free plugin. Add a new API named "SheetLink Forms" and create a call configured as in the snippet: POST, JSON body type, Content-Type: application/json, and "Use as: Action".
3. Shape the JSON body around your columns
Make each JSON key a column header from your sheet - matching is case and punctuation insensitive, so Email, email, and E-mail all land in the same column, and explicit per-field mapping is available in the dashboard when names truly differ. Wrap each value in angle brackets (<name>) so Bubble exposes it as a dynamic parameter. Keep "_slhp": "" in the body: it is the spam honeypot, and it should stay empty.
4. Initialize the call
Press "Initialize call" with sample values. The endpoint responds {"ok": true, "id": "..."}, and the row for your sample lands in the sheet - a live smoke test and schema detection in one step. Bubble now knows ok and id as result fields your workflows can read.
5. Wire it into your form workflow
On your page, build the form from Input elements and a Button. In the workflow "When Button Send is clicked", add the SheetLink Forms action and map each parameter to the matching Input's value. Follow with your normal UX steps - reset inputs, show an alert - and branch on the action's ok result if you want distinct success and failure paths.
6. Test and read the delivery log
Run the app in preview, submit, and confirm the row appended under your header. The dashboard's delivery log records each attempt; the asynchronous worker retries at 5 minutes, 30 minutes, and 2 hours if the spreadsheet API is briefly unavailable, and a formula-injection guard escapes leading =, +, -, and @ in cell values. Response codes worth knowing (404 unknown token, 410 paused, 413 too large, 429 rate limited) are in the docs.
FAQ
Do I need a paid Bubble plan or a paid connector?
No paid connector - the API Connector is Bubble's own free plugin, and SheetLink Forms is free during beta (invite required - join the waitlist). There are no per-submission or per-task fees on the SheetLink side, ever; planned post-beta pricing is a free tier around 50 submissions/month and a paid plan around $15-19/mo, unmetered.
Why include "_slhp": "" in the JSON body?
That is the honeypot field - it should be present and empty. Bots that spray endpoints tend to fill every field, and a filled honeypot is the one signal that marks a submission as spam outright. Everything else suspicious is quarantined for one-click review in the dashboard, never silently dropped, so a legitimate lead is never lost to a heuristic.
Can my workflow react to the response?
Yes. After you initialize the call, Bubble exposes the response fields - ok (boolean) and id (text) - as "Result of step X". Branch on ok for success and failure paths, or store id in your Bubble database if you want to reconcile app records against the delivery log later.
Can I capture UTM parameters and ad click IDs from Bubble?
Yes, with Bubble's own tools: use "Get data from page URL" to read utm_source, utm_campaign, gclid, fbclid, and friends, and map them into the JSON body as additional keys with matching column headers. (The sl.js embed that automates this on static sites is aimed at plain HTML pages; inside Bubble, passing URL parameters through the workflow is the cleaner route.)
What happens if a submission fails to deliver?
Acceptance and delivery are separate. Once the endpoint answers {"ok":true,...}, an asynchronous worker owns delivery and retries at 5 minutes, 30 minutes, and 2 hours, with every attempt visible in the delivery log. If the POST itself is rejected (rate limit, oversized payload), your workflow sees the error immediately and can retry or alert. You can watch the happy path end to end on the live demo.
Also on: Webflow · Framer · Squarespace