Paid Advertising and PPC Management
Data Analysis and Reporting in Marketing

HubSpot to Google Sheets for RevOps: Lifecycle Velocity (2026)

Adela
May 7, 2026
HubSpot to Google Sheets for RevOps: Lifecycle Velocity Guide 2026

If you run RevOps for a B2B company, HubSpot has the data. What it does not have is a clean answer to the two questions that actually drive pipeline decisions: where are deals sitting too long at each stage, and how well are leads converting between lifecycle stages. The native HubSpot reports tell you how many deals you have and what revenue closed. They do not surface the bottlenecks or the funnel leaks.

This guide is for getting that visibility into Google Sheets, where you can join HubSpot data with paid media, slice it by source and owner, share it without giving everyone CRM access, and most importantly build two dashboards that the rest of the connector tutorials skip: a stage transition velocity tracker and a lifecycle conversion waterfall.

Three methods to get the data flowing (manual, Apps Script, scheduled connector), the HubSpot-specific gotchas at each layer, and the field-by-field setup for both dashboards. The methods are means. The dashboards are the point. We covered the same workflow for paid platforms in our LinkedIn Ads to Google Sheets guide and Meta Ads to Google Sheets guide; this one extends to CRM-specific reporting. For the analytics data-integrity layer (sampling, thresholding, cardinality), see our GA4 to Google Sheets guide; for the SEO trust layer (anonymized queries, 16-month wall), our Search Console to Google Sheets guide.

Where lifecycle and velocity data lives in HubSpot

Before you pick a method, you need to know where HubSpot keeps the timestamps you will be querying. The lifecycle stage dates are scattered across multiple date entered/exited properties — one pair per stage, on every Contact and Deal record. The current stage is one field; the transition history is in those date properties.

For Contacts (used for the lifecycle waterfall):

  • hs_lifecyclestage_lead_date
  • hs_lifecyclestage_marketingqualifiedlead_date
  • hs_lifecyclestage_salesqualifiedlead_date
  • hs_lifecyclestage_opportunity_date
  • hs_lifecyclestage_customer_date
  • hs_lifecyclestage_evangelist_date, hs_lifecyclestage_other_date
  • lifecyclestage — current value only, not history

For Deals (used for the stage velocity dashboard):

  • hs_v2_date_entered_<stagename> and hs_v2_date_exited_<stagename> — one pair per pipeline stage
  • dealstage — current stage only
  • createdate, closedate — for total deal age

Velocity is the gap between two timestamps. "Days from MQL to SQL" is hs_lifecyclestage_salesqualifiedlead_date minus hs_lifecyclestage_marketingqualifiedlead_date. "Days stuck at Presentation Scheduled" is TODAY() minus hs_v2_date_entered_presentationscheduled, only when hs_v2_date_exited_presentationscheduled is empty.

If you do not pull those date columns, you cannot build velocity reports. The default HubSpot list view does not show them; you have to customize the view (UI), select them by name (API), or pick them in the field picker (connector). Each method's section below addresses how.

Method 1: Manual CSV export from the HubSpot UI

For one-off audits and executive snapshots. Fast, free, and useless for recurring velocity tracking, because by next Monday the data is stale.

Setup: in HubSpot, open the list view (Contacts or Deals), apply filters, click Actions → Export. Critically — and this is where most exports go wrong for RevOps — explicitly select the lifecycle date properties in the property picker. If you only check default columns, you get the current stage with no transition dates. The export emails you a CSV download link; import into Sheets via File → Import.

HubSpot-specific gotchas at this layer:

  • Export row caps scale with Hub tier. Marketing Hub Starter is capped at 10,000 rows per export; Professional at 50,000; Enterprise higher but still bounded. For portals with hundreds of thousands of contacts, you'll need to export in segments by lifecycle stage or date range, then concatenate in Sheets.
  • HubSpot dates export as ISO 8601 strings, not as Sheets-native dates. You may need a DATEVALUE wrapper if you want to do date arithmetic.
  • Custom properties export by their internal name, not their UI label. If your team calls a property "First meeting date" but the internal name is prop_first_meeting_date_c, the CSV column header will be the internal name.

