punchline!

API

API

Read your numbers from scripts, BI tools and your own agents. The API answers exactly what the dashboard shows, because both use the same query layer. With the right key, you can also send events and manage alerts, goals, triggers and URL parameters.

Guides: Keys and authentication · Query cookbook · Limits and conventions · TypeScript SDK · API reference, with a real example response for every endpoint.

Keys

Create a key in Site settings → API and AI access. It's shown once, so copy it then; revoke keys on the same page. More on keys.

  • Send it as a Bearer token: Authorization: Bearer pk_live_….
  • One key works on one site, and only that site.
  • Every key can read. Tick Can send events for POST /v1/events, and Can manage for the endpoints under Managing your site.
  • Rate limit: 120 requests a minute per key. Responses carry RateLimit-Limit, RateLimit-Remaining and RateLimit-Reset; a 429 includes Retry-After. See Limits and conventions.

Your first request

curl -s https://app.punchlineanalytics.com/v1/query \
  -H "Authorization: Bearer $PUNCHLINE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "site_id": "<your site id>",
    "date_range": { "from": "2026-09-01", "to": "2026-09-30" },
    "metrics": ["sessions", "agent_share"],
    "dimensions": ["day"]
  }'

The response echoes the query as it ran (with defaults filled in), then results and meta:

{
  "query": { "site_id": "…", "metrics": ["sessions", "agent_share"], "dimensions": ["day"], "confidence": "likely", "…": "…" },
  "results": [{ "day": "2026-09-01", "sessions": 412, "agent_share": 0.31 }],
  "meta": { "audience": "external", "min_cohort": 5, "suppressed_rows": 0, "truncated": false, "rollups_refreshed_at": "…" }
}

The query

POST /v1/query does almost everything: pick metrics, group by dimensions, narrow with filters.

Metrics
sessions, pageviews Everyone
human_sessions, agent_sessions Split by visitor type (agents include crawlers)
agent_share agent_sessions / sessions
thin_pageviews Successful responses under 2 KB (possibly JavaScript-only)
events, event_sessions, event_value Custom events (with the event_name dimension)
conversions, conversion_rate Goal conversions (with the goal dimension; path is the landing page)
visitors Distinct visitors per day, summed over the range
bounces, bounce_rate, avg_session_duration, pages_per_session, engaged_sessions, engagement_rate Session engagement (path is the landing page; also exit_path, country, device_class, browser_family, os_family)
exits, exit_rate, avg_time_on_page Per-page engagement

Dimensions (up to 4): day, week, month, path, visitor_type, actor_class, operator, tier, referrer_host, assistant, utm_source, utm_medium, utm_campaign, utm_term, utm_content, agent, agent_purpose, delivery, content_type, topic, channel, partner, exit_path, country, device_class, browser_family, os_family, plus param:<name> for each URL parameter the site allows (page and conversion metrics). See Concepts and Reports for what each value means.

Filters (up to 10) are [field, op, value], with ops eq, neq, in, not_in, starts_with, is_null, not_null. ["is_entry", "eq", true] keeps only the first page of each session (landing pages):

{
  "date_range": { "from": "2026-09-01", "to": "2026-09-30" },
  "metrics": ["sessions"],
  "dimensions": ["assistant", "path"],
  "filters": [["actor_class", "eq", "ai_referred_human"], ["is_entry", "eq", true]],
  "order_by": [["sessions", "desc"]],
  "limit": 20
}

confidence (confirmed, likely (default), all) sets which agent labels count as agents.

The query cookbook has a ready-made body for most questions.

Other endpoints

