← Back to Documentation
    DocumentationintegrationsFeb 11, 2025

    UTM Parameter Strategy & Builder Reference

    Complete guide to UTM parameter strategy, naming conventions, GA4 channel grouping rules, auto-tagging behavior, common mistakes, and BigQuery validation queries for marketing attribution.

    Overview

    UTM (Urchin Tracking Module) parameters are query-string tags appended to URLs that tell Google Analytics 4 where traffic originated. Correct UTM hygiene is the foundation of marketing attribution — get it wrong and your channel reports, ROAS calculations, and budget allocation decisions are built on bad data.

    This guide covers the five UTM parameters, GA4's default channel grouping logic, naming conventions, auto-tagging behavior, common mistakes, and BigQuery queries to audit your UTM data.

    The Five UTM Parameters

    ParameterRequiredPurposeExample
    utm_sourceYesIdentifies the platform or publisher sending trafficgoogle, facebook, linkedin, newsletter
    utm_mediumYesIdentifies the marketing channel typecpc, paid-social, email, organic-social
    utm_campaignYesIdentifies the specific campaignspring-sale-2025, brand-awareness-q1
    utm_termNoIdentifies paid search keywordsrunning+shoes, crm+software
    utm_contentNoDifferentiates creative variants or link placementshero-banner, sidebar-cta, blue-variant

    Parameter Details

    utm_source — The platform or property sending the traffic. Think of it as "where did the click physically happen?"

    utm_source=google          # Google Ads or Google organic
    utm_source=facebook        # Meta Ads (Facebook/Instagram)
    utm_source=linkedin        # LinkedIn Ads or organic
    utm_source=klaviyo         # Email via Klaviyo
    utm_source=tiktok          # TikTok Ads
    utm_source=partner-blog    # A specific referral partner
    

    utm_medium — The channel type. This is the most important parameter for GA4 default channel grouping (see below). If you get utm_medium wrong, your traffic lands in the wrong channel.

    utm_medium=cpc             # Cost-per-click (paid search)
    utm_medium=paid-social     # Paid social ads
    utm_medium=email           # Email marketing
    utm_medium=organic-social  # Organic social posts
    utm_medium=display         # Display/banner ads
    utm_medium=affiliate       # Affiliate traffic
    utm_medium=referral        # Partner/referral traffic
    utm_medium=video           # Video ads (YouTube, etc.)
    

    utm_campaign — The campaign name. Use a consistent structure that encodes useful metadata.

    utm_campaign=spring-sale-2025
    utm_campaign=brand-awareness-q1-2025
    utm_campaign=retargeting-cart-abandoners
    utm_campaign=webinar-ai-marketing-apr
    

    utm_term — The keyword or targeting term. Primarily used for paid search, but useful for any keyword-targeted campaign.

    utm_term=running+shoes
    utm_term=best+crm+software
    utm_term=brand+name
    

    utm_content — The creative variant, ad position, or CTA. Use this to A/B test different ads, links, or placements within the same campaign.

    utm_content=hero-image-v1
    utm_content=sidebar-cta
    utm_content=email-header-link
    utm_content=video-30s
    

    Full URL Example

    https://example.com/landing-page?utm_source=facebook&utm_medium=paid-social&utm_campaign=spring-sale-2025&utm_content=carousel-ad-v2
    

    GA4 Source/Medium Mapping

    How GA4 Processes UTMs

    When a user clicks a URL with UTM parameters, GA4:

    1. Reads utm_source, utm_medium, and utm_campaign from the URL query string
    2. Stores them as session-scoped dimensions (sessionSource, sessionMedium, sessionCampaignName)
    3. Applies default channel grouping rules based primarily on source and medium
    4. For first-visit attribution, stores values in the user-scoped traffic_source struct

    BigQuery Field Mapping

    In the GA4 BigQuery export, UTM values appear in multiple locations:

    UTM ParameterBigQuery LocationScope
    utm_sourcetraffic_source.sourceUser (first-touch)
    utm_mediumtraffic_source.mediumUser (first-touch)
    utm_campaigntraffic_source.nameUser (first-touch)
    utm_sourceEvent param source via collected_traffic_sourceSession
    utm_mediumEvent param medium via collected_traffic_sourceSession
    utm_campaignEvent param campaign via collected_traffic_sourceSession

    Important: traffic_source is user-scoped and reflects the user's first acquisition source. For session-level attribution, use collected_traffic_source fields or extract from event_params on session_start events.

    -- Session-level source/medium from session_start events
    SELECT
      (SELECT value.string_value FROM UNNEST(event_params) WHERE key = 'source') as session_source,
      (SELECT value.string_value FROM UNNEST(event_params) WHERE key = 'medium') as session_medium,
      (SELECT value.string_value FROM UNNEST(event_params) WHERE key = 'campaign') as session_campaign,
      COUNT(*) as sessions
    FROM `project.analytics_123456789.events_*`
    WHERE _TABLE_SUFFIX BETWEEN FORMAT_DATE('%Y%m%d', DATE_SUB(CURRENT_DATE(), INTERVAL 30 DAY))
                            AND FORMAT_DATE('%Y%m%d', CURRENT_DATE() - 1)
      AND event_name = 'session_start'
    GROUP BY 1, 2, 3
    ORDER BY sessions DESC
    

    GA4 Default Channel Grouping Rules

    GA4 classifies every session into a default channel group based on source, medium, and sometimes campaign values. These rules determine how traffic appears in GA4's standard reports.

    Channel Grouping Logic

    ChannelRules (conditions are OR unless stated)
    DirectSource is (direct) AND medium is (not set) or (none)
    Organic SearchMedium exactly matches organic
    Paid SearchMedium matches regex ^(cpc|ppc|paidsearch)$
    DisplayMedium matches regex ^(display|cpm|banner)$
    Paid SocialMedium matches regex ^(paid-social|paidsocial|paid_social)$ OR (medium is cpc/ppc/paid AND source matches a social platform)
    Organic SocialMedium matches regex ^(organic-social|social|social-network|social-media|sm|social_network|social_media)$ OR source matches a known social platform with no paid medium
    EmailMedium exactly matches email
    AffiliatesMedium exactly matches affiliate
    ReferralMedium exactly matches referral
    AudioMedium exactly matches audio
    SMSMedium matches regex ^(sms|sms_message)$
    Mobile Push NotificationsMedium matches regex ^(push|mobile|notification)$ (must end with push, mobile, or notification)
    VideoMedium matches regex ^(video|youtube)$
    Cross-networkCampaign name contains cross-network
    UnassignedDoes not match any rule above

    Known social platform sources used by GA4: facebook, instagram, linkedin, twitter, t.co, pinterest, youtube, reddit, tiktok, snapchat, threads.net, and others.

    Critical Takeaway

    If utm_medium does not match any of the patterns above, your traffic falls into Unassigned. The most common cause is using non-standard medium values like social when you meant paid-social, or ppc for a social campaign.

    Naming Conventions

    Rules

    1. Lowercase everything. GA4 is case-sensitive. Facebook and facebook create two separate source entries.
    2. Use hyphens as word separators. Avoid spaces (they become %20), underscores are acceptable but hyphens are preferred for URL readability.
    3. No special characters. Stick to a-z, 0-9, and -. Avoid &, =, ?, #, + in values.
    4. Be specific but concise. paid-social is better than ps. spring-sale-2025 is better than campaign1.
    5. Use a taxonomy. Standardize your parameter values in a shared document.

    Recommended Taxonomy

    ParameterConventionFormat
    utm_sourcePlatform name, lowercasegoogle, facebook, linkedin, klaviyo
    utm_mediumChannel type matching GA4 grouping rulescpc, paid-social, email, display, organic-social
    utm_campaigncampaign-name or campaign-name-datespring-sale-2025, retarget-cart-q1
    utm_termKeyword or targeting termrunning-shoes, brand-keyword
    utm_contentCreative variant or placementvideo-30s-v2, hero-banner, cta-red

    Examples by Channel

    Google Ads (Search)

    ?utm_source=google&utm_medium=cpc&utm_campaign=brand-search-2025&utm_term=cogny+analytics&utm_content=rsa-v1
    

    Google Ads (Display)

    ?utm_source=google&utm_medium=display&utm_campaign=retarget-site-visitors&utm_content=banner-300x250
    

    Google Ads (Performance Max)

    ?utm_source=google&utm_medium=cpc&utm_campaign=pmax-all-products&utm_content=pmax
    

    Meta Ads (Facebook/Instagram)

    ?utm_source=facebook&utm_medium=paid-social&utm_campaign=lookalike-purchase-q1&utm_content=carousel-lifestyle-v2
    

    LinkedIn Ads

    ?utm_source=linkedin&utm_medium=paid-social&utm_campaign=b2b-decision-makers&utm_content=single-image-whitepaper
    

    Email (Klaviyo/Mailchimp)

    ?utm_source=klaviyo&utm_medium=email&utm_campaign=weekly-newsletter-apr-10&utm_content=header-cta
    

    Organic Social (LinkedIn post)

    ?utm_source=linkedin&utm_medium=organic-social&utm_campaign=thought-leadership&utm_content=ceo-post-ai-trends
    

    Influencer

    ?utm_source=creator-jane-doe&utm_medium=paid-social&utm_campaign=influencer-spring-2025&utm_content=instagram-story
    

    Affiliate

    ?utm_source=partner-techblog&utm_medium=affiliate&utm_campaign=affiliate-program-2025&utm_content=review-article
    

    Auto-Tagging vs Manual Tagging

    Google Ads Auto-Tagging (gclid)

    Google Ads appends a gclid (Google Click ID) parameter to every ad click URL. When auto-tagging is enabled:

    • GA4 automatically attributes the session to Google Ads with correct campaign, ad group, keyword, and match type data
    • The gclid links GA4 sessions to Google Ads cost data for ROAS reporting
    • Source/medium is set to google / cpc automatically
    • No UTM parameters are needed for standard Google Ads tracking

    When you still need UTMs with Google Ads:

    • Cross-platform reporting tools that do not read gclid
    • Third-party analytics platforms (Mixpanel, Amplitude, etc.)
    • CRM systems that parse UTM parameters from landing page URLs
    • When you need utm_content to track creative variants in BigQuery
    • When auto-tagging is disabled (not recommended)

    Important: If both gclid and UTM parameters are present, GA4 prioritizes gclid. The UTM values are ignored for GA4 attribution but may still be captured in the raw URL (accessible via page_location event parameter).

    Meta Auto-Tagging (fbclid)

    Meta Ads appends an fbclid parameter to click URLs. However:

    • GA4 does not natively parse fbclid for attribution
    • Without UTM parameters, Meta traffic typically appears as facebook.com / referral or l.facebook.com / referral
    • You must use UTM parameters for correct Meta Ads attribution in GA4
    • Set UTMs at the ad set or ad level in Meta Ads Manager using URL Parameters

    Meta Ads Manager URL Parameter template:

    utm_source=facebook&utm_medium=paid-social&utm_campaign={{campaign.name}}&utm_content={{ad.name}}&utm_term={{adset.name}}
    

    Meta supports dynamic parameters: {{campaign.name}}, {{campaign.id}}, {{adset.name}}, {{adset.id}}, {{ad.name}}, {{ad.id}}, {{placement}}, {{site_source_name}}.

    LinkedIn Insight Tag

    The LinkedIn Insight Tag tracks conversions but does not provide click-level attribution to GA4. You must use UTM parameters for LinkedIn Ads attribution in GA4.

    LinkedIn Campaign Manager URL Parameter template:

    utm_source=linkedin&utm_medium=paid-social&utm_campaign={{CAMPAIGN_NAME}}&utm_content={{CREATIVE_NAME}}
    

    LinkedIn supports these macros: {{CAMPAIGN_ID}}, {{CAMPAIGN_NAME}}, {{CAMPAIGN_GROUP_ID}}, {{CAMPAIGN_GROUP_NAME}}, {{CREATIVE_ID}}, {{CREATIVE_NAME}}.

    When to Use Both Auto-Tagging + Manual UTMs

    Use both when:

    1. Google Ads + CRM integration: Auto-tagging feeds GA4, UTMs feed your CRM (HubSpot, Salesforce) for lead attribution
    2. Cross-platform dashboards: Tools like Looker Studio can use either, but third-party tools often need UTMs
    3. BigQuery analysis: You want UTM values in page_location for custom attribution models beyond GA4's built-in reporting

    Warning: For Google Ads, if you enable manual tagging alongside auto-tagging, ensure "Allow manual tagging (UTM values) to override auto-tagging (GCLID values)" is unchecked in GA4 Admin > Data Streams > Web Stream > Google Tag > Settings. Otherwise UTMs override the richer gclid-based attribution.

    Common UTM Mistakes

    1. Inconsistent Naming

    The single most common UTM problem. GA4 treats each unique string as a separate source.

    # These create THREE separate sources in GA4:
    utm_source=Facebook       # Wrong: capital F
    utm_source=facebook       # Correct
    utm_source=fb             # Wrong: abbreviation
    
    # These create THREE separate mediums:
    utm_medium=Social         # Wrong: not a GA4 channel grouping match
    utm_medium=paid_social    # Wrong: underscore (GA4 expects hyphen)
    utm_medium=paid-social    # Correct
    

    Fix: Maintain a shared UTM naming convention document. Enforce it with a URL builder tool or spreadsheet template.

    2. UTMs on Internal Links

    Adding UTM parameters to links within your own site (navigation, banners, CTAs) resets the session source and inflates session counts. Every internal UTM click starts a "new" session attributed to your own site.

    # NEVER do this:
    <a href="/pricing?utm_source=homepage&utm_medium=banner">See Pricing</a>
    
    # Instead, use event parameters or content groupings:
    <a href="/pricing" data-tracking="homepage-banner">See Pricing</a>
    

    Detection query:

    -- Find internal UTM pollution
    SELECT
      (SELECT value.string_value FROM UNNEST(event_params) WHERE key = 'page_location') as page_url,
      (SELECT value.string_value FROM UNNEST(event_params) WHERE key = 'page_referrer') as referrer,
      (SELECT value.string_value FROM UNNEST(event_params) WHERE key = 'source') as source,
      (SELECT value.string_value FROM UNNEST(event_params) WHERE key = 'medium') as medium,
      COUNT(*) as occurrences
    FROM `project.analytics_123456789.events_*`
    WHERE _TABLE_SUFFIX BETWEEN FORMAT_DATE('%Y%m%d', DATE_SUB(CURRENT_DATE(), INTERVAL 30 DAY))
                            AND FORMAT_DATE('%Y%m%d', CURRENT_DATE() - 1)
      AND event_name = 'session_start'
      AND (SELECT value.string_value FROM UNNEST(event_params) WHERE key = 'page_referrer') LIKE '%example.com%'
      AND (SELECT value.string_value FROM UNNEST(event_params) WHERE key = 'source') IS NOT NULL
      AND (SELECT value.string_value FROM UNNEST(event_params) WHERE key = 'source') != '(direct)'
    GROUP BY 1, 2, 3, 4
    ORDER BY occurrences DESC
    

    3. Missing utm_medium

    Without utm_medium, GA4 cannot classify traffic into the correct default channel grouping. Traffic with a source but no medium typically falls into Unassigned.

    # Bad: No medium — goes to Unassigned
    ?utm_source=facebook&utm_campaign=spring-sale
    
    # Good: Medium present — correctly classified as Paid Social
    ?utm_source=facebook&utm_medium=paid-social&utm_campaign=spring-sale
    

    4. URL-Encoded Special Characters

    Spaces in UTM values become %20, which creates messy source/medium values in reports. Ampersands break the URL structure entirely.

    # Bad:
    utm_campaign=Spring Sale 2025      # Becomes Spring%20Sale%202025
    utm_campaign=Q1 - Brand & Perf     # Ampersand breaks URL parsing
    
    # Good:
    utm_campaign=spring-sale-2025
    utm_campaign=q1-brand-and-perf
    

    5. UTMs on Organic/SEO Pages

    Never add UTM parameters to pages that search engines will crawl. Search engines may index the UTM-tagged URL, splitting your page authority and creating duplicate content. Organic search traffic is automatically attributed by GA4 without any UTM parameters.

    6. Forgetting to Update UTMs After Campaign Changes

    Reusing the same UTM campaign name across different quarters or years without updating it makes historical analysis impossible. Always include a date or version identifier.

    # Bad: Reused across years
    utm_campaign=brand-search
    
    # Good: Time-scoped
    utm_campaign=brand-search-q1-2025
    

    UTM Builder Patterns

    URL Template by Channel

    Google Ads (Search):

    {landing_page}?utm_source=google&utm_medium=cpc&utm_campaign={campaign_name}&utm_term={keyword}&utm_content={ad_variant}
    

    Google Ads (Display/Video):

    {landing_page}?utm_source=google&utm_medium=display&utm_campaign={campaign_name}&utm_content={creative_size_or_variant}
    

    Meta Ads:

    {landing_page}?utm_source=facebook&utm_medium=paid-social&utm_campaign={{campaign.name}}&utm_content={{ad.name}}&utm_term={{adset.name}}
    

    LinkedIn Ads:

    {landing_page}?utm_source=linkedin&utm_medium=paid-social&utm_campaign={{CAMPAIGN_NAME}}&utm_content={{CREATIVE_NAME}}
    

    Email:

    {landing_page}?utm_source={esp_name}&utm_medium=email&utm_campaign={email_campaign_name}&utm_content={link_placement}
    

    Organic Social:

    {landing_page}?utm_source={platform}&utm_medium=organic-social&utm_campaign={post_theme}&utm_content={post_variant}
    

    Spreadsheet / Naming Convention Template

    Use a shared spreadsheet with these columns to generate UTM URLs at scale:

    ColumnDescriptionExample
    Landing Page URLBase URL without parametershttps://example.com/landing
    SourcePlatform namefacebook
    MediumChannel typepaid-social
    CampaignCampaign namespring-sale-2025
    TermKeyword or targeting (optional)lookalike-purchase
    ContentCreative variant (optional)carousel-v2
    Generated URLFormula outputFull URL with parameters

    Spreadsheet formula (Google Sheets):

    =A2&"?utm_source="&B2&"&utm_medium="&C2&"&utm_campaign="&D2&IF(E2<>"","&utm_term="&E2,"")&IF(F2<>"","&utm_content="&F2,"")
    

    Bulk URL Generation

    For campaigns with many ad variants, generate URLs programmatically:

    import urllib.parse
    
    base_url = "https://example.com/landing"
    variants = [
        {"source": "facebook", "medium": "paid-social", "campaign": "spring-sale-2025", "content": "carousel-v1"},
        {"source": "facebook", "medium": "paid-social", "campaign": "spring-sale-2025", "content": "carousel-v2"},
        {"source": "facebook", "medium": "paid-social", "campaign": "spring-sale-2025", "content": "video-15s"},
    ]
    
    for v in variants:
        params = urllib.parse.urlencode({
            "utm_source": v["source"],
            "utm_medium": v["medium"],
            "utm_campaign": v["campaign"],
            "utm_content": v["content"],
        })
        print(f"{base_url}?{params}")
    

    Validation with BigQuery

    Find All Unique Source/Medium Combinations

    Identify every source/medium pair in your data to spot inconsistencies:

    SELECT
      traffic_source.source,
      traffic_source.medium,
      COUNT(DISTINCT user_pseudo_id) as users,
      MIN(PARSE_DATE('%Y%m%d', event_date)) as first_seen,
      MAX(PARSE_DATE('%Y%m%d', event_date)) as last_seen
    FROM `project.analytics_123456789.events_*`
    WHERE _TABLE_SUFFIX BETWEEN FORMAT_DATE('%Y%m%d', DATE_SUB(CURRENT_DATE(), INTERVAL 90 DAY))
                            AND FORMAT_DATE('%Y%m%d', CURRENT_DATE() - 1)
    GROUP BY 1, 2
    ORDER BY users DESC
    

    Find Orphaned/Inconsistent UTMs

    Detect variations in source naming (e.g., facebook vs Facebook vs fb):

    WITH source_variants AS (
      SELECT
        LOWER(traffic_source.source) as normalized_source,
        traffic_source.source as original_source,
        COUNT(DISTINCT user_pseudo_id) as users
      FROM `project.analytics_123456789.events_*`
      WHERE _TABLE_SUFFIX BETWEEN FORMAT_DATE('%Y%m%d', DATE_SUB(CURRENT_DATE(), INTERVAL 90 DAY))
                              AND FORMAT_DATE('%Y%m%d', CURRENT_DATE() - 1)
        AND traffic_source.source IS NOT NULL
      GROUP BY 1, 2
    )
    
    SELECT
      normalized_source,
      ARRAY_AGG(STRUCT(original_source, users) ORDER BY users DESC) as variants,
      COUNT(*) as variant_count
    FROM source_variants
    GROUP BY 1
    HAVING COUNT(*) > 1
    ORDER BY variant_count DESC
    

    Check Default Channel Grouping Assignment

    Verify which channel group GA4 assigns to each source/medium pair:

    SELECT
      traffic_source.source,
      traffic_source.medium,
      CASE
        WHEN traffic_source.source = '(direct)' AND (traffic_source.medium IS NULL OR traffic_source.medium IN ('(not set)', '(none)')) THEN 'Direct'
        WHEN traffic_source.medium = 'organic' THEN 'Organic Search'
        WHEN REGEXP_CONTAINS(traffic_source.medium, r'^(cpc|ppc|paidsearch)$') THEN 'Paid Search'
        WHEN REGEXP_CONTAINS(traffic_source.medium, r'^(display|cpm|banner)$') THEN 'Display'
        WHEN REGEXP_CONTAINS(traffic_source.medium, r'^(paid-social|paidsocial|paid_social)$') THEN 'Paid Social'
        WHEN REGEXP_CONTAINS(traffic_source.medium, r'^(social|social-network|social-media|sm|social_network|social_media|organic-social)$') THEN 'Organic Social'
        WHEN traffic_source.medium = 'email' THEN 'Email'
        WHEN traffic_source.medium = 'affiliate' THEN 'Affiliates'
        WHEN traffic_source.medium = 'referral' THEN 'Referral'
        WHEN traffic_source.medium = 'audio' THEN 'Audio'
        WHEN REGEXP_CONTAINS(traffic_source.medium, r'^(sms|sms_message)$') THEN 'SMS'
        WHEN REGEXP_CONTAINS(traffic_source.medium, r'^(video|youtube)$') THEN 'Video'
        WHEN REGEXP_CONTAINS(IFNULL(traffic_source.name, ''), r'cross-network') THEN 'Cross-network'
        ELSE 'Unassigned'
      END as predicted_channel_group,
      COUNT(DISTINCT user_pseudo_id) as users
    FROM `project.analytics_123456789.events_*`
    WHERE _TABLE_SUFFIX BETWEEN FORMAT_DATE('%Y%m%d', DATE_SUB(CURRENT_DATE(), INTERVAL 30 DAY))
                            AND FORMAT_DATE('%Y%m%d', CURRENT_DATE() - 1)
    GROUP BY 1, 2, 3
    ORDER BY users DESC
    

    Find Internal UTM Pollution

    Detect sessions where UTMs were triggered from your own domain (internal links with UTMs):

    SELECT
      (SELECT value.string_value FROM UNNEST(event_params) WHERE key = 'page_location') as landing_page,
      (SELECT value.string_value FROM UNNEST(event_params) WHERE key = 'page_referrer') as referrer,
      (SELECT value.string_value FROM UNNEST(event_params) WHERE key = 'source') as session_source,
      (SELECT value.string_value FROM UNNEST(event_params) WHERE key = 'medium') as session_medium,
      (SELECT value.string_value FROM UNNEST(event_params) WHERE key = 'campaign') as session_campaign,
      COUNT(*) as sessions
    FROM `project.analytics_123456789.events_*`
    WHERE _TABLE_SUFFIX BETWEEN FORMAT_DATE('%Y%m%d', DATE_SUB(CURRENT_DATE(), INTERVAL 30 DAY))
                            AND FORMAT_DATE('%Y%m%d', CURRENT_DATE() - 1)
      AND event_name = 'session_start'
      -- Replace example.com with your domain
      AND REGEXP_CONTAINS(
        (SELECT value.string_value FROM UNNEST(event_params) WHERE key = 'page_referrer'),
        r'example\.com'
      )
      AND REGEXP_CONTAINS(
        (SELECT value.string_value FROM UNNEST(event_params) WHERE key = 'page_location'),
        r'utm_'
      )
    GROUP BY 1, 2, 3, 4, 5
    ORDER BY sessions DESC
    

    Find Sessions Missing utm_medium

    Identify campaigns that are missing the critical utm_medium parameter:

    SELECT
      (SELECT value.string_value FROM UNNEST(event_params) WHERE key = 'source') as source,
      (SELECT value.string_value FROM UNNEST(event_params) WHERE key = 'campaign') as campaign,
      COUNT(*) as sessions
    FROM `project.analytics_123456789.events_*`
    WHERE _TABLE_SUFFIX BETWEEN FORMAT_DATE('%Y%m%d', DATE_SUB(CURRENT_DATE(), INTERVAL 30 DAY))
                            AND FORMAT_DATE('%Y%m%d', CURRENT_DATE() - 1)
      AND event_name = 'session_start'
      AND (SELECT value.string_value FROM UNNEST(event_params) WHERE key = 'source') IS NOT NULL
      AND (SELECT value.string_value FROM UNNEST(event_params) WHERE key = 'source') != '(direct)'
      AND (
        (SELECT value.string_value FROM UNNEST(event_params) WHERE key = 'medium') IS NULL
        OR (SELECT value.string_value FROM UNNEST(event_params) WHERE key = 'medium') IN ('(not set)', '(none)', '')
      )
    GROUP BY 1, 2
    ORDER BY sessions DESC
    

    Next Steps

    Claude Code Skill

    This UTM strategy reference is also available as a free Claude Code skill — use it directly in your terminal to build UTM URLs, audit your naming conventions, and validate attribution data:

    # Install
    curl -sSL https://raw.githubusercontent.com/cognyai/claude-code-marketing-skills/main/install.sh | bash
    
    # Use
    /utm-builder                          # Full UTM strategy overview
    /utm-builder naming conventions       # Show naming convention rules
    /utm-builder channel grouping         # GA4 default channel grouping rules
    /utm-builder audit                    # BigQuery validation queries
    

    View on GitHub →

    Resources

    Ready for similar results?
    Start with Solo at $9/mo or talk to us about Cloud.
    ❯ get startedcompare plans →