When this method fits: quarterly board snapshot, audit before a CRM cleanup, one-time export for an annual review deck.

When it does not: any dashboard where the data needs to refresh daily.

Method 2: Apps Script with the HubSpot CRM API

The free path when an in-house RevOps team wants daily velocity refresh without a connector subscription. Setup is 30–60 minutes the first time; 10–15 minutes per additional object type. Maintenance ownership is yours.

Step 1 — Create a Private App in HubSpot. Settings → Integrations → Private Apps → Create a private app. Minimum scopes for the velocity dashboard: crm.objects.contacts.read, crm.objects.deals.read, crm.objects.companies.read. Add crm.lists.read if you want to filter by HubSpot saved lists. Create app, and HubSpot generates a long-lived access token. Store it securely.

Step 2 — In your Sheet, open Extensions → Apps Script and paste the script below. It pulls deals along with the stage transition dates that power the velocity dashboard.

function exportHubSpotDealsForVelocity() {
 const HUBSPOT_TOKEN = 'YOUR_PRIVATE_APP_TOKEN';
 // Pull deal data plus stage-transition timestamps. Adjust stage names to your pipeline.
 const properties = [
   'dealname','amount','pipeline','dealstage','createdate','closedate','hubspot_owner_id',
   'hs_v2_date_entered_qualifiedtobuy','hs_v2_date_exited_qualifiedtobuy',
   'hs_v2_date_entered_presentationscheduled','hs_v2_date_exited_presentationscheduled',
   'hs_v2_date_entered_contractsent','hs_v2_date_exited_contractsent',
   'hs_v2_date_entered_closedwon','hs_v2_date_entered_closedlost'
 ].join(',');

 const baseUrl = 'https://api.hubapi.com/crm/v3/objects/deals';
 const options = {
   method: 'GET',
   headers: { 'Authorization': 'Bearer ' + HUBSPOT_TOKEN, 'Content-Type': 'application/json' },
   muteHttpExceptions: true
 };

 const sheet = SpreadsheetApp.getActiveSheet();
 sheet.clear();
 sheet.appendRow(['Deal ID','Deal Name','Amount','Pipeline','Current Stage','Created','Closed',
                  'Entered Qualified','Exited Qualified',
                  'Entered Presentation','Exited Presentation',
                  'Entered Contract Sent','Closed Won']);

 let after = null;
 do {
   const url = baseUrl + '?limit=100&properties=' + properties + (after ? '&after=' + after : '');
   const response = UrlFetchApp.fetch(url, options);
   const data = JSON.parse(response.getContentText());

   (data.results || []).forEach(row => {
     const p = row.properties || {};
     sheet.appendRow([
       row.id, p.dealname, p.amount, p.pipeline, p.dealstage,
       p.createdate, p.closedate,
       p.hs_v2_date_entered_qualifiedtobuy, p.hs_v2_date_exited_qualifiedtobuy,
       p.hs_v2_date_entered_presentationscheduled, p.hs_v2_date_exited_presentationscheduled,
       p.hs_v2_date_entered_contractsent, p.hs_v2_date_entered_closedwon
     ]);
   });

   after = data.paging && data.paging.next ? data.paging.next.after : null;
 } while (after);
}

Step 3 — Schedule it. Apps Script → Triggers (clock icon) → Add Trigger → run exportHubSpotDealsForVelocity daily at 6 AM. Your velocity sheet refreshes before the team's stand-up.

HubSpot-specific gotchas at the API layer:

  • Cursor-based pagination, not offset. HubSpot returns paging.next.after; pass it back as after= on the next request. Don't try limit=10000 in one shot — HubSpot caps at 100 per page for the v3 API.
  • Rate limits scale with Hub tier. Free Marketing Hub: 100 req/10s and 250,000/day. Professional: 150/10s and 500,000/day. Enterprise: 200/10s. If you query 50K+ deals daily, watch the daily quota.
  • Custom properties are queried by internal name, not label. Find internal names at Settings → Properties → click the property → "Internal name" field.
  • Pipeline stage names are pipeline-specific. qualifiedtobuy is a default of the standard sales pipeline. Custom pipelines have custom stage internal names — visible at Settings → Pipelines → click the pipeline → each stage shows its internal name. Update the properties list in the script accordingly.
  • Apps Script execution limit. Free Workspace: 6 minutes; paid Workspace: 30 minutes. Pulling 50K+ deals with many properties can hit this. Solutions: pull deals in segments by createdate ranges, or move to Method 3.

