Learn how to sync HubSpot deals and pipeline data to Google Sheets using the API, Zapier, or Apps Script, and why Operations Hub won't do what you think.
HubSpot does not have a native Google Sheets connector. Operations Hub Data Sync handles CRM object sync between platforms, not spreadsheet exports. To get live deal and pipeline data into Sheets, you need the HubSpot API, a third-party connector, or an automation tool like Zapier. Each method has real tradeoffs around row limits, update frequency, and cost.
The Operations Hub Misconception
If you searched "HubSpot Google Sheets sync" hoping Operations Hub would solve it, you are not alone.
"I upgraded to Operations Hub thinking it would push deals into Sheets automatically. It doesn't do that at all." (HubSpot Community forum, 2024)
Operations Hub Data Sync is designed to sync CRM data between HubSpot and other CRMs or business apps (Salesforce, Pipedrive, etc.). It is not a spreadsheet connector. There is no toggle that says "send deals to Google Sheets." This confusion is compounded by HubSpot's own marketing language around "data sync," which sounds like it should cover everything.
What Operations Hub does include (on Professional and Enterprise tiers) is programmable automation and Webhooks. Those can be used to build a custom pipeline, but you are still writing the integration logic yourself.
Why This Is Harder Than It Looks
Several HubSpot-specific behaviors make this integration non-trivial even once you know the right approach.
API v3 cursor-based pagination. The Deals API returns a maximum of 100 records per request. To retrieve your full deal list, you must implement pagination using the after cursor returned in each response. Scripts that skip this will silently truncate results at 100 rows, and most templates you find online skip it.
Internal stage names differ per pipeline. HubSpot deal stages have human-readable labels (e.g., "Proposal Sent") but the API returns internal IDs like appointmentscheduled or a UUID like 7439291. These IDs are unique per portal and per pipeline. A stage called "Closed Won" in your pipeline has a different ID than the same label in another portal. You must fetch your pipeline definitions separately to map IDs to labels.
Hidden velocity properties. HubSpot tracks hs_v2_date_entered_[stage_id] and hs_v2_date_exited_[stage_id] for every stage in every pipeline. These properties let you calculate time-in-stage and deal velocity, but they are not returned by default. You must explicitly request them by constructing property names from your pipeline stage IDs, which again requires fetching the pipeline definition first. The older hs_date_entered_* properties (v1) still exist but are deprecated.
Export row caps by tier. Manual exports from the HubSpot UI cap at 1,000 rows on Starter, 10,000 on Professional, and higher on Enterprise. If you have more deals than that, you will not get a complete dataset from the UI export workflow.
Method 1: Manual Export
Best for: One-time analysis, teams with fewer than 1,000 deals, no technical resources.
In HubSpot, navigate to CRM > Deals. Apply any filters you need, then click Actions > Export. Select the properties you want and choose CSV format. Import that file into Google Sheets.
Limitations:
- Data is static the moment you export it
- Row caps apply (1,000 on Starter)
- Repeating this process manually is not sustainable for reporting
This method works well for an ad-hoc analysis but is not a sync solution.
Method 2: Zapier
Best for: Non-technical teams who need basic automation on smaller deal volumes.
Zapier supports HubSpot as a trigger source. You can set up a Zap that fires when a deal is created or updated, then appends a row to a Google Sheet.
Limitations that matter:
- New deals only by default. Zapier's "Deal Updated" trigger fires on changes but does not backfill existing deals. You start with an empty sheet and populate it going forward.
- No bulk historical sync. There is no built-in way to dump your existing 5,000 deals into Sheets via Zapier without building a workaround.
- Task limits. Depending on your Zapier plan, high-volume pipelines can exhaust monthly task quotas quickly, especially if deals update frequently.
- Stage ID problem persists. Zapier surfaces the internal stage ID, not the label. You either need a lookup step or accept cryptic stage values in your sheet.
- Update lag. Zapier's polling-based triggers check for changes every 1–15 minutes depending on plan tier. This is not real-time.
Zapier is the right choice if you need something running today with no code and your volume is manageable.
Method 3: Apps Script + HubSpot API v3
Best for: Technical teams who want full control, historical data, and custom property mapping.
Google Apps Script runs inside Google Sheets and can call the HubSpot API directly. This gives you complete control over which properties you pull, how often, and how data is structured.
Step 1: Create a HubSpot Private App
In HubSpot, go to Settings > Integrations > Private Apps. Create a new app and grant it crm.objects.deals.read scope. Copy the access token.
Step 2: Fetch pipeline definitions
Before syncing deals, fetch your pipelines to build a stage ID-to-label map:
function getPipelineStageMap() {
const token = PropertiesService.getScriptProperties().getProperty('HUBSPOT_TOKEN');
const url = 'https://api.hubapi.com/crm/v3/pipelines/deals';
const response = UrlFetchApp.fetch(url, {
headers: { Authorization: 'Bearer ' + token }
});
const pipelines = JSON.parse(response.getContentText()).results;
const stageMap = {};
pipelines.forEach(pipeline => {
pipeline.stages.forEach(stage => {
stageMap[stage.id] = stage.label;
});
});
return stageMap;
}Step 3: Fetch all deals with cursor pagination
function getAllDeals(stageMap) {
const token = PropertiesService.getScriptProperties().getProperty('HUBSPOT_TOKEN');
const properties = [
'dealname', 'amount', 'dealstage', 'pipeline',
'closedate', 'createdate', 'hubspot_owner_id',
'hs_deal_stage_probability'
].join(',');
let deals = [];
let after = null;
do {
let url = `https://api.hubapi.com/crm/v3/objects/deals?limit=100&properties=${properties}`;
if (after) url += `&after=${after}`;
const response = UrlFetchApp.fetch(url, {
headers: { Authorization: 'Bearer ' + token }
});
const data = JSON.parse(response.getContentText());
data.results.forEach(deal => {
const props = deal.properties;
deals.push([
deal.id,
props.dealname,
props.amount,
stageMap[props.dealstage] || props.dealstage, // resolve stage label
props.pipeline,
props.closedate,
props.createdate
]);
});
after = data.paging?.next?.after || null;
} while (after);
return deals;
}Step 4: Write to Sheets and schedule
Use SpreadsheetApp.getActiveSheet().getRange(...).setValues(deals) to write results. Set a time-driven trigger to run this function every hour or every night.
Key considerations:
- Store the access token in Script Properties, not hardcoded
- HubSpot's API rate limit is 100 requests per 10 seconds for private apps
- Add
Utilities.sleep(200)between paginated requests if you have thousands of deals - To include velocity properties, add
hs_v2_date_entered_[stageId]to your properties array. You must construct these strings from the stage IDs you fetched in Step 2
Method 4: Third-Party Connectors
Best for: Teams who want reliable scheduled syncs without writing or maintaining code.
Several tools specialize in pulling HubSpot data into Google Sheets with scheduled refresh, historical backfill, and proper field mapping:
- brooked.io, purpose-built for CRM and marketing platform data into Sheets, handles pagination and stage label mapping automatically
- Coefficient, browser extension that syncs HubSpot into Sheets with a UI-driven field selector
- Superjoin, similar connector with scheduled refresh options
- Databox, dashboard-focused, not Sheets native
Third-party connectors solve the pagination and stage ID problems for you. The tradeoff is a monthly subscription and dependency on the vendor's uptime and API quota management.
Comparison Table
| Method | Real-Time | Historical Data | No-Code | Cost | Row Limit |
|---|---|---|---|---|---|
| Manual Export | No | Yes (capped) | Yes | Free | Tier-dependent |
| Zapier | Near (poll) | No (forward only) | Yes | $20–$69/mo | Task quota |
| Apps Script + API | Scheduled | Yes (full) | No | Free | None |
| brooked.io | Scheduled | Yes (full) | Yes | Subscription | None |
| Coefficient | Scheduled | Yes (full) | Yes | Subscription | None |
Troubleshooting Common Issues
"My sheet only has 100 rows even though I have thousands of deals." Your script is not paginating. Check that you are reading the paging.next.after cursor from each API response and looping until it is null or absent.
**"Deal stages are showing as random strings like 7439291."** These are internal stage IDs. Fetch your pipeline definitions first and build a lookup map. See the Apps Script example above.
**"The hs_v2_date_entered_* properties are returning null."** You must include the full property name with the stage ID appended. For example: hs_v2_date_entered_7439291. The stage ID must match your specific portal's pipeline configuration. It is not a generic name.
"My Zapier Zap is using too many tasks." Consider batching with a scheduled Apps Script instead, or upgrade your Zapier plan. Alternatively, use a trigger that fires only on stage changes rather than any deal update.
"Operations Hub keeps being suggested as the fix." Operations Hub is not the answer here. Its Data Sync feature does not write to Google Sheets. If a sales rep or consultant suggests this, they are likely confusing it with a different feature.
Bottom Line
Syncing HubSpot deals to Google Sheets is not a one-click process. The right method depends on your deal volume, technical resources, and how frequently you need data refreshed. Manual export works for one-off analysis. Zapier works for forward-looking automation at low volume. Apps Script with the HubSpot API v3 gives you complete control but requires handling pagination and stage ID mapping yourself. Third-party connectors handle the hard parts automatically at a subscription cost.
The two most common failure modes are scripts that truncate at 100 rows (missing pagination) and sheets full of cryptic stage IDs (missing the pipeline definition lookup). Solve those two problems first and the rest is straightforward.
Get Live HubSpot Data in Sheets Without the Headaches
brooked.io handles HubSpot API pagination, stage label mapping, and scheduled sync automatically, no Apps Script maintenance required. Connect your HubSpot account, select your deal properties, and have a live-updating sheet in minutes.
Related reading:
- Why Your GA4 Numbers in Google Sheets Don't Match GA4
- GA4 Custom Dimensions in Google Sheets: The customEvent API Format Nobody Documents
Troubleshooting quick reference
Frequently asked questions
Does HubSpot have a native Google Sheets integration?
No. As of mid-2025, HubSpot does not offer a built-in Google Sheets connector. The closest native options are manual exports (capped by tier) or using the API via Apps Script. Third-party tools like brooked.io fill this gap.
What does Operations Hub Data Sync actually do?
Operations Hub Data Sync syncs CRM contacts, companies, and deals between HubSpot and other supported CRMs and business apps. It is not a spreadsheet tool. Supported integrations are listed in HubSpot's App Marketplace and do not include Google Sheets as a sync destination.
How do I get deal stage labels instead of internal IDs?
Call the /crm/v3/pipelines/deals endpoint to retrieve all pipelines and their stages. Each stage object contains an id (the internal identifier) and a label (the human-readable name). Build a dictionary mapping IDs to labels and use it when writing stage data to your sheet.
How often can I refresh HubSpot data in Google Sheets?
With Apps Script triggers, you can refresh as frequently as every minute, though running a full deal sync that often is rarely practical for large datasets. Most teams use hourly or daily refresh schedules. HubSpot's API rate limit (100 requests per 10 seconds for private apps) is the practical ceiling, not Google's trigger frequency.
What are the `hs_v2_date_entered` and `hs_v2_date_exited` properties?
These are HubSpot's stage timestamp properties. For each pipeline stage, HubSpot automatically records when a deal entered and exited that stage. The property names follow the pattern hs_v2_date_entered_[stageId]. They are essential for calculating deal velocity and time-in-stage metrics but are not returned in default API responses. You must request them explicitly by name.
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 →

