Cross-Platform Tracking: Unifying Meta, Google & TikTok Data in One Dashboard
Cross-Platform Tracking: Unifying Meta, Google & TikTok Data in One Dashboard
Most performance marketers live in three or more ad platforms simultaneously. Google Ads↗ says ROAS is 4.2x. Meta says it is 3.8x. TikTok says 2.1x. GA4↗ tells a completely different story. When every platform reports different numbers using different attribution models, making accurate budget allocation decisions feels impossible.
The solution is a unified cross-platform tracking system that pulls data from all ad platforms and analytics tools into one consistent view. This guide covers the technical architecture, API integrations, data normalization methods, and dashboard design needed to build a single source of truth for cross-channel performance marketing.
Why This Matters: Without unified tracking, budget allocation is based on each platform's self-reported (and naturally inflated) numbers. Cross-platform unification reveals the true ROAS of each channel, identifies over-credited and under-credited campaigns, and enables data-driven budget shifts that improve overall marketing efficiency by 15-30%.
Table of Contents
- Why Platform-Level Reporting Fails
- Cross-Platform Architecture
- Data Collection: APIs and Connectors
- Data Normalization
- Attribution Alignment
- Building the Unified Dashboard
- Automated Reporting and Alerts
- Implementation Roadmap
- FAQ
Why Platform-Level Reporting Fails
The Self-Attribution Problem
Every ad platform takes credit for conversions that occurred within its attribution window. When a user is exposed to ads on multiple platforms, each one claims the conversion. The result: the sum of platform-reported conversions exceeds your actual total conversions.
Actual orders this month: 1,000
Platform-reported conversions:
├── Google Ads: 650 conversions
├── [Meta Ads](https://www.facebook.com/policies/ads/): 520 conversions
├── [TikTok Ads](https://ads.tiktok.com/help/article/tiktok-advertising-policies-ad-creatives-landing-page): 180 conversions
└── Total claimed: 1,350 (35% inflation)
This is not a tracking error -- it is a fundamental feature of platform attribution. Each platform legitimately contributed to some of those conversions, but they cannot agree on how much credit each deserves.
Different Reporting Standards
Each platform uses different terminology, metrics, and calculations:
| Metric | Google Ads | Meta Ads | TikTok Ads |
|---|---|---|---|
| Impressions | Standard | Standard | Standard |
| Clicks | Clicks (all) | Link clicks or All clicks | Clicks |
| CTR | Clicks / Impressions | Link CTR or CTR (all) | CTR |
| Conversions | User-configurable | Results (configurable) | Conversions |
| Revenue | Conv. value | Purchase value | Value |
| ROAS | Conv. value / Cost | Purchase ROAS | ROAS |
| Attribution | DDA (default) | 7d click / 1d view | 7d click / 1d view |
| Reporting time | Click time | Impression time | Click time |
| Currency | Account currency | Account currency | Account currency |
For a deeper understanding of why platform data differs, see our Tracking Data Discrepancies guide.
Cross-Platform Architecture
The Three-Layer Model
Layer 1: Data Collection
├── Google Ads API → Raw campaign data
├── Meta Marketing API → Raw ad data
├── TikTok Marketing API → Raw campaign data
├── GA4 API → Analytics data (neutral observer)
└── Backend/CRM → Ground truth (orders, revenue)
Layer 2: Data Processing
├── Normalize metrics (clicks, CTR, conversions)
├── Align attribution windows
├── Currency conversion
├── De-duplicate cross-platform conversions
└── Calculate blended metrics
Layer 3: Data Presentation
├── Unified dashboard (Looker Studio, Tableau, custom)
├── Automated reports (weekly, monthly)
├── Alerts (performance anomalies, tracking issues)
└── Budget recommendation engine
Technology Stack Options
| Component | Free Option | Paid Option | Enterprise |
|---|---|---|---|
| Data extraction | Custom scripts (Python) | Funnel.io, Supermetrics | Fivetran, Adverity |
| Data warehouse | Google Sheets, BigQuery (free tier) | BigQuery, Snowflake | Snowflake, Redshift |
| Transformation | SQL views, Python | dbt, Dataform | dbt Cloud |
| Dashboard | Looker Studio | Tableau, Power BI | Looker, Sisense |
| Alerting | Google Apps Script | Custom webhooks | PagerDuty, Datadog |
Data Collection: APIs and Connectors
Google Ads API
# Python: Pull Google Ads campaign data
from google.ads.googleads.client import GoogleAdsClient
client = GoogleAdsClient.load_from_storage("google-ads.yaml")
ga_service = client.get_service("GoogleAdsService")
query = """
SELECT
campaign.name,
campaign.id,
metrics.impressions,
metrics.clicks,
metrics.cost_micros,
metrics.conversions,
metrics.conversions_value,
segments.date
FROM campaign
WHERE segments.date DURING LAST_30_DAYS
ORDER BY metrics.cost_micros DESC
"""
response = ga_service.search_stream(
customer_id="1234567890",
query=query
)
for batch in response:
for row in batch.results:
campaign_data = {
'platform': 'google_ads',
'campaign_name': row.campaign.name,
'campaign_id': str(row.campaign.id),
'date': str(row.segments.date),
'impressions': row.metrics.impressions,
'clicks': row.metrics.clicks,
'cost': row.metrics.cost_micros / 1_000_000, # Convert micros
'conversions': row.metrics.conversions,
'revenue': row.metrics.conversions_value
}
save_to_warehouse(campaign_data)
Meta Marketing API
# Python: Pull Meta Ads campaign data
import requests
access_token = "YOUR_ACCESS_TOKEN"
ad_account_id = "act_1234567890"
url = f"https://graph.facebook.com/v19.0/{ad_account_id}/insights"
params = {
'access_token': access_token,
'fields': 'campaign_name,campaign_id,impressions,clicks,spend,'
'actions,action_values',
'date_preset': 'last_30d',
'level': 'campaign',
'time_increment': 1, # Daily breakdown
'limit': 500
}
response = requests.get(url, params=params)
data = response.json()
for row in data.get('data', []):
# Extract purchase conversions and revenue from actions array
conversions = 0
revenue = 0
for action in row.get('actions', []):
if action['action_type'] == 'purchase':
conversions = int(action['value'])
for action_value in row.get('action_values', []):
if action_value['action_type'] == 'purchase':
revenue = float(action_value['value'])
campaign_data = {
'platform': 'meta_ads',
'campaign_name': row['campaign_name'],
'campaign_id': row['campaign_id'],
'date': row['date_start'],
'impressions': int(row['impressions']),
'clicks': int(row['clicks']), # Note: This is "all clicks"
'cost': float(row['spend']),
'conversions': conversions,
'revenue': revenue
}
save_to_warehouse(campaign_data)
TikTok Marketing API
# Python: Pull TikTok Ads campaign data
import requests
import json
access_token = "YOUR_ACCESS_TOKEN"
advertiser_id = "1234567890"
url = "https://business-api.tiktok.com/open_api/v1.3/report/integrated/get/"
headers = {
'Access-Token': access_token,
'Content-Type': 'application/json'
}
payload = {
'advertiser_id': advertiser_id,
'report_type': 'BASIC',
'dimensions': ['campaign_id', 'stat_time_day'],
'data_level': 'AUCTION_CAMPAIGN',
'metrics': ['campaign_name', 'impressions', 'clicks', 'spend',
'conversion', 'total_complete_payment_value'],
'start_date': '2026-02-09',
'end_date': '2026-03-09',
'page_size': 1000
}
response = requests.get(url, headers=headers, params={'filtering': json.dumps(payload)})
# Process response similar to above
GA4 Data API
# Python: Pull GA4 data as neutral cross-channel view
from google.analytics.data_v1beta import BetaAnalyticsDataClient
from google.analytics.data_v1beta.types import RunReportRequest, DateRange, Dimension, Metric
client = BetaAnalyticsDataClient()
request = RunReportRequest(
property='properties/XXXXXXXXX',
date_ranges=[DateRange(start_date='30daysAgo', end_date='today')],
dimensions=[
Dimension(name='sessionSource'),
Dimension(name='sessionMedium'),
Dimension(name='sessionCampaignName'),
Dimension(name='date')
],
metrics=[
Metric(name='sessions'),
Metric(name='conversions'),
Metric(name='purchaseRevenue'),
Metric(name='totalUsers')
]
)
response = client.run_report(request)
For comprehensive GA4 API configuration, see our GA4 Setup Complete Guide.
Data Normalization
Metric Alignment
Different platforms define the same metrics differently. You must normalize before comparing.
# Data normalization layer
def normalize_campaign_data(raw_data):
platform = raw_data['platform']
normalized = {
'platform': platform,
'campaign_name': raw_data['campaign_name'],
'campaign_id': raw_data['campaign_id'],
'date': raw_data['date'],
'impressions': raw_data['impressions'],
'cost': raw_data['cost'],
'cost_currency': 'USD', # After currency conversion
}
# Normalize clicks
if platform == 'meta_ads':
# Meta "clicks" includes all clicks (profile, comments, etc.)
# Use "link_clicks" for comparable metric (if available)
normalized['clicks'] = raw_data.get('link_clicks', raw_data['clicks'])
else:
normalized['clicks'] = raw_data['clicks']
# Normalize conversions
normalized['conversions'] = raw_data['conversions']
normalized['revenue'] = raw_data['revenue']
# Calculate derived metrics
normalized['ctr'] = (normalized['clicks'] / normalized['impressions'] * 100
if normalized['impressions'] > 0 else 0)
normalized['cpc'] = (normalized['cost'] / normalized['clicks']
if normalized['clicks'] > 0 else 0)
normalized['cpa'] = (normalized['cost'] / normalized['conversions']
if normalized['conversions'] > 0 else 0)
normalized['roas'] = (normalized['revenue'] / normalized['cost']
if normalized['cost'] > 0 else 0)
return normalized
Currency Normalization
If you advertise in multiple currencies, convert all costs and revenue to a single base currency:
# Currency conversion using daily exchange rates
from datetime import date
EXCHANGE_RATES = {
'2026-03-09': {
'TWD': 0.031, # TWD to USD
'EUR': 1.09, # EUR to USD
'GBP': 1.27 # GBP to USD
}
}
def convert_to_usd(amount, currency, date_str):
if currency == 'USD':
return amount
rate = EXCHANGE_RATES.get(date_str, {}).get(currency, 1)
return amount * rate
Naming Convention Alignment
Campaign names often differ slightly across platforms. Create a mapping table:
# Campaign name normalization
CAMPAIGN_MAPPING = {
# Google Ads name → Unified name
'Brand - Exact Match - US': 'brand_search_us',
'Brand - Broad Match - US': 'brand_search_us',
# Meta name → Unified name
'Brand Awareness - US - Broad': 'prospecting_awareness_us',
'[ABO] Prospecting - Interests - US': 'prospecting_interest_us',
# TikTok name → Unified name
'Prospecting_US_25-44_Interest': 'prospecting_interest_us',
}
def normalize_campaign_name(platform_name):
return CAMPAIGN_MAPPING.get(platform_name, platform_name)
Attribution Alignment
The Attribution Challenge Across Platforms
Each platform uses its own attribution model and window, making direct comparison meaningless without alignment.
Alignment Strategies
Strategy 1: Use GA4 as the neutral referee
GA4 sees all traffic regardless of source and applies a single attribution model. Use GA4-attributed conversions for cross-channel comparison:
-- BigQuery: GA4 data as cross-channel attribution source
SELECT
session_source AS source,
session_medium AS medium,
session_campaign AS campaign,
COUNT(DISTINCT user_pseudo_id) AS users,
COUNTIF(event_name = 'purchase') AS conversions,
SUM(CASE WHEN event_name = 'purchase'
THEN ecommerce.purchase_revenue ELSE 0 END) AS revenue
FROM `project.analytics_XXXXXXXXX.events_*`
WHERE _TABLE_SUFFIX BETWEEN '20260209' AND '20260309'
GROUP BY 1, 2, 3
ORDER BY revenue DESC
Strategy 2: Standardize attribution windows
Compare all platforms using the same window. Set Meta to 7-day click only (no view-through) and Google Ads to 7-day click for apples-to-apples comparison.
Strategy 3: Blended ROAS with backend truth
Use total revenue from your backend and total cost from all platforms:
Blended ROAS = Total Backend Revenue / Total Ad Spend (all platforms)
Channel ROAS (blended): Keep each platform's cost but use GA4 attributed revenue
Google Ads ROAS = GA4 Google Revenue / Google Ads Spend
Meta ROAS = GA4 Meta Revenue / Meta Spend
TikTok ROAS = GA4 TikTok Revenue / TikTok Spend
For understanding how attribution models affect these calculations, refer to our Conversion Tracking Complete Guide.
Building the Unified Dashboard
Dashboard Structure
Design your dashboard around the decisions it needs to support:
Dashboard: Cross-Platform Performance
├── Executive Summary (Page 1)
│ ├── Blended ROAS (big number)
│ ├── Total Spend vs Total Revenue (trend chart)
│ ├── Cost per Acquisition by platform (bar chart)
│ └── Budget split vs Revenue split (pie charts)
│
├── Platform Comparison (Page 2)
│ ├── Side-by-side: Spend, Conversions, ROAS per platform
│ ├── Platform ROAS trend (line chart, 30 days)
│ ├── CPA trend by platform (line chart)
│ └── Discrepancy analysis (platform-reported vs GA4)
│
├── Campaign Drill-Down (Page 3)
│ ├── All campaigns, all platforms, sortable table
│ ├── Columns: Platform, Campaign, Spend, Clicks, Conv, Revenue, ROAS, CPA
│ ├── Filter by platform, date range, campaign type
│ └── Color coding: Green = above target ROAS, Red = below
│
├── Funnel Analysis (Page 4)
│ ├── Cross-platform funnel: Impression → Click → Visit → Convert
│ ├── Drop-off rates by platform
│ ├── Landing page performance by traffic source
│ └── Device breakdown
│
└── Budget Recommendations (Page 5)
├── Current budget allocation vs optimal (based on marginal ROAS)
├── Recommended shifts with expected impact
└── Historical performance of budget changes
Looker Studio Implementation
-- SQL view for Looker Studio dashboard (BigQuery)
CREATE VIEW `project.marketing.unified_daily` AS
-- Google Ads data
SELECT
'Google Ads' AS platform,
campaign_name,
date,
impressions,
clicks,
cost,
conversions,
revenue,
SAFE_DIVIDE(revenue, cost) AS roas,
SAFE_DIVIDE(cost, conversions) AS cpa,
SAFE_DIVIDE(clicks, impressions) * 100 AS ctr
FROM `project.marketing.google_ads_daily`
UNION ALL
-- Meta Ads data
SELECT
'Meta Ads' AS platform,
campaign_name,
date,
impressions,
link_clicks AS clicks,
spend AS cost,
purchase_conversions AS conversions,
purchase_revenue AS revenue,
SAFE_DIVIDE(purchase_revenue, spend) AS roas,
SAFE_DIVIDE(spend, purchase_conversions) AS cpa,
SAFE_DIVIDE(link_clicks, impressions) * 100 AS ctr
FROM `project.marketing.meta_ads_daily`
UNION ALL
-- TikTok Ads data
SELECT
'TikTok Ads' AS platform,
campaign_name,
date,
impressions,
clicks,
cost,
conversions,
revenue,
SAFE_DIVIDE(revenue, cost) AS roas,
SAFE_DIVIDE(cost, conversions) AS cpa,
SAFE_DIVIDE(clicks, impressions) * 100 AS ctr
FROM `project.marketing.tiktok_ads_daily`;
For proper UTM tracking that feeds into cross-platform analysis, see our UTM Parameters Usage Guide.
Automated Reporting and Alerts
Weekly Performance Report
Automate a weekly email report summarizing cross-platform performance:
# Weekly report template
def generate_weekly_report(data):
report = {
'period': 'Last 7 days',
'summary': {
'total_spend': sum(d['cost'] for d in data),
'total_revenue': sum(d['revenue'] for d in data),
'blended_roas': sum(d['revenue'] for d in data) / sum(d['cost'] for d in data),
'total_conversions': sum(d['conversions'] for d in data)
},
'by_platform': {},
'top_campaigns': [],
'alerts': []
}
# Platform breakdown
for platform in ['Google Ads', 'Meta Ads', 'TikTok Ads']:
platform_data = [d for d in data if d['platform'] == platform]
if platform_data:
report['by_platform'][platform] = {
'spend': sum(d['cost'] for d in platform_data),
'revenue': sum(d['revenue'] for d in platform_data),
'roas': (sum(d['revenue'] for d in platform_data) /
sum(d['cost'] for d in platform_data)),
'conversions': sum(d['conversions'] for d in platform_data)
}
# Performance alerts
for platform, metrics in report['by_platform'].items():
if metrics['roas'] < 2.0:
report['alerts'].append(f"WARNING: {platform} ROAS below 2.0 ({metrics['roas']:.1f}x)")
return report
Real-Time Anomaly Detection
# Alert when a platform's daily spend or conversions deviate significantly
def check_anomalies(today_data, historical_avg):
alerts = []
for platform in today_data:
spend_deviation = abs(today_data[platform]['cost'] - historical_avg[platform]['cost']) / historical_avg[platform]['cost']
conv_deviation = abs(today_data[platform]['conversions'] - historical_avg[platform]['conversions']) / max(historical_avg[platform]['conversions'], 1)
if spend_deviation > 0.5:
alerts.append({
'severity': 'HIGH',
'platform': platform,
'metric': 'spend',
'message': f"{platform} spend deviated {spend_deviation:.0%} from average"
})
if conv_deviation > 0.5:
alerts.append({
'severity': 'CRITICAL',
'platform': platform,
'metric': 'conversions',
'message': f"{platform} conversions deviated {conv_deviation:.0%} from average"
})
return alerts
For deeper analysis of the data flowing through this system, see our Ad Data Analysis for Beginners guide.
Implementation Roadmap
Phase 1: Data Collection (Weeks 1-2)
- Set up API access for Google Ads, Meta, TikTok
- Build data extraction scripts (or configure connector tool)
- Create data warehouse tables/views
- Schedule daily data pulls
Phase 2: Normalization (Week 3)
- Implement metric normalization logic
- Create campaign name mapping
- Set up currency conversion
- Validate normalized data against platform dashboards
Phase 3: Dashboard (Weeks 4-5)
- Build the unified dashboard in Looker Studio or your tool of choice
- Create the five-page structure described above
- Add interactive filters (date range, platform, campaign)
- Test with stakeholders
Phase 4: Automation (Week 6)
- Set up automated weekly reports
- Configure anomaly detection alerts
- Build budget recommendation logic
- Document maintenance procedures
Ongoing Maintenance
- Review API changes quarterly (platforms update their APIs frequently)
- Update campaign mapping when new campaigns launch
- Refine anomaly detection thresholds based on false positive rate
- Review attribution alignment settings as platform defaults change
For the full tracking setup that feeds into cross-platform analysis, see our Pixel + CAPI Dual Tracking Setup guide.
Building a cross-platform dashboard from scratch is complex. RedClaw builds unified tracking dashboards for performance marketers, handling the API integrations, data normalization, and ongoing maintenance so you can focus on optimization. Get a free tracking audit
FAQ
What is the easiest way to start cross-platform reporting without building a data warehouse?
Start with Google Looker Studio (free) plus a data connector like Supermetrics or Funnel.io ($50-200/month). These connectors pull data from Google Ads, Meta, and TikTok directly into Looker Studio without requiring a data warehouse, APIs, or coding. The trade-off is less flexibility in data normalization and attribution alignment, but it gets you started with side-by-side platform comparison within hours.
How do I handle attribution inflation when summing conversions across platforms?
Never sum platform-reported conversions as your total. Instead, use your backend (CRM, order management system) as the source of truth for total conversions. Then use GA4's cross-channel attribution to understand each platform's contribution. For budget allocation, calculate each platform's blended contribution rate: GA4-attributed conversions per platform divided by total backend conversions.
Should I include organic channels in my cross-platform dashboard?
Yes. Including organic search (from GA4 + Search Console), email (from your ESP), and direct traffic provides the full picture of your marketing performance. Paid media does not operate in isolation; organic channels often play supporting roles in conversion paths. A dashboard showing only paid channels misses the interaction effects between paid and organic touchpoints.
How often should I refresh cross-platform data?
Daily refreshes are sufficient for most businesses. Ad platform APIs often have a 24-48 hour delay for finalized data (conversions may be adjusted retroactively due to attribution windows and modeling). Running hourly refreshes creates noise from preliminary data. If you need real-time monitoring, use each platform's native dashboard for intraday checks and your unified dashboard for strategic decisions using settled data.
Can I compare ROAS across platforms if they use different attribution models?
Not directly -- comparing Google Ads DDA ROAS to Meta 7d-click-1d-view ROAS is misleading. To make valid comparisons, either standardize attribution windows (set all platforms to 7-day click only) or use GA4 as the neutral attribution layer. GA4 applies one consistent model across all channels, giving you comparable ROAS figures. Alternatively, use blended ROAS (backend revenue divided by platform cost) as the universal comparison metric.
Stop making budget decisions from platform silos. Our cross-platform dashboards give you one number for every metric, across every channel. Check your ROAS
Related reading: Pixel + CAPI Dual Tracking Setup | GA4 Setup Complete Guide | Conversion Tracking Complete Guide | UTM Parameters Usage Guide | Ad Data Analysis for Beginners
Related Posts
iGaming Social Media Marketing: Complete Guide to Brand Growth & Player Engagement in 2026
Master iGaming social media marketing with proven strategies for Facebook, Instagram, Twitter, Telegram & more. Learn content creation, engagement tactics, compliance guidelines & build a high-converting social media system.
Google Ads Gambling Policy 2026: Complete Certification Guide for iGaming Advertisers
Master Google Ads Gambling Policy with our comprehensive 2026 guide. Learn certification requirements, country-specific regulations, compliance strategies, and avoid account suspensions.
iGaming Audience Targeting: Advanced Lookalike Strategies for 2026
Master advanced Lookalike Audience strategies for iGaming advertising. Learn seed audience optimization, layering techniques, and signal-based targeting to maximize player acquisition ROAS.