What it does
Top referring hosts. Attribution metric — counts direct browser visits only ('user' class). AI-mediated traffic (Claude/ChatGPT fetching on a user's behalf) is excluded because the AI sets its own host as referrer or strips it; including it would inflate 'direct' or 'claude.ai' without telling you where the human attention actually came from. Counted once per session (field `sessions`), from the host the session entered on — not once per pageview, so don't compare it against pageview counts. percentage_of_total is a percentage of all referred sessions: 58.3 means 58.3%. group_by: channel groups the same entries into channels (search, social, …) instead, see group_by. Each row also has bounce_rate (ratio 0–1 of the row's sessions with exactly one pageview and no custom event; the engagement beacon does not count) and avg_session_duration_seconds (first to last event, over the row's sessions with more than one event; null when there is none).
Example prompts
Ask:
"Where is mysite.com traffic coming from this week?"
"Top referrers for the last 30 days, anything new?"
"Show me my top 20 referrers."
A typical response:
Top referrers for mysite.com (last 7 days):
news.ycombinator.com 1,847 sessions
twitter.com 612 sessions
reddit.com 487 sessions
bluesky.app 312 sessions
dev.to 258 sessions
google.com (organic) 244 sessions
perplexity.ai 147 sessions (AI-mediated human)
chatgpt.com 124 sessions (AI-mediated human)
duckduckgo.com 89 sessions
linkedin.com 71 sessions
Returns an array of { referrer_host, sessions } objects.
Counted once per session, from the source the session entered on — not
once per pageview.
Attribution metric: counts direct browser visits only
(the user traffic class). AI-mediated traffic
(ai_user_action, i.e. Claude or ChatGPT fetching on a user's
behalf) is excluded from this list, because the AI sets its own host as
the referrer or strips it. Including it would inflate "direct" or
"claude.ai" without telling you where the human attention actually
came from. Use
traffic_class_breakdown
to see the AI-mediated share separately.
The "AI-mediated human" lines above (perplexity.ai,
chatgpt.com) ARE included here. Those are humans clicking
through from those services in their browser, not the AI fetching for
them. That distinction matters for understanding AI's role in your
acquisition funnel.
Arguments
| Name | Type | Required | Description / Default |
|---|---|---|---|
site_id |
string | required | Site identifier from list_sites (8-character base32, e.g. 'wjxayhdd'). |
period |
string | optional |
Time window. Keywords: today, yesterday, last_N_days (N from 1 to 730: today plus the N-1 days before, e.g. last_7_days, last_28_days), last_24_hours, this_week, last_week (weeks start on Monday), this_month, last_month, this_year, last_year, last_12_months. Or a custom date range YYYY-MM-DD..YYYY-MM-DD (inclusive). Days follow the site's timezone (list_sites; echoed as `timezone` in every response).
default: last_7_days
|
limit |
integer | optional |
Maximum number of rows to return. Capped at 1000 server-side.
default: 10
|
group_by |
string enum: host, channel |
optional |
host (default): one row per referring host; sessions without a referrer are left out. channel: one row per channel (search, ai_assistant, social, email, paid, campaign, referral, direct), decided by the session's entry. utm_medium wins over the host (utm_medium=email on a Gmail link is email; paid comes only from utm_medium such as cpc), then the referring host; no referrer and no utm is direct, utm tags without a referrer are campaign, unknown hosts are referral. direct is included, so percentage_of_total is a percentage of ALL entry sessions. Each row has top_hosts: its three most frequent entry hosts (utm_source values for rows without a host, [] for direct). conversion_event works per channel.
default: host
|
conversion_event |
string | optional | Optional custom event name (case-sensitive, from list_events) to treat as the conversion. Each row then gains conversion_base (units in that row), converted (how many of them fired the event anywhere in the period) and conversion_rate (ratio 0–1: 0.05 means 5%; null when no unit is attributed to the row: it has no browser sessions/visitors at all, e.g. purely server-ingested traffic, or — on privacy_mode=all sites in top_referrers/top_sources/top_entry_pages — the row was never any visitor's first entry in the period, even though it has sessions). The response gains a site-wide `conversion` block: conversion_base (units in the period), converted (how many fired the event), conversion_rate (ratio 0–1: 0.05 means 5%), `unit` and a `note` to relay. Use it to compare conversion rates between rows — absolute counts alone favour whatever row has the most traffic. The unit depends on the site's privacy_mode: 'session' on strict and balanced sites (no identity survives UTC midnight there, so only conversions within one visit count), 'visitor' on privacy_mode=all sites (first-party cookie, so a visitor who arrives Monday and converts Thursday counts as converted, if both days are inside the period). The auto-tracked 'pageview' and 'engagement' are rejected — pick one of the site's own events. Revenue: if the event carries a numeric `revenue` property (optional `currency`, e.g. mcpa('track', 'purchase', { revenue: 149, currency: 'EUR' })), each row gains revenue (sum over the conversion events of the units in that row; like conversion_base, not additive across rows where a unit appears in several) and revenue_per_unit (revenue / conversion_base), and the block gains revenue_total (over the converted units), currency (null when none was sent) and revenue_events (events with a numeric value; other values are ignored). Values are as reported by the site, not verified; negative values (refunds) are summed as sent. Only three-letter currency codes count as a currency; other values are unspecified. With two or more codes nothing is converted or added across codes: revenue_total, revenue_events and the rows' revenue cover the dominant code (most distinct converting units; itself visitor-influenced) plus values without a code, and the block adds revenue_by_currency listing each code with its own count. Not available with conversion_path. |
conversion_path |
string | optional | Optional goal page, the alternative to conversion_event (pass one, not both): a unit counts as converted if it has a pageview on this path anywhere in the period, so no custom event is needed and it works on past data. '/thank-you' matches that path exactly (a trailing slash on either side is ignored); a trailing * matches by prefix ('/checkout/success*'). Must start with '/'. Matched case-sensitively against the stored path, which has no query string and stores credential-like segments (long random tokens, UUIDs) as [redacted], so a goal containing one never matches. Only pageviews count: an engagement beacon or custom event on that page does not. Fields, unit (session, or visitor on privacy_mode=all sites) and the `conversion` block are as for conversion_event; the block names `path` and `match` (exact or prefix) instead of `event`. Filters narrow the units; the goal pageview counts anywhere in the period. |
filters |
object | optional | Optional segment filter. All given keys must match (AND; no OR, no negation); an empty string means not set. Keys: path (exact) or path_prefix; device_type, browser, os (exact, case-sensitive); referrer_host, utm_source, utm_medium, utm_campaign (exact); channel (search, ai_assistant, social, email, paid, campaign, referral, direct; as top_referrers group_by: channel assigns it). path/path_prefix match rows: pageviews count rows on matching paths, sessions/visitors count those with at least one matching row, and bounce_rate/avg_session_duration_seconds use all rows of those sessions. referrer_host, utm_* and channel match the session's entry (its first pageview in the period) and then select every row of the matching sessions, in every privacy mode. In top_entry_pages, top_referrers, top_sources and get_overview's top_source, path/path_prefix also test the entry: path_prefix '/blog/' there means sessions that entered on /blog/. Source and device filters make every number browser-only: server-side-ingested rows (including AI-mediated ai_user_action) have no session and no device fields, so they never match; the response then adds a `filter_note`. In get_overview, bot_share is null under any key other than path/path_prefix. With conversion_event the filter narrows the units; the conversion itself still counts anywhere in the period. The applied filters are echoed back as `filters`. |
How to call it directly
If you're integrating from your own code rather than a chat client, this is the JSON-RPC payload:
curl -X POST https://mcp-analytics.com/mcp \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "top_referrers",
"arguments": {
"site_id": "abc12345"
}
}
}'
Token comes from /settings after you sign up. Replace any required arguments above.
Related tools
add_site: Register a new site.top_movers: What changed between two periods: the rows of one dimension that grew or shrank the most, in one call instead of diffing two list calls.get_timeseries: Time-bucketed metric over a period.compare_periods: Compare a metric between two periods.list_events: All event names with counts (includes 'pageview' and custom events).