The HubSpot CRM API v3 reference documents every endpoint and property filter syntax.

Skip the Apps Script maintenance

Dataslayer connects HubSpot to Google Sheets, Looker Studio (now Data Studio), BigQuery and Power BI on a schedule. Multi-portal support, pre-built field mappings for contacts, deals, companies, tickets, email campaigns, and line items.

Try Dataslayer Free

Method 3: Scheduled no-code connector

The path for RevOps teams with multiple portals (agencies), no appetite for code maintenance, or a need to access derived dimensions the bare API does not expose cleanly.

Dataslayer connects HubSpot to Sheets via OAuth in under 10 minutes. From Google Sheets: Extensions → Add-ons → Get add-ons → install Dataslayer. Then Extensions → Dataslayer → Launch Sidebar, choose HubSpot, authorize via the HubSpot OAuth flow, pick the portal (or portals), pick objects, pick fields, set the date range, click Run.

Why a connector specifically wins for HubSpot RevOps work:

  • Multi-portal native. Agencies juggling 5–20 client portals query them in the same workbook with a portal column to differentiate. Dataslayer batches API calls across portals respecting per-portal rate limits.
  • Custom property surface. Dataslayer's field picker exposes the full HubSpot schema (the Dataslayer schema covers 934 dimensions and 249 metrics) categorized by lifecycle dates, marketing attribution, geo, content, and pipeline. Finding hs_lifecyclestage_marketingqualifiedlead_date does not require remembering the internal name.
  • Pre-computed derived fields. Days-since-created, days-in-current-stage, days-since-last-engagement — calculated by the connector so you don't write Sheets formulas for the basics.
  • Multi-pipeline awareness. If you have separate pipelines for new business and renewals, Dataslayer exposes stage internal names per pipeline; you don't have to lookup each one.

Costs: Free for 1 connector and 1 user. Starter $35/month annual covers 3 connectors and 1 destination. Pro tier needed for >5 portals or >100 accounts.

The HubSpot gotcha that NO method fixes: the Operations Hub Data Sync feature is NOT a Google Sheets connector. Data Sync keeps HubSpot in sync with other CRMs (Salesforce, Pipedrive). For HubSpot → Sheets, the three methods in this post are the practical paths. Don't waste time looking for a Data Sync → Sheets bridge; it doesn't exist.

The 2 RevOps dashboards worth building

Once the data is flowing into Sheets (any of the 3 methods), here are the two reports that change how RevOps operates. Neither is something HubSpot's native UI surfaces well, and both require the lifecycle date properties.

Dashboard 1: Stage transition velocity tracker

The question: how long are deals sitting at each pipeline stage, and where is the bottleneck?

Columns to pull from HubSpot:

  • Deal ID, Deal Name, Amount, Deal Owner, Current Stage, Created Date, Close Date
  • hs_v2_date_entered_qualifiedtobuy, hs_v2_date_exited_qualifiedtobuy
  • hs_v2_date_entered_presentationscheduled, hs_v2_date_exited_presentationscheduled
  • hs_v2_date_entered_contractsent, hs_v2_date_exited_contractsent
  • hs_v2_date_entered_closedwon

Calculations in Sheets:

  • Days in current stage: =IF(<exited_current_stage> = "", TODAY() - <entered_current_stage>, "")
  • Days at Qualified: =<exited_qualified> - <entered_qualified>
  • Median days at each stage per quarter using =QUARTILE() or =MEDIAN() filtered by quarter

Pivot layout:

  • Rows: pipeline stage
  • Columns: quarter (or month)
  • Values: AVG(days in stage)
  • Conditional formatting: highlight cells where velocity slowed >20% versus prior quarter

