Pricing

Send HubSpot Form Submissions to Google Sheets Automatically (Step-by-Step)

docs.google.com/spreadsheetsFile Edit View Insert Format Data Tools Extensions HelpA1ABCcontact_namedeal_stagedeal_value2Alice JohnsonClosed Won$48,5003Bob MartinezNegotiation$32,0004Carol LeeProposal$21,7005David KimQualified$15,2006Emma ParkNew Lead$8,900HubSpot dataSheet2BrookedHubSpotOpen BrookedExtensions → Brooked → Open+ New importSelect objectContactsDealsCompaniesTicketsFilter dealsStageAll stagesOwnerAll ownersClose dateThis quarterImporting deals…Fetching 5 records from HubSpot…Writing to Sheet1!A1Import complete ✓5 records importedAuto-refreshEvery hourEvery dayEvery week
JW
James Whitfield

Learn how to automatically send HubSpot form submissions to Google Sheets. Covers Workflows, Zapier, Apps Script, and third-party connectors, including date conversion and UTM tracking tips.

Every time a visitor fills out your HubSpot form, that submission creates or updates a contact record. Getting those submissions into Google Sheets automatically: with all the fields intact, timestamps readable, and UTM parameters captured: sounds straightforward. In practice, there are several non-obvious constraints that determine which method works for your situation.

This guide covers every viable method, what each one requires, where each one breaks down, and how to handle the two most common data problems: Unix timestamp dates and UTM parameters that go missing.

What Actually Happens When a Form Is Submitted

Understanding the data model prevents a significant amount of confusion.

When someone submits a HubSpot form, HubSpot does two things. It records the form submission event (the raw data entered, tied to the form ID and page URL). It also creates or updates a contact record with the property values from that submission.

This distinction matters because HubSpot Workflows operate on objects (contacts, companies, deals) not on form submission events. You cannot trigger a workflow directly on "a form submission occurred as an event." You trigger it on the contact object that was created or updated as a result of that submission.

This means you are always working with contact properties in Sheets, not raw submission data. If the same contact submits again and changes a field, the workflow fires again but the contact property reflects the latest value, not the original submission.

If you need a true submission-by-submission log (preserving every entry even from repeat submitters), the Workflow approach alone will not give you that. You will need the Forms API or a connector that hooks into the submission event stream.

Method 1: HubSpot Operations Hub Workflow

Requirements: Operations Hub Starter or above (paid add-on or included in some suites)

This is the native method and the most reliable for most teams already on an eligible HubSpot tier.

Setup steps:

  1. Go to Automation > Workflows > Create workflow
  2. Choose "Contact-based" as the object type
  3. Set the enrollment trigger to "Contact submits a form" and select your target form, or use "Form submission" as a contact property filter
  4. Add an action: "Create Google Sheets row"
  5. Connect your Google account when prompted
  6. Select the target spreadsheet and sheet tab
  7. Define your columns and map HubSpot contact properties to each

Key limitations:

The action supports a maximum of 20 columns per row. If you need more fields, you can add a second "Create Google Sheets row" action in the same workflow targeting a different tab, then VLOOKUP or join the data in Sheets.

The workflow fires on the contact object, so repeated submissions from the same contact update the contact's properties but the Sheets action writes a new row each time enrollment is re-triggered. Make sure re-enrollment is turned on for your trigger condition if you want every submission captured.

Dates arrive as Unix timestamps in milliseconds. See the conversion section below.

Method 2: Zapier

Requirements: Zapier free or paid account; HubSpot integration enabled

Zapier connects HubSpot form submissions to Google Sheets without requiring Operations Hub. The trigger available is "New Form Submission" which fires on the submission event rather than the contact object: making it better suited for logging every individual submission including repeat entries from the same contact.

Setup steps:

  1. In Zapier, create a new Zap
  2. Trigger app: HubSpot, Event: New Form Submission
  3. Connect your HubSpot account and select the form
  4. Action app: Google Sheets, Event: Create Spreadsheet Row
  5. Map the HubSpot fields to your sheet columns

Key limitations:

Zapier's free plan limits you to 100 tasks per month. On paid plans, costs scale with volume. For high-traffic forms this becomes expensive quickly.

Zapier polls for new submissions on a schedule (every 5-15 minutes on most plans) rather than receiving real-time webhooks. For time-sensitive workflows, this lag matters.

The field mapping in Zapier is position-based (column A, B, C) rather than header-name-based, which behaves differently from the HubSpot native action but avoids the header-mismatch problem described in the one-cell troubleshooting guide.

