# 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](https://punchlineanalytics.com/docs/api#the-query) and every value in [Concepts](https://punchlineanalytics.com/docs/concepts).

```bash
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, plus `is_entry`.
- **`date_range`**: inclusive UTC days.
- **`order_by`** (up to 3 `[field, "asc" | "desc"]` pairs) and **`limit`** (1–1,000, default 100): which rows come back.
- **`confidence`**: how sure a label must be to count as an agent. `likely` by default; `confirmed` counts only verified signatures and declared crawlers from matching IPs; `all` adds 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?

```json query
{
  "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

```json query
{
  "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

```json query
{
  "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?

```json query
{
  "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

```json query
{
  "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

```json query
{
  "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?

```json query
{
  "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](https://punchlineanalytics.com/docs/agent-content).

### Only what we're certain of

```json query
{
  "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

```json query
{
  "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

```json query
{
  "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?

```json query
{
  "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

```json query
{
  "date_range": { "from": "2026-09-01", "to": "2026-09-30" },
  "metrics": ["conversions", "conversion_rate"],
  "dimensions": ["goal", "visitor_type"]
}
```

Needs at least one [goal](https://punchlineanalytics.com/docs/events-goals). `conversion_rate` is conversions over sessions in the same group.

### Which channels bring people who buy?

```json query
{
  "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

```json query
{
  "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](https://punchlineanalytics.com/docs/events-goals)).

## Campaigns and sources

### Campaign results for people only

```json query
{
  "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

```json query
{
  "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](https://punchlineanalytics.com/docs/url-params).

## Pages and audience

### Top landing pages for people, with bounce rate

```json query
{
  "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

```json query
{
  "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

```json query
{
  "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

```json
{
  "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 than `limit`. Raise the limit, filter, or use an [export](https://punchlineanalytics.com/docs/api#exports).
- **`rollups_refreshed_at`**: numbers come from rollups refreshed every 10 minutes; today's totals are as of this time.

See [Limits and conventions](https://punchlineanalytics.com/docs/api-conventions) for dates, rate limits and pagination.