What it surfaces that the HubSpot UI does not:

  • Deals stuck at "Presentation Scheduled" for >45 days — sales team isn't following up
  • Median "Qualified → Contract Sent" doubled this quarter — qualification rubric changed and no one caught it
  • Velocity by Deal Owner reveals who is slow vs. fast, and where coaching helps

Dashboard 2: Lifecycle conversion waterfall

The question: of contacts who became MQL last quarter, how many converted to SQL, then Opportunity, then Customer — and at what rate by acquisition source?

Columns to pull from HubSpot:

  • Contact ID, Email, Original Source, Original Source Drill-down 1, Latest Converting Campaign
  • hs_lifecyclestage_lead_date
  • hs_lifecyclestage_marketingqualifiedlead_date
  • hs_lifecyclestage_salesqualifiedlead_date
  • hs_lifecyclestage_opportunity_date
  • hs_lifecyclestage_customer_date

Calculations in Sheets:

  • MQL → SQL conversion rate (quarterly): ratio of COUNTIFS where SQL date in quarter to COUNTIFS where MQL date in quarter
  • Avg days MQL → SQL: average of SQL_date - MQL_date for contacts where both are populated
  • Cohort retention: group contacts by quarter they hit MQL, then track what percent made it to SQL, Opportunity, Customer in subsequent quarters

Pivot layout:

  • Rows: original source (Organic, Paid, Direct, Referral, Email)
  • Columns: lifecycle stage transitions (MQL→SQL, SQL→Opp, Opp→Customer)
  • Values: conversion rate percentage

What this dashboard answers that the QBR otherwise can't:

  • Which acquisition channels produce the fastest movers (not just the most leads)
  • Where the funnel actually leaks — is it marketing-to-sales handoff (MQL→SQL) or sales execution (Opp→Customer)?
  • Whether changes to MQL scoring criteria last quarter actually improved downstream conversion or just changed the volume

For dashboard layout best practices once you have these reports running, see our Marketing Dashboard KPIs Playbook. For cross-source attribution (joining HubSpot data with paid media to validate which campaigns produce the deals that close), see Marketing Attribution Is Broken in 2026.

Comparing the three methods

Aspect Manual CSV Apps Script Scheduled connector
Setup time 5 min 30-60 min Under 10 min
Cost Free Free From $35/mo
Lifecycle date properties Must pick manually Must request by internal name In field picker, categorized
Scheduled refresh No Yes (Apps Script triggers) Yes (built in)
Multi-portal One export per portal One script per portal Native, one query
Custom properties By internal name By internal name By label, auto-mapped
Code maintenance None Owner Vendor

The decision typically comes down to portal count and team appetite for code. One portal and you have a developer on the team: Apps Script. Multiple portals or RevOps is your full-time job: scheduled connector. Single export needed for an executive deck this Friday: manual CSV.

HubSpot quirks worth knowing

Five HubSpot-specific behaviors that affect every method and every dashboard you build:

  • Custom property internal names ≠ labels. Always check Settings → Properties → "Internal name" before writing API calls or filters.
  • Pipeline stage names are pipeline-specific. qualifiedtobuy is the default sales pipeline's "Qualified to Buy" stage. Custom pipelines have different internal names per stage. Settings → Pipelines → each stage shows its internal name.
  • Marketing Hub Free caps API at 100 req/10s and 250K/day. Plan multi-portal queries to stay under it.
  • Operations Hub Data Sync is not a Google Sheets connector. It's HubSpot ↔ other CRMs. Don't waste time looking for a Sheets bridge there.
  • Multi-touch attribution reports compute server-side. The API exposes the inputs (campaigns, sources, touchpoints), but for the final calculated multi-touch attribution, the HubSpot UI report remains the source of truth. Your Sheet can replicate first-touch and last-touch, not multi-touch.

Common errors and how to read them

A few errors come up enough that recognizing them saves time.

401 unauthorized: the access token is invalid or has been revoked. Regenerate the private app token in HubSpot Settings → Integrations → Private Apps and update your script or connector configuration.