UTM parameters are available in Zapier only if they are stored as contact properties. The raw submission data does not always include them.

Method 3: Apps Script + HubSpot Forms API

Requirements: Google account; HubSpot API key or private app token; basic familiarity with JavaScript

This method gives you the most control and is free beyond development time. It is the right choice for teams with a developer available who want to avoid per-task costs.

How it works:

You write a Google Apps Script that calls the HubSpot Forms Submissions API endpoint at regular intervals (using a time-driven trigger) or receives a webhook from HubSpot. The script parses the response and writes new submissions to the sheet.

The API endpoint for form submissions is:

Code
GET https://api.hubapi.com/form-integrations/v1/submissions/forms/{formGuid}

This returns every submission for a given form, paginated. The script tracks the last-seen submission timestamp and only writes new rows.

Sample logic flow:

  1. Fetch submissions since the last stored timestamp
  2. For each submission, extract the field values from the values array (each entry has a name and value key)
  3. Map field names to column headers in your sheet
  4. Append the row
  5. Store the new high-water timestamp in PropertiesService

Key limitations:

HubSpot's Forms API returns raw submission data (the values as entered) which is closer to true submission logging than the Workflow approach. However, UTM and analytics data are stored at the contact level, not in the submission object, so you still need a separate API call to retrieve them.

Rate limits: the HubSpot API allows 100 requests per 10 seconds for standard accounts. A naive polling script for multiple forms can hit this quickly.

Method 4: Third-Party Connectors

Requirements: Subscription to the connector tool

Several dedicated HubSpot-to-Sheets connectors handle the authentication, pagination, rate limiting, and schema management that make the DIY approaches tedious.

Options worth evaluating:

Coefficient syncs HubSpot data including form submissions to Sheets on a schedule, with refresh options as frequent as hourly. It installs as a Sheets add-on and supports live lookups.

Supermetrics is strong for marketing data and supports HubSpot form data alongside ad platform data, useful if you are combining sources.

Dataslayer focuses on marketing analytics and supports HubSpot as a source with Google Sheets as a destination.

Brooked.io is built specifically for HubSpot-to-Sheets sync and handles the timestamp conversion, property mapping, and incremental sync automatically.

These connectors are the practical choice for non-developers or teams that need reliable, maintained syncs without ongoing script maintenance.

How to Capture UTM Parameters

UTM parameters from form submissions are not stored in the submission event itself. HubSpot captures them as contact properties when a cookie-tracked visitor completes a form. The relevant properties are:

Property LabelInternal NameWhat It Stores
Original Sourcehs_analytics_sourceThe source category (Organic Search, Social, etc.)
First Touch URLhs_analytics_first_urlFull URL of first page visited
Latest Sourcehs_latest_sourceSource of the most recent session
Latest Source Data 1hs_latest_source_data_1UTM campaign (from most recent session)
Latest Source Data 2hs_latest_source_data_2UTM content (from most recent session)

For the original UTM parameters from the very first visit, HubSpot stores them in hs_analytics_first_url as part of the URL string. You will need to parse the UTM values out of that URL in your Sheet using a formula or in your script.

There is no separate "utm_source at time of this submission" property in standard HubSpot. If you need submission-level UTM attribution, you need to either pass UTMs as hidden form fields (which writes them as custom contact properties) or use a custom approach with cookies and a webhook.

How to Convert HubSpot Date Timestamps

HubSpot sends date and datetime property values as Unix timestamps in milliseconds. A value like 1718053200000 represents a specific date and time.

Google Sheets date system counts days since December 30, 1899. The conversion requires:

  1. Converting milliseconds to seconds (divide by 1000)
  2. Converting seconds to days (divide by 86400, which is 60 × 60 × 24)
  3. Adding the date offset between the Unix epoch (January 1, 1970) and the Sheets epoch

The formula, assuming your timestamp is in cell A1:

Code
=A1/1000/60/60/24 + DATE(1970,1,1)

After entering this formula, format the result cell as a date (Format > Number > Date) to see a human-readable date instead of a decimal number.

For datetime values where you also want the time component:

Code
=A1/1000/60/60/24 + DATE(1970,1,1)

The same formula works: format the cell as "Date time" to show both.

If you are writing values via Apps Script rather than formulas, convert before writing:

javascript
function msToSheetsDate(ms) {
  // Sheets date serial: days since Dec 30 1899
  return (ms / 1000 / 86400) + 25569;
}