Endpoint Returns
POST /v1/events Send custom events from your servers (Events and goals)
GET /v1/sites, /v1/sites/{id} Your site and how it's installed
GET /v1/operators Known agent operators and how each is verified
GET /v1/sessions Agent and crawler sessions, newest first; page with cursor
GET /v1/sessions/{id}, /v1/sessions/{id}/evidence One agent session, and why it was labelled an agent
GET /v1/content/funnel, /v1/content/recommendations Agent content
GET /v1/reconciliation GA4 comparison
GET /v1/accounts Companies whose agents and people researched you, by intent, with a signal strength (Accounts)
POST /v1/handoffs, GET /v1/handoffs Mint a handoff link for an agent (events:write), and see links opened and converted (Handoff links)
GET /v1/ai-influence The post-conversion survey's estimate of AI-assisted conversions, against what links show (Events and goals)
GET /v1/resolution Where agent sessions ran (in someone's browser, the cloud, a local tool) and how many joined a person or a company
GET /v1/reports Saved reports shared with everyone on the site, with their Explore settings (Reports)

Exports

For pulls bigger than a query returns, POST /v1/exports takes the same body as /v1/query with a limit of up to 100,000 rows (the default), and keeps the result as CSV for 24 hours. Rows withheld for privacy stay withheld (suppressed_rows says how many).

curl https://app.punchlineanalytics.com/v1/exports -H "Authorization: Bearer $PUNCHLINE_KEY" \
  -d '{"date_range": {"from": "2026-09-01", "to": "2026-09-30"}, "metrics": ["pageviews"], "dimensions": ["day", "path", "operator"]}'
# → {"data": {"id": "…", "rows": 8412, "download_url": "https://…/v1/exports/…/download", …}}
curl --compressed -o september.csv -H "Authorization: Bearer $PUNCHLINE_KEY" "<download_url>"

Add "format": "parquet" for a Parquet file instead (typed columns, no gzip wrapper needed).

The CSV file is sent gzip-encoded, so use --compressed with curl (browsers and fetch decompress on their own). It's capped at 4 MB compressed; narrow the range or dimensions if you hit that. Cells that start with =, +, - or @ get a leading ', so a page path crafted as a formula can't run in a spreadsheet.

Managing your site

With a key that can manage (the admin scope):

Endpoint
GET, POST /v1/alerts; GET, PATCH, DELETE /v1/alerts/{id} Alerts
GET, POST /v1/goals; GET, PATCH, DELETE /v1/goals/{id} Goals
GET, POST /v1/triggers; GET, PATCH, DELETE /v1/triggers/{id} No-code event triggers; changing one gives it a new version
GET, PATCH, DELETE /v1/url-parameters The URL-parameter allowlist and rules

PATCH takes only the fields you're changing. Writes accept Idempotency-Key, so a retry never creates a second alert. Validation is the same as in the dashboard, with the reason in error.message:

curl https://app.punchlineanalytics.com/v1/alerts \
  -H "Authorization: Bearer $PUNCHLINE_KEY" -H "Idempotency-Key: agent-share-1" \
  -d '{"name": "Agent share spike", "kind": "anomaly", "metric": "agent_share", "op": "up", "value": 50}'

Every parameter and response is in the API reference, also available as OpenAPI 3.1 at /v1/openapi.json.

TypeScript

@punchline-analytics/sdk is a typed client generated from the same OpenAPI spec, so its types always match the API. Install it with npm install @punchline-analytics/sdk; see TypeScript SDK for every method.

import { Punchline } from "@punchline-analytics/sdk";

const punchline = new Punchline({ apiKey: process.env.PUNCHLINE_API_KEY! });

const { results } = await punchline.query({
  site_id: "<your site id>",
  date_range: { from: "2026-09-01", to: "2026-09-30" },
  metrics: ["sessions", "agent_share"],
  dimensions: ["agent_purpose"],
});

// Every agent session, page by page:
for await (const session of punchline.allSessions({ from: "2026-09-24", to: "2026-09-30" })) {
  console.log(session.type, session.operator, session.paths);
}

Errors throw PunchlineError with status, code and param; a 429 is retried after its Retry-After.

Errors

Every error has the same shape, with the parameter at fault when there is one:

{ "error": { "type": "invalid_request_error", "code": "invalid_parameter", "message": "limit: must be at most 1000", "param": "limit", "docs_url": "…/v1/errors#invalid_parameter" } }

Privacy

Outside your own dashboard, any group containing fewer than 5 people's page views is withheld and counted in meta.suppressed_rows. Groups of only agents and crawlers are always shown. Sessions of people are never returned; agent sessions are.

Warehouse export

To get data into BigQuery or Snowflake every day without calling the API, set up Warehouse export in Site settings. Each morning it writes yesterday's traffic, conversions, events and agent sessions into tables in your dataset or schema. It replaces each day's rows, so reruns are safe. For BigQuery, you share one dataset with Punchline's service account as BigQuery Data Editor and label it with your site ID; Snowflake uses a key pair Punchline generates, plus a setup script to run once. The Snowflake private key is stored encrypted. The same privacy rules apply as for the API.

Ask the docs

Answers come from these docs only, written by Claude (Anthropic). Your question is sent to answer it, and isn't stored.