B2B Lead Generation Analytics
Comprehensive analytics setup for B2B lead generation, including form tracking, lead scoring, and offline conversion imports.
B2B Analytics Challenges
Unique Characteristics
| B2B Challenge | Analytics Implication | |---------------|----------------------| | Long sales cycles | Extended attribution windows | | Multiple stakeholders | Account-level tracking needed | | Offline conversions | CRM integration required | | High-value deals | Lead quality > quantity | | Complex funnels | Multi-touch attribution |
The B2B Funnel
┌─────────────────────────────────────────────────────────┐
│ B2B Lead Funnel │
├─────────────────────────────────────────────────────────┤
│ │
│ Website → Lead → MQL → SQL → Won │
│ Visitor Gen Qualified Sales Deal│
│ │
│ Tracked in: │
│ GA4 GA4/CRM CRM CRM CRM │
│ │ │ │ │ │
│ └──────────┴────────────┴─────────┘ │
│ Import back to Google Ads │
│ │
└─────────────────────────────────────────────────────────┘
Form Tracking
Standard Form Events
// Form viewed
dataLayer.push({
event: 'form_view',
form_name: 'contact_sales',
form_location: 'pricing_page',
form_type: 'lead_gen'
});
// Form started (first interaction)
dataLayer.push({
event: 'form_start',
form_name: 'contact_sales',
form_location: 'pricing_page'
});
// Form submitted
dataLayer.push({
event: 'generate_lead',
form_name: 'contact_sales',
form_location: 'pricing_page',
lead_type: 'sales_inquiry',
company_size: '51-200',
industry: 'technology',
value: 500 // Estimated lead value
});
Field-Level Tracking
Track form abandonment by monitoring field completion:
// Track field completion
function trackFieldComplete(formName, fieldName) {
dataLayer.push({
event: 'form_field_complete',
form_name: formName,
field_name: fieldName,
field_order: getFieldOrder(fieldName)
});
}
// Attach to form fields
document.querySelectorAll('#contact-form input').forEach(input => {
input.addEventListener('blur', function() {
if (this.value) {
trackFieldComplete('contact_sales', this.name);
}
});
});
Lead Quality Signals
Capture data that indicates lead quality:
dataLayer.push({
event: 'generate_lead',
form_name: 'demo_request',
// Standard info
lead_type: 'demo_request',
// Quality signals
company_size: '201-500',
industry: 'financial_services',
job_title_category: 'executive', // C-level, VP, Director, Manager, IC
use_case: 'enterprise_deployment',
budget_range: '100k_plus',
timeline: 'this_quarter',
// Lead scoring input
lead_score: calculateLeadScore(),
lead_grade: 'A' // Based on company fit
});
Lead Scoring
Scoring Framework
function calculateLeadScore() {
let score = 0;
// Behavioral scoring
score += pageviews > 5 ? 10 : pageviews * 2;
score += visitedPricing ? 15 : 0;
score += visitedCaseStudies ? 10 : 0;
score += downloadedContent ? 20 : 0;
score += watchedDemo ? 25 : 0;
// Form-based scoring
score += getCompanySizeScore(companySize);
score += getIndustryScore(industry);
score += getTitleScore(jobTitle);
score += getTimelineScore(timeline);
return score;
}
function getCompanySizeScore(size) {
const scores = {
'1-10': 5,
'11-50': 10,
'51-200': 15,
'201-500': 25,
'501-1000': 30,
'1000+': 35
};
return scores[size] || 0;
}
Pass Score to GA4
// Include score with lead event
dataLayer.push({
event: 'generate_lead',
lead_score: 75,
lead_grade: 'A',
// ... other params
});
// Also set as user property for future analysis
dataLayer.push({
event: 'set_user_properties',
user_properties: {
lead_score: 75,
lead_grade: 'A',
lead_status: 'new'
}
});
Content Engagement
Content Asset Tracking
// Content download
dataLayer.push({
event: 'file_download',
file_name: 'enterprise-security-guide.pdf',
content_type: 'whitepaper',
content_topic: 'security',
content_stage: 'consideration' // awareness, consideration, decision
});
// Webinar registration
dataLayer.push({
event: 'webinar_registration',
webinar_name: 'Data Strategy 2024',
webinar_topic: 'analytics',
webinar_date: '2024-02-15'
});
// Video engagement
dataLayer.push({
event: 'video_complete',
video_title: 'Product Demo',
video_duration: 420,
video_type: 'product_demo'
});
Engagement Scoring
// Track engagement depth
const engagementScore = {
pageviews: 0,
downloads: 0,
videoWatched: 0,
minutesOnSite: 0
};
// Update on events
dataLayer.push = (function(original) {
return function(data) {
if (data.event === 'page_view') engagementScore.pageviews++;
if (data.event === 'file_download') engagementScore.downloads++;
if (data.event === 'video_complete') engagementScore.videoWatched++;
// Send engagement tier
if (shouldUpdateTier()) {
original.call(this, {
event: 'engagement_tier_change',
engagement_tier: calculateTier(engagementScore)
});
}
return original.apply(this, arguments);
};
})(dataLayer.push);
Offline Conversion Import
Why Import Offline Conversions?
Ad Click → Lead → MQL → SQL → Opportunity → Closed Won
│ │ │
│ └─── Tracked in GA4 ────────────────┘
│ │
└──── Click ID (gclid) links them ───────────┘
Google Ads Offline Import
- Capture GCLID at form submission:
// Get GCLID from URL or cookie
function getGclid() {
// Check URL first
const urlParams = new URLSearchParams(window.location.search);
let gclid = urlParams.get('gclid');
// Fall back to cookie
if (!gclid) {
const cookies = document.cookie.split(';');
for (let cookie of cookies) {
if (cookie.trim().startsWith('_gcl_aw=')) {
gclid = cookie.split('.').pop();
break;
}
}
}
return gclid;
}
// Include in form submission
dataLayer.push({
event: 'generate_lead',
gclid: getGclid(),
lead_id: generatedLeadId,
// ... other params
});
-
Store in CRM with lead record
-
Import conversions when status changes:
# Google Ads offline conversion format
Parameters:TimeZone=America/Los_Angeles
Google Click ID,Conversion Name,Conversion Time,Conversion Value,Conversion Currency
abc123,SQL,2024-01-15 14:30:00,1000,USD
def456,Closed Won,2024-01-20 09:15:00,50000,USD
GA4 Offline Events (Measurement Protocol)
// Server-side: Import CRM events to GA4
const ga4Event = {
client_id: 'stored_client_id', // Captured at lead time
user_id: 'lead_123',
events: [{
name: 'crm_stage_change',
params: {
stage: 'sql',
lead_id: 'lead_123',
opportunity_value: 50000,
days_since_lead: 14
}
}]
};
// Send via Measurement Protocol
fetch(`https://www.google-analytics.com/mp/collect?measurement_id=${GA4_ID}&api_secret=${SECRET}`, {
method: 'POST',
body: JSON.stringify(ga4Event)
});
Account-Based Tracking
Company Identification
// If using Clearbit, 6sense, or similar
if (window.clearbit) {
clearbit.reveal(function(error, company) {
if (company) {
dataLayer.push({
event: 'company_identified',
company_name: company.name,
company_domain: company.domain,
company_industry: company.category.industry,
company_size: company.metrics.employees,
company_revenue: company.metrics.estimatedAnnualRevenue
});
}
});
}
Account-Level Events
// Track at account level
dataLayer.push({
event: 'page_view',
// Standard page view params...
// Account-level dimensions
account_id: identifiedCompanyId || 'anonymous',
account_name: companyName || 'unknown',
account_tier: getAccountTier(companyRevenue),
is_target_account: isInTargetList(companyDomain)
});
CRM Integration
HubSpot Integration
// Push GA4 client_id to HubSpot
if (window._hsq) {
_hsq.push(['identify', {
ga_client_id: getGA4ClientId()
}]);
}
// Track HubSpot form submissions in GA4
hbspt.forms.create({
// ... form config
onFormSubmit: function($form) {
dataLayer.push({
event: 'generate_lead',
form_name: 'hubspot_contact',
form_id: formId
});
}
});
Salesforce Integration
Sync key fields for attribution:
| GA4 Data | Salesforce Field | |----------|------------------| | client_id | GA4_Client_ID__c | | gclid | GCLID__c | | source | Lead_Source_Detail__c | | medium | Lead_Medium__c | | campaign | Lead_Campaign__c |
Attribution for B2B
Extended Windows
Configure for longer B2B cycles:
// Set extended attribution in GA4 Admin
// Admin → Attribution Settings → Lookback window: 90 days
Multi-Touch Analysis
-- BigQuery: Touchpoint analysis
WITH touchpoints AS (
SELECT
user_pseudo_id,
traffic_source.source,
traffic_source.medium,
traffic_source.campaign,
event_timestamp,
ROW_NUMBER() OVER (PARTITION BY user_pseudo_id ORDER BY event_timestamp) as touch_order
FROM `project.analytics.events_*`
WHERE event_name = 'page_view'
AND traffic_source.source IS NOT NULL
),
conversions AS (
SELECT
user_pseudo_id,
MIN(event_timestamp) as conversion_time
FROM `project.analytics.events_*`
WHERE event_name = 'generate_lead'
GROUP BY 1
)
SELECT
t.source,
t.medium,
t.touch_order,
COUNT(*) as touchpoints,
COUNT(DISTINCT c.user_pseudo_id) as conversions
FROM touchpoints t
LEFT JOIN conversions c ON t.user_pseudo_id = c.user_pseudo_id
AND t.event_timestamp < c.conversion_time
GROUP BY 1, 2, 3
ORDER BY touch_order, conversions DESC
Reporting Dashboards
B2B Lead Dashboard
| Section | Metrics | |---------|---------| | Lead Volume | Total leads, by source, by form | | Lead Quality | Score distribution, by grade | | Conversion Funnel | Stage conversion rates | | Content Performance | Downloads, engagement by asset | | Pipeline Attribution | Opportunity value by source |
Sales & Marketing Alignment
| Marketing View | Sales View | |----------------|------------| | MQLs generated | SQLs accepted | | Cost per lead | Cost per opportunity | | Lead score accuracy | Win rate by lead score | | Content influence | Deal acceleration |
Previous: SaaS Analytics Next: E-commerce Analytics