API
Query cookbook
Ready-made bodies for POST /v1/query, one per common question. Each runs as is: add your site_id and send it. All of them are checked against the live query validator every time we change Punchline, so they can't go out of date. The fields are explained in the query and every value in Concepts.
curl https://app.punchlineanalytics.com/v1/query \
-H "Authorization: Bearer $PUNCHLINE_API_KEY" \
-H "Content-Type: application/json" \
-d @query.json
How a query is built
metrics: what to count. They must come from one family: page metrics, session metrics, event metrics or conversion metrics (the error says which can go together).dimensions: how to split it. Up to 4. Leave it out for one total.filters: up to 10[field, op, value]triples, all of which must hold. Any dimension can be filtered, plusis_entry.date_range: inclusive UTC days.order_by(up to 3[field, "asc" | "desc"]pairs) andlimit(1–1,000, default 100): which rows come back.confidence: how sure a label must be to count as an agent.likelyby default;confirmedcounts only verified signatures and declared crawlers from matching IPs;alladds weak patterns. Visitors below the bar count as people, so totals never change, only the split.
Filter operators: eq, neq, in, not_in (with a list), starts_with, is_null, not_null (no value).
Traffic overview
How much of my traffic is AI agents, day by day?
{
"date_range": { "from": "2026-09-01", "to": "2026-09-30" },
"metrics": ["sessions", "agent_sessions", "agent_share"],
"dimensions": ["day"],
"order_by": [["day", "asc"]]
}
agent_share is a fraction (0.31 is 31%). It counts agents and crawlers together.
Weekly or monthly instead
{
"date_range": { "from": "2026-01-01", "to": "2026-09-30" },
"metrics": ["sessions"],
"dimensions": ["month", "visitor_type"],
"order_by": [["month", "asc"]]
}
week is the Monday that starts each ISO week and month the first of the month, both as YYYY-MM-DD.
People, AI-referred people, agents and crawlers side by side
{
"date_range": { "from": "2026-09-01", "to": "2026-09-30" },
"metrics": ["sessions", "pageviews"],
"dimensions": ["visitor_type"]
}
visitor_type is human, ai_referred, agent or crawler. actor_class is the finer label underneath: ai_referred_human, signed_agent, declared_agent, fetcher, suspected_agent, spoofed_bot and so on.
AI agents and crawlers
Which AI companies visit, and how often?
{
"date_range": { "from": "2026-09-01", "to": "2026-09-30" },
"metrics": ["pageviews"],
"dimensions": ["operator", "agent"],
"filters": [["visitor_type", "in", ["agent", "crawler"]]],
"order_by": [["pageviews", "desc"]],
"limit": 25
}
operator is the company (openai, anthropic, google, …), agent the named bot (GPTBot, ChatGPT-User, ClaudeBot, …). Unattributed automation has a null operator.
Live fetches for someone asking now, versus crawling for training
{
"date_range": { "from": "2026-09-01", "to": "2026-09-30" },
"metrics": ["pageviews"],
"dimensions": ["agent_purpose"],
"filters": [["visitor_type", "in", ["agent", "crawler"]]]
}
agent_purpose is user_triggered or agent (live: someone is waiting for the answer), training, search or crawl (building a dataset or index), other_automation, or impostor (claims to be a known bot from the wrong address).
What agents read most
{
"date_range": { "from": "2026-09-01", "to": "2026-09-30" },
"metrics": ["pageviews"],
"dimensions": ["path"],
"filters": [["visitor_type", "eq", "agent"]],
"order_by": [["pageviews", "desc"]],
"limit": 50
}
Did agents get my pages?
{
"date_range": { "from": "2026-09-01", "to": "2026-09-30" },
"metrics": ["pageviews", "thin_pageviews"],
"dimensions": ["path", "delivery"],
"filters": [["visitor_type", "in", ["agent", "crawler"]], ["delivery", "neq", "ok"]],
"order_by": [["pageviews", "desc"]]
}
delivery is what the install saw happen to the request: ok, redirect, blocked, not_found, client_error or server_error (null with the browser snippet, which can't see responses). thin_pageviews counts successful responses under 2 KB, often a page that needs JavaScript to show anything. See Agent content.
Only what we're certain of
{
"date_range": { "from": "2026-09-01", "to": "2026-09-30" },
"metrics": ["pageviews"],
"dimensions": ["operator", "tier"],
"filters": [["visitor_type", "in", ["agent", "crawler"]]],
"confidence": "confirmed"
}
tier says how each label was reached: verified (a valid signature), declared (a known bot from its published addresses), inferred_high, inferred_low.
Impostors: bots pretending to be someone they aren't
{
"date_range": { "from": "2026-09-01", "to": "2026-09-30" },
"metrics": ["pageviews"],
"dimensions": ["agent", "path"],
"filters": [["actor_class", "eq", "spoofed_bot"]],
"order_by": [["pageviews", "desc"]]
}
People sent by AI assistants
Which assistants send people, and where they land
{
"date_range": { "from": "2026-09-01", "to": "2026-09-30" },
"metrics": ["sessions"],
"dimensions": ["assistant", "path"],
"filters": [["visitor_type", "eq", "ai_referred"], ["is_entry", "eq", true]],
"order_by": [["sessions", "desc"]],
"limit": 20
}
assistant is chatgpt, perplexity, claude, gemini or copilot. is_entry keeps only each session's first page, so path is the landing page.
Do AI-referred visitors engage more than others?
{
"date_range": { "from": "2026-09-01", "to": "2026-09-30" },
"metrics": ["sessions", "engagement_rate", "pages_per_session", "avg_session_duration"],
"dimensions": ["visitor_type"],
"filters": [["visitor_type", "in", ["human", "ai_referred"]]]
}
Conversions and events
Conversions by who converted
{
"date_range": { "from": "2026-09-01", "to": "2026-09-30" },
"metrics": ["conversions", "conversion_rate"],
"dimensions": ["goal", "visitor_type"]
}
Needs at least one goal. conversion_rate is conversions over sessions in the same group.
Which channels bring people who buy?
{
"date_range": { "from": "2026-09-01", "to": "2026-09-30" },
"metrics": ["conversions"],
"dimensions": ["channel"],
"filters": [["goal", "eq", "Purchase"], ["visitor_type", "in", ["human", "ai_referred"]]],
"order_by": [["conversions", "desc"]]
}
channel (how the visit arrived) is ai_assistants, organic_search, paid, email, social, affiliate, campaign, referral or direct, for every visitor type; add visitor_type as a dimension to split it into people, agents and crawlers. Replace Purchase with your goal's name.
Revenue from server events
{
"date_range": { "from": "2026-09-01", "to": "2026-09-30" },
"metrics": ["events", "event_value"],
"dimensions": ["event_name", "day"],
"filters": [["event_name", "in", ["purchase", "renewal"]]]
}
event_value sums the value you sent with each event (sending events).
Campaigns and sources
Campaign results for people only
{
"date_range": { "from": "2026-09-01", "to": "2026-09-30" },
"metrics": ["sessions", "bounce_rate"],
"dimensions": ["utm_source", "utm_medium", "utm_campaign"],
"filters": [["visitor_type", "eq", "human"], ["utm_campaign", "not_null"]],
"order_by": [["sessions", "desc"]]
}
Agents follow links in emails and ads too; filtering to people keeps them from inflating campaign numbers.
Referral partners
{
"date_range": { "from": "2026-09-01", "to": "2026-09-30" },
"metrics": ["sessions", "pageviews"],
"dimensions": ["partner"],
"filters": [["partner", "not_null"]]
}
Partners come from your URL-parameter rules.
Pages and audience
Top landing pages for people, with bounce rate
{
"date_range": { "from": "2026-09-01", "to": "2026-09-30" },
"metrics": ["sessions", "bounce_rate", "avg_session_duration"],
"dimensions": ["path"],
"filters": [["visitor_type", "in", ["human", "ai_referred"]]],
"order_by": [["sessions", "desc"]],
"limit": 25
}
With session metrics, path is the landing page.
Countries and devices
{
"date_range": { "from": "2026-09-01", "to": "2026-09-30" },
"metrics": ["sessions"],
"dimensions": ["country", "device_class"],
"filters": [["visitor_type", "eq", "human"]],
"order_by": [["sessions", "desc"]]
}
By section of the site
{
"date_range": { "from": "2026-09-01", "to": "2026-09-30" },
"metrics": ["pageviews"],
"dimensions": ["content_type", "visitor_type"]
}
content_type (pricing, docs, blog, …) and topic come from your page rules, or from common path conventions when there are none.
Reading the response
{
"query": { "…": "the query as it ran, with defaults filled in" },
"results": [{ "visitor_type": "agent", "sessions": 412 }],
"meta": { "audience": "external", "min_cohort": 5, "suppressed_rows": 2, "truncated": false, "rollups_refreshed_at": "2026-10-06T14:50:00.000Z" }
}
suppressed_rows: rows withheld because fewer than 5 people's page views were in them. Agent-only rows are never withheld. Fewer dimensions means fewer small groups.truncated: more rows matched thanlimit. Raise the limit, filter, or use an export.rollups_refreshed_at: numbers come from rollups refreshed every 10 minutes; today's totals are as of this time.
See Limits and conventions for dates, rate limits and pagination.
Ask the docs
Answers come from these docs only, written by Claude (Anthropic). Your question is sent to answer it, and isn't stored.