403 forbidden with scope error: the private app does not have the scope needed to read the requested object. Edit the private app, add the missing CRM scope, save, and retry. The HubSpot Private Apps documentation lists every scope and what it grants.

429 too many requests: you hit the HubSpot API rate limit. Wait and retry; or stagger your refresh schedule across multiple times of day if running multiple portal queries.

Empty results array with no error: the filter you applied excluded all records. Common cause: filtering by a property name that does not exist or by a date field with an unexpected format (HubSpot dates are milliseconds since epoch; quoting them as strings does not match).

FAQ

How do I find the internal name of a HubSpot custom property?
Settings → Properties → pick the object (Contact, Deal, etc.) → click the property name → the "Internal name" field shows the value to use in your API call or Sheets connector. Internal names often have a _c suffix and may use underscores; labels are human-readable.

Do I need Marketing Hub Professional or above to use the CRM API?
No. The HubSpot CRM API is available on every tier including Marketing Hub Free. The constraint is rate limits: Free gives 100 requests per 10 seconds and 250,000 per day; Professional gives 150/10s and 500K/day; Enterprise gives 200/10s. For a typical Sheets reporting setup pulling deals and contacts daily, the Free tier is enough.

Why are my lifecycle stage dates blank for some contacts?
HubSpot only records the date when a contact actually moves through that stage. A contact created directly as an MQL skips the hs_lifecyclestage_lead_date (it's blank), but has hs_lifecyclestage_marketingqualifiedlead_date populated. Your dashboard formulas need to handle blanks (use IF(date<>"", ..., "") patterns).

Is HubSpot Operations Hub Data Sync the same as a Google Sheets connector?
No. Operations Hub Data Sync keeps HubSpot in sync with other CRMs (Salesforce, Pipedrive, Microsoft Dynamics), not with Google Sheets. For HubSpot → Sheets, the three methods in this post are the practical paths; there is no native HubSpot feature for that direction.

Should I use a Private App or OAuth for my Apps Script integration?
Private apps for internal use (your own portal, your own team, your own Sheet). OAuth only if you are building software that other HubSpot accounts will install. For "I want to pull my own portal's data into my own Sheet," always use a private app — it's simpler and avoids the OAuth app review process.

Can I push data from Sheets back into HubSpot?
Yes, but it requires write scopes on the private app and POST/PATCH requests instead of GET. The CRM API supports bulk updates of contacts and deals. The post here covers the read direction (HubSpot → Sheets) which is what RevOps reporting needs; write-back patterns (Sheets → HubSpot for bulk property updates) are a separate workflow.

How does the Looker Studio rebrand to Data Studio affect HubSpot reporting in Sheets?
It does not. The rebrand was a naming change, not a product change. Existing HubSpot connections to what is now called Data Studio keep working. We covered the rebrand specifics in our Looker Studio to Data Studio rebrand post.

Conclusion

The three methods to pull HubSpot data into Google Sheets exist on a spectrum: manual CSV for one-off audits, Apps Script for free automation with code maintenance, scheduled connector for hands-off multi-portal RevOps reporting. The right method depends on portal count, team appetite for code, and refresh frequency.

But the methods are means, not the end. The deliverable is the two dashboards: a stage transition velocity tracker that surfaces where pipeline is stuck, and a lifecycle conversion waterfall that quantifies the funnel and the source-to-revenue path. Both require pulling the lifecycle date properties — and most teams skip those columns in their first export, then wonder why their HubSpot → Sheets workflow doesn't change anything about how RevOps makes decisions.

Pull the date columns. Build the dashboards. The connector is one weekend's setup; the velocity insight is something you'll reference every Monday for the next year. Start a free Dataslayer trial if you want to skip the API maintenance and get to the dashboards faster.

HOW CAN WE HELP?

RELATED POST

Marketing Data Warehouse in 2026: When You Need One and Which Path Fits

HubSpot to Google Sheets for RevOps: Lifecycle Velocity (2026)

The Marketing Dashboard KPIs Playbook for 2026 (and Anti-Patterns to Cut)

Our Partners