Skip to content

End-to-End Analytics for Google Ads + HubSpot

Business Case

A performance marketer needs to understand which keywords and search queries in Google Ads generate actual sales, not just clicks and leads. A standard Google Ads report shows conversions based on the ad account's own data, but doesn't show how many of those conversions turned into paid deals in HubSpot. This calls for end-to-end analytics with ROMI calculated for each keyword.

Connections

This report requires two connections:

  1. Google Ads — the google-ads connector

    • Permissions: google_ads_read
    • Status: active
  2. HubSpot CRM — the hubspot connector

    • Permissions: read access to deals and contacts
    • Status: active

Matching scheme (matching preset):

  • Matching Preset: Google Ads + HubSpot (GCLID) — a built-in LightLead preset
  • Matching key: GCLID (Google Click ID) — a unique click identifier passed via UTM tags or Google Ads auto-tagging
  • Mapping: google_ads.gclidhubspot.contact.gclidhubspot.deal.associated_contact.gclid

You need to set up a hidden "GCLID" field at the contact level in HubSpot. When a form on your website is submitted, the GCLID from the URL must be saved into this field. Without this, the matching won't work — check this setting before running the report.

Report Settings

  • Name: End-to-End Analytics Google Ads → HubSpot
  • Report type: attribution report with GCLID matching
  • Matching Preset: Google Ads + HubSpot (GCLID)
  • Data sources: all google-ads and hubspot connections
  • Date range: last_30_days
  • Attribution window: 30 days (click → lead) + 90 days (lead → deal)
  • Grouping: by campaign (campaign_name), ad group (ad_group_name), keyword (keyword_name), pipeline (pipeline_name)
  • Sorting: by ROMI descending
  • Timezone: Europe/Moscow

Parameters

json
{
  "date_range": "last_30_days",
  "matching_preset": "google-ads-hubspot-gclid",
  "attribution_window": {
    "click_to_lead_days": 30,
    "lead_to_deal_days": 90
  },
  "group_by": ["campaign_name", "ad_group_name", "keyword_name", "pipeline_name"],
  "sort": { "field": "romi", "direction": "desc" },
  "timezone": "Europe/Moscow",
  "limit": 2000
}

The lead_to_deal_days: 90 attribution window suits B2B companies with a long sales cycle. For B2C, reduce it to 30. Grouping by keyword (keyword_name) is the main value of this setup, since it lets you make decisions at the search-query level, not just the campaign level.

Metrics

MetricTypeSourceDescription
costcurrencyGoogle AdsBudget spent
impressionsnumberGoogle AdsImpressions
clicksnumberGoogle AdsClicks
ctrpercentGoogle AdsClick-through rate
cpccurrencyGoogle AdsAverage cost per click
leadsnumberHubSpotNumber of leads (contacts with GCLID)
mqlnumberHubSpotMarketing Qualified Leads
sqlnumberHubSpotSales Qualified Leads
deals_creatednumberHubSpotDeals created
deals_wonnumberHubSpotDeals won
revenuecurrencyHubSpotRevenue from won deals

Calculated metrics:

json
{
  "calculated_metrics": [
    {
      "name": "cpl",
      "formula": "IF([leads] > 0, [cost] / [leads], 0)",
      "type": "money",
      "description": "Cost per lead"
    },
    {
      "name": "cpl_sql",
      "formula": "IF([sql] > 0, [cost] / [sql], 0)",
      "type": "money",
      "description": "Cost per qualified lead"
    },
    {
      "name": "romi",
      "formula": "IF([cost] > 0, ([revenue] - [cost]) / [cost] * 100, 0)",
      "type": "percent",
      "description": "ROMI: return on ad investment"
    },
    {
      "name": "lead_to_mql_cr",
      "formula": "IF([leads] > 0, ROUND([mql] / [leads] * 100, 1), 0)",
      "type": "percent",
      "description": "Lead → MQL conversion rate"
    },
    {
      "name": "revenue_per_keyword",
      "formula": "IF([deals_won] > 0, [revenue] / [deals_won], 0)",
      "type": "money",
      "description": "Average revenue per deal by keyword"
    }
  ]
}

ROMI > 0% means the ad spend is paying off. ROMI > 100% means every ruble spent on ads brings back a ruble of profit. ROMI by keyword lets you find "golden" queries with ROMI > 500% and raise bids on them.

Filters

json
{
  "logic": "AND",
  "conditions": [
    {
      "field": "cost",
      "operator": "GREATER_THAN",
      "value": 300
    },
    {
      "field": "clicks",
      "operator": "GREATER_THAN",
      "value": 20
    },
    {
      "field": "impressions",
      "operator": "GREATER_THAN",
      "value": 500
    }
  ]
}
  • cost > 300 — minimum budget for statistical significance
  • clicks > 20 — minimum clicks for a keyword
  • impressions > 500 — sufficient reach

To analyze a specific keyword match type: match_type EQUAL "exact" or match_type EQUAL "phrase".

Write Mode

Mode: Replace

Data from two sources and the GCLID matching require a full rewrite for consistency. In addition, the 90-day attribution window means that data from the last 3 months can change as new deals close.

Schedule

json
{
  "schedule": {
    "enabled": true,
    "interval": "daily",
    "time": "07:30",
    "timezone": "Europe/Moscow",
    "retry_on_failure": true,
    "max_retries": 3
  }
}

Daily at 7:30. Keep in mind that ROMI for the last 1-2 weeks will look understated (deals haven't closed yet). For decision-making, rely on data over last_90_days or at least last_30_days, ignoring the most recent week.

Expected Result

  • Status: completed or partial
  • Row count: campaigns × groups × keywords (typically 100-800 rows depending on the size of the keyword set)
  • End-to-end metrics: ROMI for each keyword — you can see which queries pay off and which run at a loss
  • Lead qualification: CPL vs. CPL(SQL) — a query can produce cheap leads that fail to qualify, visible from a low MQL/SQL conversion rate
  • Actions: queries with ROMI < 0% get excluded or have bids lowered; queries with ROMI > 300% get bids raised and are moved into a separate campaign

Screenshot

[Screenshot]

[Screenshot]

Maintained by the LightLead Documentation Team · Last verified: 2026-07-26