Then write the result as a plain number and format the column as a date in your sheet.

Method Comparison Table

MethodCostReal-TimeMax ColumnsUTM SupportDev RequiredBest For
HubSpot WorkflowOps Hub Starter+Near real-time20 per actionContact properties onlyNoTeams with Ops Hub
ZapierFree–paid (per task)~5-15 min lagUnlimitedContact properties onlyNoLow-volume, no dev
Apps Script + APIFreePolling intervalUnlimitedContact + submissionYesDev teams, high volume
Third-party connectorSubscriptionVaries (hourly+)UnlimitedDepends on connectorNoNon-dev, reliable sync

Troubleshooting

Submissions are not appearing in Sheets

Check that the workflow enrollment trigger is set to fire on re-enrollment if you expect multiple submissions from the same contact. A contact already in the workflow will not re-trigger unless re-enrollment is enabled.

Date values appear as large numbers

Apply the timestamp conversion formula above. The raw number is correct. It just needs to be converted to a date format Sheets can display.

UTM parameters are blank

UTM data is only captured if the contact visited via a tracked URL before submitting. If they submitted directly (no prior tracked visit) or used a browser extension that strips UTM parameters, the properties will be empty. Also confirm you are using the correct internal property names listed in the UTM section above.

The workflow fires but writes wrong data to columns

See the companion article on the header-matching problem. This is the single most common cause of data writing to the wrong column.

Zapier is missing some submissions

Zapier polls on a schedule. Submissions that occur between polls are caught on the next poll. They are not lost. However, if your account hits task limits, some submissions may not be processed.

Bottom Line

For most HubSpot teams, the Workflow method covers 80% of needs with zero coding required: as long as you have Operations Hub and stay within the 20-column limit. For submission-level logging, UTM attribution, or higher column counts, either the Forms API with Apps Script or a dedicated connector is the right path. Whichever method you use, apply the timestamp conversion formula immediately so your date data is usable from day one.

Automate Your Form Submission Sync

Setting up and maintaining a reliable HubSpot-to-Sheets form sync is more involved than it first appears. Brooked.io handles the connection, timestamp conversion, and incremental updates automatically, so you get clean, current data in Sheets without maintaining scripts or monitoring Zap task counts.

Related articles:

Troubleshooting quick reference

Frequently asked questions

Does this work with HubSpot free forms?

The Forms API works regardless of your HubSpot tier: all HubSpot accounts can access form submission data via API. The Workflow method requires Operations Hub Starter or above. Zapier and third-party connectors work with any tier.

Can I log every submission including repeat contacts?

Yes, but not with the default Workflow trigger alone. You need to enable re-enrollment on the trigger condition, or use Zapier or the Forms API which fire on each submission event rather than contact enrollment.

Will this capture submissions from embedded forms on non-HubSpot pages?

Yes, as long as the HubSpot tracking code is on the page. The form must be a HubSpot-hosted form (not just a look-alike). Custom-built forms that post to HubSpot via the Forms Submission API will also appear in submission logs.

Can I include file upload fields from forms?

File upload properties store a URL to the file in HubSpot's file manager, not the file itself. The URL can be written to Sheets, but the actual file is not transferred.

Is there a way to retroactively import old form submissions?

The HubSpot Forms API supports pagination through historical submissions. An Apps Script can page through all submissions and write them to Sheets in a one-time import. Third-party connectors typically offer a historical sync option when first connecting.

What happens if my Sheet runs out of rows?

Google Sheets supports up to 10 million cells. For a sheet with 20 columns, that is 500,000 rows. If you are approaching that limit, archive older rows to a separate tab or sheet and keep only recent submissions active.

Ready to connect your data to Google Sheets?

Brooked's free tier covers 100 imports per month with AI Analyst included, no credit card required.

Install Brooked free →

Also in CRM

More CRM guides

CRM

How to Connect HubSpot to Google Sheets

5 ways to sync HubSpot with Google Sheets: official add-on vs Workflows vs Apps Script vs Brooked. Two-way sync, scheduled refresh, no Operations Hub.

JW
James Whitfield
Read
CRM

How to Connect Salesforce to Google Sheets

Every realistic way to get Salesforce data into Google Sheets in 2026, manual report exports, Data Loader, Zapier, Apps Script with SOQL, and a no-code connector, plus the API-edition gotcha that blocks most setups.

BT
Brooked Team
Read

Get your spreadsheet hours back

Brooked sets up in seconds. Your team is querying live data before lunch.

Get started free