punchline!

API

API reference

Read your site's traffic split into people, AI agents and crawlers, with the evidence behind every label. Authenticate with an API key as a Bearer token. Each key is bound to one site. Outside your own dashboard, groups with fewer than 5 people's page views are withheld (meta.suppressed_rows), and sessions of people are never returned.

Base URL: https://app.punchlineanalytics.com. Authenticate with Authorization: Bearer pk_live_… (keys and scopes). Generated from the OpenAPI 3.1 spec that the API is tested against; every example below is a real response, recorded by calling the endpoint in our test suite against a sample site, so they can't drift from what the API returns.

Query

Metrics by dimensions, and exports for bigger pulls.

POST /v1/query

Metrics by dimensions, with filters. The main endpoint.

Scope: read (every key) · SDK: punchline.query()

Body (JSON)

Field Type Required Description
site_id string (uuid) yes
date_range object yes
metrics list of sessions, pageviews, human_sessions, agent_sessions, agent_share, thin_pageviews, events, event_sessions, event_value, conversions, conversion_rate, visitors, bounces, bounce_rate, avg_session_duration, pages_per_session, engaged_sessions, engagement_rate, exits, exit_rate, avg_time_on_page yes
dimensions list of string
filters list of any
confidence confirmed, likely, all How strict to be about agent labels. confirmed: verified signatures and declared crawlers only. likely (default): adds high-confidence inferences. all: adds low-confidence patterns. Weaker labels count as human.
order_by list of any
limit integer (1–1000) Default 100

Example

curl -X POST "https://app.punchlineanalytics.com/v1/query" \
  -H "Authorization: Bearer $PUNCHLINE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "site_id": "$PUNCHLINE_SITE_ID",
    "date_range": {
      "from": "2026-10-06",
      "to": "2026-10-06"
    },
    "metrics": [
      "sessions",
      "agent_share"
    ],
    "dimensions": [
      "visitor_type"
    ]
  }'

Response 200:

{
  "query": {
    "site_id": "25cbb06c-e534-4f14-a740-785079ddc69c",
    "date_range": {
      "from": "2026-10-06",
      "to": "2026-10-06"
    },
    "metrics": [
      "sessions",
      "agent_share"
    ],
    "dimensions": [
      "visitor_type"
    ],
    "filters": [],
    "confidence": "likely",
    "order_by": [
      [
        "sessions",
        "desc"
      ]
    ],
    "limit": 100
  },
  "results": [
    {
      "visitor_type": "human",
      "sessions": 9,
      "agent_share": 0
    },
    {
      "visitor_type": "ai_referred",
      "sessions": 6,
      "agent_share": 0
    },
    {
      "visitor_type": "crawler",
      "sessions": 1,
      "agent_share": 1
    },
    {
      "visitor_type": "agent",
      "sessions": 1,
      "agent_share": 1
    }
  ],
  "meta": {
    "audience": "external",
    "min_cohort": 5,
    "suppressed_rows": 0,
    "rows_before_limit": 4,
    "truncated": false,
    "rollups_refreshed_at": "2026-10-06T15:00:00.000Z"
  },
  "dashboard_url": "https://app.punchlineanalytics.com/dashboard?site=25cbb06c-e534-4f14-a740-785079ddc69c"
}

Responses

POST /v1/exports

Run a query (the /v1/query model, limit up to 100,000, default 100,000) and keep the result as CSV for 24 hours. Same privacy suppression as /v1/query.

Scope: read (every key) · SDK: punchline.createExport()

Body (JSON)

Field Type Required Description
site_id string (uuid) yes
format csv
date_range object yes
metrics list of sessions, pageviews, human_sessions, agent_sessions, agent_share, thin_pageviews, events, event_sessions, event_value, conversions, conversion_rate, visitors, bounces, bounce_rate, avg_session_duration, pages_per_session, engaged_sessions, engagement_rate, exits, exit_rate, avg_time_on_page yes
dimensions list of string
filters list of any
confidence confirmed, likely, all How strict to be about agent labels. confirmed: verified signatures and declared crawlers only. likely (default): adds high-confidence inferences. all: adds low-confidence patterns. Weaker labels count as human.
order_by list of any
limit integer (1–100000)

Example

curl -X POST "https://app.punchlineanalytics.com/v1/exports" \
  -H "Authorization: Bearer $PUNCHLINE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "site_id": "$PUNCHLINE_SITE_ID",
    "date_range": {
      "from": "2026-10-06",
      "to": "2026-10-06"
    },
    "metrics": [
      "pageviews"
    ],
    "dimensions": [
      "path",
      "visitor_type"
    ]
  }'

Response 201:

{
  "data": {
    "id": "9ce4690d-fbee-412d-af39-cb4722e35406",
    "status": "ready",
    "format": "csv",
    "query": {
      "date_range": {
        "from": "2026-10-06",
        "to": "2026-10-06"
      },
      "metrics": [
        "pageviews"
      ],
      "dimensions": [
        "path",
        "visitor_type"
      ],
      "filters": [],
      "confidence": "likely",
      "order_by": [
        [
          "pageviews",
          "desc"
        ]
      ],
      "limit": 100000
    },
    "rows": 11,
    "suppressed_rows": 0,
    "truncated": false,
    "bytes": 142,
    "created_at": "2026-10-06T15:00:00.000Z",
    "expires_at": "2026-10-07T15:00:00.000Z",
    "download_url": "https://app.punchlineanalytics.com/v1/exports/9ce4690d-fbee-412d-af39-cb4722e35406/download"
  }
}

Responses

GET /v1/exports/{id}

An export's size and download link.

Scope: read (every key) · SDK: punchline.getExport()

Parameter In Type Required Description
id path string (uuid) yes The export's ID.

Example

curl "https://app.punchlineanalytics.com/v1/exports/9ce4690d-fbee-412d-af39-cb4722e35406" \
  -H "Authorization: Bearer $PUNCHLINE_API_KEY"

Response 200:

{
  "data": {
    "id": "9ce4690d-fbee-412d-af39-cb4722e35406",
    "status": "ready",
    "format": "csv",
    "query": {
      "date_range": {
        "from": "2026-10-06",
        "to": "2026-10-06"
      },
      "metrics": [
        "pageviews"
      ],
      "dimensions": [
        "path",
        "visitor_type"
      ],
      "filters": [],
      "confidence": "likely",
      "order_by": [
        [
          "pageviews",
          "desc"
        ]
      ],
      "limit": 100000
    },
    "rows": 11,
    "suppressed_rows": 0,
    "truncated": false,
    "bytes": 142,
    "created_at": "2026-10-06T15:00:00.000Z",
    "expires_at": "2026-10-07T15:00:00.000Z",
    "download_url": "https://app.punchlineanalytics.com/v1/exports/9ce4690d-fbee-412d-af39-cb4722e35406/download"
  }
}

Responses

GET /v1/exports/{id}/download

The CSV, gzip-encoded (Content-Encoding: gzip; with curl, use --compressed). Cells starting with = + - @ are prefixed with ' so spreadsheets don't run them.

Scope: read (every key) · SDK: punchline.downloadExport()

Parameter In Type Required Description
id path string (uuid) yes The export's ID.

Responses

Your site and agents

The site behind the key, known operators, and agent sessions with their evidence.

GET /v1/sites

The site this key can read, with setup status.

Scope: read (every key) · SDK: punchline.listSites()

Example

curl "https://app.punchlineanalytics.com/v1/sites" \
  -H "Authorization: Bearer $PUNCHLINE_API_KEY"

Response 200:

{
  "data": [
    {
      "id": "25cbb06c-e534-4f14-a740-785079ddc69c",
      "name": "Shop",
      "domain": "shop.example",
      "setup": {
        "edge_install_reporting": true,
        "snippet_reporting": false,
        "browser_only": false,
        "ga4_connected": true
      },
      "url_params": []
    }
  ]
}

Responses

GET /v1/sites/{id}

One site.

Scope: read (every key) · SDK: punchline.getSite()

Parameter In Type Required Description
id path string (uuid) yes

Example

curl "https://app.punchlineanalytics.com/v1/sites/$PUNCHLINE_SITE_ID" \
  -H "Authorization: Bearer $PUNCHLINE_API_KEY"

Response 200:

{
  "data": {
    "id": "25cbb06c-e534-4f14-a740-785079ddc69c",
    "name": "Shop",
    "domain": "shop.example",
    "setup": {
      "edge_install_reporting": true,
      "snippet_reporting": false,
      "browser_only": false,
      "ga4_connected": true
    },
    "url_params": []
  }
}

Responses

GET /v1/operators

Known agent operators and how each is verified.

Scope: read (every key) · SDK: punchline.listOperators()

Example

curl "https://app.punchlineanalytics.com/v1/operators" \
  -H "Authorization: Bearer $PUNCHLINE_API_KEY"

Response 200:

{
  "registry_version": "2026-10-25.1",
  "data": [
    {
      "name": "GPTBot",
      "operator": "openai",
      "type": "crawler",
      "actor_class": "crawler",
      "verified_by": [
        "published_ip_ranges"
      ],
      "docs_url": "https://platform.openai.com/docs/bots"
    },
    {
      "name": "OAI-SearchBot",
      "operator": "openai",
      "type": "crawler",
      "actor_class": "crawler",
      "verified_by": [
        "published_ip_ranges"
      ],
      "docs_url": "https://platform.openai.com/docs/bots"
    }
  ]
}

Responses

GET /v1/sessions

Agent and crawler sessions, newest first. Page with cursor=next_cursor.

Scope: read (every key) · SDK: punchline.listSessions()

Parameter In Type Required Description
site_id query string (uuid) Optional; must be the API key's site.
from query string (date) First UTC day, YYYY-MM-DD. Default: 6 days before to.
to query string (date) Last UTC day, YYYY-MM-DD, inclusive. Default: today.
actor_class query signed_agent, declared_agent, crawler, fetcher, suspected_agent, spoofed_bot
operator query string
tier query verified, declared, inferred_high, inferred_low
limit query integer (1–100) Default 25
cursor query string

Example

curl "https://app.punchlineanalytics.com/v1/sessions?site_id=$PUNCHLINE_SITE_ID&from=2026-10-06&to=2026-10-06&limit=2" \
  -H "Authorization: Bearer $PUNCHLINE_API_KEY"

Response 200:

{
  "data": [
    {
      "session_id": "d9828ab0-ecf1-4046-a47f-dabd6a37e60e",
      "type": "declared agent",
      "actor_class": "declared_agent",
      "operator": "openai",
      "operator_docs": "https://platform.openai.com/docs/bots",
      "tier": "inferred_high",
      "started_at": "2026-10-06T13:30:00.000Z",
      "ended_at": "2026-10-06T13:32:00.000Z",
      "pageviews": 3,
      "paths": [
        "/pricing",
        "/security",
        "/compare/acme-vs-shop"
      ],
      "rules": [
        {
          "id": "R2u",
          "description": "Declares a known bot that couldn't be confirmed: no IP data, DNS failed, or an address outside the operator's published list on a network it's known to use"
        }
      ],
      "evidence": {
        "ua_claims": "ChatGPT-User",
        "claim_verification": "unavailable",
        "claim": "unverifiable",
        "asn": 16509
      }
    },
    {
      "session_id": "4d7e7cb2-df1a-40c3-a4a2-54c208b008f2",
      "type": "crawler",
      "actor_class": "crawler",
      "operator": "openai",
      "operator_docs": "https://platform.openai.com/docs/bots",
      "tier": "declared",
      "started_at": "2026-10-06T13:00:00.000Z",
      "ended_at": "2026-10-06T13:03:00.000Z",
      "pageviews": 4,
      "paths": [
        "/",
        "/pricing",
        "/docs/api",
        "/security"
      ],
      "rules": [
        {
          "id": "R2",
          "description": "Declares a known bot, and its IP matches the operator's published ranges or reverse DNS"
        }
      ],
      "evidence": {
        "ua_claims": "GPTBot",
        "ip_in_published_range": true,
        "claim": "confirmed"
      }
    }
  ],
  "next_cursor": null
}

Responses

GET /v1/sessions/{id}

One agent session as a request timeline.

Scope: read (every key) · SDK: punchline.getSession()

Parameter In Type Required Description
id path string (uuid) yes

Example

curl "https://app.punchlineanalytics.com/v1/sessions/d9828ab0-ecf1-4046-a47f-dabd6a37e60e?site_id=$PUNCHLINE_SITE_ID" \
  -H "Authorization: Bearer $PUNCHLINE_API_KEY"

Response 200:

{
  "data": {
    "session_id": "d9828ab0-ecf1-4046-a47f-dabd6a37e60e",
    "type": "declared agent",
    "actor_class": "declared_agent",
    "operator": "openai",
    "operator_docs": "https://platform.openai.com/docs/bots",
    "tier": "inferred_high",
    "started_at": "2026-10-06T13:30:00.000Z",
    "ended_at": "2026-10-06T13:32:00.000Z",
    "pageviews": 3,
    "paths": [
      "/pricing",
      "/security",
      "/compare/acme-vs-shop"
    ],
    "rules": [
      {
        "id": "R2u",
        "description": "Declares a known bot that couldn't be confirmed: no IP data, DNS failed, or an address outside the operator's published list on a network it's known to use"
      }
    ],
    "evidence": {
      "ua_claims": "ChatGPT-User",
      "claim_verification": "unavailable",
      "claim": "unverifiable",
      "asn": 16509
    }
  },
  "dashboard_url": "https://app.punchlineanalytics.com/dashboard/agents/sessions?site=25cbb06c-e534-4f14-a740-785079ddc69c&operator=openai&class=declared_agent&tier=inferred_high"
}

Responses

GET /v1/sessions/{id}/evidence

Why this session was labelled an agent: rules, evidence, tier.

Scope: read (every key) · SDK: punchline.getSessionEvidence()

Parameter In Type Required Description
id path string (uuid) yes

Example

curl "https://app.punchlineanalytics.com/v1/sessions/d9828ab0-ecf1-4046-a47f-dabd6a37e60e/evidence?site_id=$PUNCHLINE_SITE_ID" \
  -H "Authorization: Bearer $PUNCHLINE_API_KEY"

Response 200:

{
  "data": {
    "session_id": "d9828ab0-ecf1-4046-a47f-dabd6a37e60e",
    "type": "declared agent",
    "actor_class": "declared_agent",
    "operator": "openai",
    "tier": "inferred_high",
    "rules": [
      {
        "id": "R2u",
        "description": "Declares a known bot that couldn't be confirmed: no IP data, DNS failed, or an address outside the operator's published list on a network it's known to use"
      }
    ],
    "evidence": {
      "ua_claims": "ChatGPT-User",
      "claim_verification": "unavailable",
      "claim": "unverifiable",
      "asn": 16509
    }
  }
}

Responses

Reports

The dashboard's reports as JSON.

GET /v1/handoffs

Handoff links created in a date range: opened by a person, opened by an agent, and converted (the opener's session completed a goal), with values, per kind and agent operator.

Scope: read (every key) · SDK: punchline.getHandoffs()

Parameter In Type Required Description
site_id query string (uuid) Optional; must be the API key's site.
from query string (date) First UTC day, YYYY-MM-DD. Default: 6 days before to.
to query string (date) Last UTC day, YYYY-MM-DD, inclusive. Default: today.

Example

curl "https://app.punchlineanalytics.com/v1/handoffs?site_id=$PUNCHLINE_SITE_ID&from=2026-10-06&to=2026-10-06" \
  -H "Authorization: Bearer $PUNCHLINE_API_KEY"

Response 200:

{
  "data": {
    "date_range": {
      "from": "2026-10-06",
      "to": "2026-10-06"
    },
    "totals": {
      "created": 1,
      "opened_by_person": 0,
      "opened_by_agent": 0,
      "converted": 0,
      "value_created": 249,
      "value_opened": 0,
      "value_converted": 0
    },
    "by_kind": [
      {
        "kind": "cart",
        "created": 1,
        "opened_by_person": 0,
        "opened_by_agent": 0,
        "converted": 0,
        "value_created": 249,
        "value_opened": 0,
        "value_converted": 0
      }
    ],
    "by_operator": [
      {
        "operator": "openai",
        "created": 1,
        "opened_by_person": 0,
        "opened_by_agent": 0,
        "converted": 0,
        "value_created": 249,
        "value_opened": 0,
        "value_converted": 0
      }
    ]
  },
  "dashboard_url": "https://app.punchlineanalytics.com/dashboard/handoffs?site=25cbb06c-e534-4f14-a740-785079ddc69c"
}

Responses

GET /v1/content/funnel

Agent content funnel: fetched (live vs crawl) → delivered → cited, per page, content type or topic.

Scope: read (every key) · SDK: punchline.getContentFunnel()

Parameter In Type Required Description
site_id query string (uuid) Optional; must be the API key's site.
from query string (date) First UTC day, YYYY-MM-DD. Default: 6 days before to.
to query string (date) Last UTC day, YYYY-MM-DD, inclusive. Default: today.
confidence query confirmed, likely, all How strict to be about agent labels. confirmed: verified signatures and declared crawlers only. likely (default): adds high-confidence inferences. all: adds low-confidence patterns. Weaker labels count as human.
group_by query path, content_type, topic Group pages by path (default), content type or topic.
limit query integer (1–200) Default 25

Example

curl "https://app.punchlineanalytics.com/v1/content/funnel?site_id=$PUNCHLINE_SITE_ID&from=2026-10-06&to=2026-10-06&limit=3" \
  -H "Authorization: Bearer $PUNCHLINE_API_KEY"

Response 200:

{
  "data": {
    "date_range": {
      "from": "2026-10-06",
      "to": "2026-10-06"
    },
    "group_by": "path",
    "rows": [
      {
        "key": "/pricing",
        "fetches": {
          "total": 2,
          "live": 1,
          "crawl": 1,
          "other": 0
        },
        "delivery": {
          "known": 2,
          "ok": 2,
          "redirect": 0,
          "failed": 0,
          "not_found": 0,
          "blocked": 0,
          "thin": 0
        },
        "delivered_share": 1,
        "cited": 6,
        "citation_rate": 600,
        "converted": 6,
        "impostor_fetches": 0
      },
      {
        "key": "/security",
        "fetches": {
          "total": 2,
          "live": 1,
          "crawl": 1,
          "other": 0
        },
        "delivery": {
          "known": 2,
          "ok": 2,
          "redirect": 0,
          "failed": 0,
          "not_found": 0,
          "blocked": 0,
          "thin": 0
        },
        "delivered_share": 1,
        "cited": 0,
        "citation_rate": 0,
        "converted": 0,
        "impostor_fetches": 0
      },
      {
        "key": "/",
        "fetches": {
          "total": 1,
          "live": 0,
          "crawl": 1,
          "other": 0
        },
        "delivery": {
          "known": 1,
          "ok": 1,
          "redirect": 0,
          "failed": 0,
          "not_found": 0,
          "blocked": 0,
          "thin": 0
        },
        "delivered_share": 1,
        "cited": 0,
        "citation_rate": null,
        "converted": 0,
        "impostor_fetches": 0
      }
    ],
    "totals": {
      "key": "all",
      "fetches": {
        "total": 7,
        "live": 3,
        "crawl": 4,
        "other": 0
      },
      "delivery": {
        "known": 7,
        "ok": 7,
        "redirect": 0,
        "failed": 0,
        "not_found": 0,
        "blocked": 0,
        "thin": 0
      },
      "delivered_share": 1,
      "cited": 6,
      "citation_rate": 200,
      "converted": 6,
      "impostor_fetches": 0
    },
    "delivery_coverage": 1,
    "confidence": "likely"
  },
  "dashboard_url": "https://app.punchlineanalytics.com/dashboard/content?site=25cbb06c-e534-4f14-a740-785079ddc69c&confidence=likely"
}

Responses

GET /v1/content/recommendations

Evidence-based things to try so assistants can read and cite the site, with the previous period for comparison.

Scope: read (every key) · SDK: punchline.getRecommendations()

Parameter In Type Required Description
site_id query string (uuid) Optional; must be the API key's site.
from query string (date) First UTC day, YYYY-MM-DD. Default: 6 days before to.
to query string (date) Last UTC day, YYYY-MM-DD, inclusive. Default: today.
confidence query confirmed, likely, all How strict to be about agent labels. confirmed: verified signatures and declared crawlers only. likely (default): adds high-confidence inferences. all: adds low-confidence patterns. Weaker labels count as human.

Example

curl "https://app.punchlineanalytics.com/v1/content/recommendations?site_id=$PUNCHLINE_SITE_ID&from=2026-10-06&to=2026-10-06" \
  -H "Authorization: Bearer $PUNCHLINE_API_KEY"

Response 200:

{
  "data": {
    "date_range": {
      "from": "2026-10-06",
      "to": "2026-10-06"
    },
    "previous_range": {
      "from": "2026-10-05",
      "to": "2026-10-05"
    },
    "delivery_coverage": 1,
    "findings": [],
    "confidence": "likely"
  },
  "dashboard_url": "https://app.punchlineanalytics.com/dashboard/content?site=25cbb06c-e534-4f14-a740-785079ddc69c&confidence=likely"
}

Responses

GET /v1/accounts

Agent intent by company: what each company's agents and people read, grouped by intent, with a strength and a summary. Account-level only.

Scope: read (every key) · SDK: punchline.getAccounts()

Parameter In Type Required Description
site_id query string (uuid) Optional; must be the API key's site.
from query string (date) First UTC day, YYYY-MM-DD. Default: 6 days before to.
to query string (date) Last UTC day, YYYY-MM-DD, inclusive. Default: today.
confidence query confirmed, likely, all How strict to be about agent labels. confirmed: verified signatures and declared crawlers only. likely (default): adds high-confidence inferences. all: adds low-confidence patterns. Weaker labels count as human.
company_domain query string One company, e.g. acme.com
min_strength query light, moderate, strong Default light (all accounts)
limit query integer (1–200) Default 25

Example

curl "https://app.punchlineanalytics.com/v1/accounts?site_id=$PUNCHLINE_SITE_ID&from=2026-10-06&to=2026-10-06" \
  -H "Authorization: Bearer $PUNCHLINE_API_KEY"

Response 200:

{
  "data": {
    "date_range": {
      "from": "2026-10-06",
      "to": "2026-10-06"
    },
    "confidence": "likely",
    "accounts": [
      {
        "company_domain": "acme.com",
        "method": "form_domain",
        "strength": "moderate",
        "score": 9,
        "summary": "Agents read pricing, security and comparison pages 3 times",
        "agents": {
          "sessions": 1,
          "pageviews": 3,
          "operators": [
            "openai"
          ],
          "runtimes": [
            "cloud"
          ]
        },
        "people": null,
        "intents": [
          {
            "intent": "pricing",
            "label": "Pricing",
            "agent_pageviews": 1,
            "people_pageviews": null
          },
          {
            "intent": "security",
            "label": "Security & compliance",
            "agent_pageviews": 1,
            "people_pageviews": null
          },
          {
            "intent": "comparison",
            "label": "Comparisons",
            "agent_pageviews": 1,
            "people_pageviews": null
          }
        ],
        "previous_agent_sessions": 0,
        "first_seen": "2026-10-06",
        "last_seen": "2026-10-06"
      }
    ],
    "meta": {
      "withheld_people": 0
    }
  },
  "dashboard_url": "https://app.punchlineanalytics.com/dashboard/accounts?site=25cbb06c-e534-4f14-a740-785079ddc69c&confidence=likely"
}

Responses

GET /v1/ai-influence

Estimated AI-assisted conversions from the post-conversion survey (ai_assist_yes / _no / _not_sure events), with a 95% interval, against conversions Punchline connects directly, and the multiplier.

Scope: read (every key) · SDK: punchline.getAiInfluence()

Parameter In Type Required Description
site_id query string (uuid) Optional; must be the API key's site.
from query string (date) First UTC day, YYYY-MM-DD. Default: 6 days before to.
to query string (date) Last UTC day, YYYY-MM-DD, inclusive. Default: today.
confidence query confirmed, likely, all How strict to be about agent labels. confirmed: verified signatures and declared crawlers only. likely (default): adds high-confidence inferences. all: adds low-confidence patterns. Weaker labels count as human.
goal query string One goal's conversions; default all goals

Example

curl "https://app.punchlineanalytics.com/v1/ai-influence?site_id=$PUNCHLINE_SITE_ID&from=2026-10-06&to=2026-10-06" \
  -H "Authorization: Bearer $PUNCHLINE_API_KEY"

Response 200:

{
  "data": {
    "date_range": {
      "from": "2026-10-06",
      "to": "2026-10-06"
    },
    "confidence": "likely",
    "responses": {
      "yes": 14,
      "no": 22,
      "not_sure": 6
    },
    "yes_share": 0.3888888888888889,
    "yes_share_interval": [
      0.24784655417822943,
      0.5513582970325652
    ],
    "enough_responses": true,
    "people_conversions": 6,
    "estimated_ai_assisted": {
      "low": 1.4870793250693768,
      "mid": 2.3333333333333335,
      "high": 3.3081497821953914
    },
    "observed": {
      "ai_referred": 6,
      "handoffs": 0,
      "total": 6
    },
    "multiplier": 0.3888888888888889
  },
  "dashboard_url": "https://app.punchlineanalytics.com/dashboard/goals?site=25cbb06c-e534-4f14-a740-785079ddc69c&confidence=likely"
}

Responses

GET /v1/resolution

Where agent sessions ran (in a person's browser, cloud, local tool) and how many joined a person's visit or a company the same day.

Scope: read (every key) · SDK: punchline.getResolution()

Parameter In Type Required Description
site_id query string (uuid) Optional; must be the API key's site.
from query string (date) First UTC day, YYYY-MM-DD. Default: 6 days before to.
to query string (date) Last UTC day, YYYY-MM-DD, inclusive. Default: today.
confidence query confirmed, likely, all How strict to be about agent labels. confirmed: verified signatures and declared crawlers only. likely (default): adds high-confidence inferences. all: adds low-confidence patterns. Weaker labels count as human.

Example

curl "https://app.punchlineanalytics.com/v1/resolution?site_id=$PUNCHLINE_SITE_ID&from=2026-10-06&to=2026-10-06" \
  -H "Authorization: Bearer $PUNCHLINE_API_KEY"

Response 200:

{
  "data": {
    "date_range": {
      "from": "2026-10-06",
      "to": "2026-10-06"
    },
    "confidence": "likely",
    "agents": {
      "sessions": 1,
      "by_runtime": {
        "in_browser": 0,
        "cloud": 1,
        "local": 0,
        "unknown": 0
      },
      "in_browser_share": 0,
      "joined_person": 0,
      "joined_company": 1,
      "methods": {
        "same_day_visit": 0,
        "company_domain": 1,
        "company_ip": 0,
        "handoff": 0,
        "any": 1
      },
      "connected_by_runtime": {
        "in_browser": 0,
        "cloud": 1,
        "local": 0,
        "unknown": 0
      }
    },
    "crawlers": {
      "sessions": 1
    },
    "days": [
      {
        "day": "2026-10-06",
        "in_browser": 0,
        "cloud": 1,
        "local": 0,
        "unknown": 0
      }
    ],
    "operators": [
      {
        "operator": "openai",
        "sessions": 1,
        "by_runtime": {
          "in_browser": 0,
          "cloud": 1,
          "local": 0,
          "unknown": 0
        },
        "joined_person": 0
      }
    ]
  },
  "dashboard_url": "https://app.punchlineanalytics.com/dashboard/resolution?site=25cbb06c-e534-4f14-a740-785079ddc69c&confidence=likely"
}

Responses

GET /v1/reconciliation

GA4 sessions vs Punchline's, with the gap explained. Needs GA4 connected.

Scope: read (every key) · SDK: punchline.getReconciliation()

Parameter In Type Required Description
site_id query string (uuid) Optional; must be the API key's site.
from query string (date) First UTC day, YYYY-MM-DD. Default: 6 days before to.
to query string (date) Last UTC day, YYYY-MM-DD, inclusive. Default: today.
confidence query confirmed, likely, all How strict to be about agent labels. confirmed: verified signatures and declared crawlers only. likely (default): adds high-confidence inferences. all: adds low-confidence patterns. Weaker labels count as human.

Example

curl "https://app.punchlineanalytics.com/v1/reconciliation?site_id=$PUNCHLINE_SITE_ID&from=2026-10-06&to=2026-10-06" \
  -H "Authorization: Bearer $PUNCHLINE_API_KEY"

Response 200:

{
  "data": {
    "date_range": {
      "from": "2026-10-06",
      "to": "2026-10-06"
    },
    "confidence": "likely",
    "totals": {
      "ga4": 14,
      "ourHumans": 15,
      "agentsGa4NeverSees": 2,
      "agentsGa4CountsAsPeople": 0,
      "unexplained": -1,
      "daysWithGa4": 1
    },
    "days": [
      {
        "day": "2026-10-06",
        "ga4": 14,
        "ourHumans": 15,
        "agentsGa4NeverSees": 2,
        "agentsGa4CountsAsPeople": 0,
        "unexplained": -1
      }
    ],
    "estimate_note": "agentsGa4CountsAsPeople is an estimate: browser-based agents run GA4's script, so GA4 likely counted them."
  },
  "dashboard_url": "https://app.punchlineanalytics.com/dashboard/reconciliation?site=25cbb06c-e534-4f14-a740-785079ddc69c&confidence=likely"
}

Responses

GET /v1/reports

Saved reports shared with this site (private reports are never listed), with their Explore parameters: tm metric, d breakdown, f filters, tt chart type, tg granularity, range, who, confidence.

Scope: read (every key) · SDK: punchline.listReports()

Example

curl "https://app.punchlineanalytics.com/v1/reports" \
  -H "Authorization: Bearer $PUNCHLINE_API_KEY"

Response 200:

{
  "data": [
    {
      "id": "1a85afd3-851d-48e9-a9ae-aca544ff6ea9",
      "name": "AI referrals by assistant",
      "params": {
        "tm": "sessions",
        "bd": "assistant"
      },
      "updated_at": "2026-10-06T15:00:00.000Z"
    }
  ]
}

Responses

Send data

Events from your servers and handoff links for agents.

POST /v1/events

Send custom events from your servers (up to 1,000). Needs the events:write scope. Send Idempotency-Key to make retries safe.

Scope: events:write (keys that can send events) · SDK: punchline.sendEvents()

Parameter In Type Required Description
Idempotency-Key header string Retries with the same key and body return the first response.

Body (JSON)

Field Type Required Description
site_id string (uuid) yes
events list of EventInput yes

Example

curl -X POST "https://app.punchlineanalytics.com/v1/events" \
  -H "Authorization: Bearer $PUNCHLINE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "site_id": "$PUNCHLINE_SITE_ID",
    "events": [
      {
        "name": "signup",
        "value": 49,
        "currency": "USD",
        "props": {
          "plan": "team"
        }
      }
    ]
  }'

Response 202:

{
  "accepted": 1
}

Responses

POST /v1/handoffs

Mint a handoff link for an agent that built a cart, quote, configuration or booking. Give the agent's request (visitor) to tie the link to its session; when a person opens the link, the open is recorded. Needs the events:write scope. Only the token's hash is stored.

Scope: events:write (keys that can send events) · SDK: punchline.createHandoff()

Body (JSON)

Field Type Required Description
site_id string (uuid) yes
kind cart, quote, configuration, booking, other
value number (0–) The cart or quote total
currency string
expires_in_days integer (1–90)
url string A path or https URL on your site; the response adds the token to it
visitor object The agent's request, used in memory only

Example

curl -X POST "https://app.punchlineanalytics.com/v1/handoffs" \
  -H "Authorization: Bearer $PUNCHLINE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "site_id": "$PUNCHLINE_SITE_ID",
    "kind": "cart",
    "value": 249,
    "currency": "USD",
    "url": "/cart/8431",
    "visitor": {
      "ip": "3.0.0.9",
      "user_agent": "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko); compatible; ChatGPT-User/1.0; +https://openai.com/bot"
    }
  }'

Response 201:

{
  "data": {
    "token": "TLDqSZynF30ytN624lHQqPhX",
    "param": "pl_ho",
    "url": "https://shop.example/cart/8431?pl_ho=TLDqSZynF30ytN624lHQqPhX",
    "kind": "cart",
    "expires_at": "2026-11-05T15:00:00.000Z",
    "agent": {
      "session_found": false,
      "actor_class": "declared_agent",
      "operator": "openai"
    }
  }
}

Responses

Manage

Alerts, goals, event triggers and URL parameters. Needs the admin scope.

GET /v1/alerts

The site's alerts. Needs the admin scope.

Scope: admin (keys that can manage) · SDK: punchline.listAlerts()

Example

curl "https://app.punchlineanalytics.com/v1/alerts" \
  -H "Authorization: Bearer $PUNCHLINE_API_KEY"

Response 200:

{
  "data": [
    {
      "id": "5620320e-d3c1-4405-a004-6c9b1cf8e994",
      "name": "Agent share spike",
      "kind": "anomaly",
      "metric": "agent_share",
      "filters": [],
      "confidence": "likely",
      "op": "up",
      "value": 50,
      "min_volume": 20,
      "active": true,
      "description": "Agent share rises by 50% or more on the usual, when the usual volume is at least 20.",
      "created_at": "2026-10-06T15:00:00.000Z"
    }
  ]
}

Responses

POST /v1/alerts

Create a alert. Needs the admin scope.

Scope: admin (keys that can manage) · SDK: punchline.createAlert()

Parameter In Type Required Description
Idempotency-Key header string Retries with the same key and body return the first response.

Body (JSON)

Field Type Required Description
name string
kind threshold, anomaly, new_operator, account_signal
metric string A /v1/query metric
filters list of any
confidence confirmed, likely, all
op above, below, up, down, either
value number
min_volume integer (0–)
active boolean

Example

curl -X POST "https://app.punchlineanalytics.com/v1/alerts" \
  -H "Authorization: Bearer $PUNCHLINE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Agent share spike",
    "kind": "anomaly",
    "metric": "agent_share",
    "op": "up",
    "value": 50
  }'

Response 201:

{
  "data": {
    "id": "5620320e-d3c1-4405-a004-6c9b1cf8e994",
    "name": "Agent share spike",
    "kind": "anomaly",
    "metric": "agent_share",
    "filters": [],
    "confidence": "likely",
    "op": "up",
    "value": 50,
    "min_volume": 20,
    "active": true,
    "description": "Agent share rises by 50% or more on the usual, when the usual volume is at least 20.",
    "created_at": "2026-10-06T15:00:00.000Z"
  }
}

Responses

GET /v1/alerts/{id}

One alert.

Scope: admin (keys that can manage) · SDK: punchline.getAlert()

Parameter In Type Required Description
id path string (uuid) yes The alert's ID.

Example

curl "https://app.punchlineanalytics.com/v1/alerts/5620320e-d3c1-4405-a004-6c9b1cf8e994" \
  -H "Authorization: Bearer $PUNCHLINE_API_KEY"

Response 200:

{
  "data": {
    "id": "5620320e-d3c1-4405-a004-6c9b1cf8e994",
    "name": "Agent share spike",
    "kind": "anomaly",
    "metric": "agent_share",
    "filters": [],
    "confidence": "likely",
    "op": "up",
    "value": 50,
    "min_volume": 20,
    "active": true,
    "description": "Agent share rises by 50% or more on the usual, when the usual volume is at least 20.",
    "created_at": "2026-10-06T15:00:00.000Z"
  }
}

Responses

PATCH /v1/alerts/{id}

Change a alert: send only the fields to change.

Scope: admin (keys that can manage) · SDK: punchline.updateAlert()

Parameter In Type Required Description
id path string (uuid) yes The alert's ID.
Idempotency-Key header string Retries with the same key and body return the first response.

Body (JSON)

Field Type Required Description
name string
kind threshold, anomaly, new_operator, account_signal
metric string A /v1/query metric
filters list of any
confidence confirmed, likely, all
op above, below, up, down, either
value number
min_volume integer (0–)
active boolean

Example

curl -X PATCH "https://app.punchlineanalytics.com/v1/alerts/5620320e-d3c1-4405-a004-6c9b1cf8e994" \
  -H "Authorization: Bearer $PUNCHLINE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "value": 80
  }'

Response 200:

{
  "data": {
    "id": "5620320e-d3c1-4405-a004-6c9b1cf8e994",
    "name": "Agent share spike",
    "kind": "anomaly",
    "metric": "agent_share",
    "filters": [],
    "confidence": "likely",
    "op": "up",
    "value": 80,
    "min_volume": 20,
    "active": true,
    "description": "Agent share rises by 80% or more on the usual, when the usual volume is at least 20.",
    "created_at": "2026-10-06T15:00:00.000Z"
  }
}

Responses

DELETE /v1/alerts/{id}

Delete a alert.

Scope: admin (keys that can manage) · SDK: punchline.deleteAlert()

Parameter In Type Required Description
id path string (uuid) yes The alert's ID.
Idempotency-Key header string Retries with the same key and body return the first response.

Example

curl -X DELETE "https://app.punchlineanalytics.com/v1/alerts/5620320e-d3c1-4405-a004-6c9b1cf8e994" \
  -H "Authorization: Bearer $PUNCHLINE_API_KEY"

Response 204, no body.

Responses

GET /v1/goals

The site's goals. Needs the admin scope.

Scope: admin (keys that can manage) · SDK: punchline.listGoals()

Example

curl "https://app.punchlineanalytics.com/v1/goals" \
  -H "Authorization: Bearer $PUNCHLINE_API_KEY"

Response 200:

{
  "data": [
    {
      "id": "242cd0db-2502-4eb9-a311-f9fb4bdc7e41",
      "name": "Purchase",
      "kind": "page",
      "match": "=/thanks"
    },
    {
      "id": "398a21ce-1622-4d5d-a136-527782fc885c",
      "name": "Signed up",
      "kind": "event",
      "match": "signup"
    }
  ]
}

Responses

POST /v1/goals

Create a goal. Needs the admin scope.

Scope: admin (keys that can manage) · SDK: punchline.createGoal()

Parameter In Type Required Description
Idempotency-Key header string Retries with the same key and body return the first response.

Body (JSON)

Field Type Required Description
name string
kind event, page
match string An event name, or a path (=/path for exactly that page)

Example

curl -X POST "https://app.punchlineanalytics.com/v1/goals" \
  -H "Authorization: Bearer $PUNCHLINE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Signed up",
    "kind": "event",
    "match": "signup"
  }'

Response 201:

{
  "data": {
    "id": "398a21ce-1622-4d5d-a136-527782fc885c",
    "name": "Signed up",
    "kind": "event",
    "match": "signup"
  }
}

Responses

GET /v1/goals/{id}

One goal.

Scope: admin (keys that can manage) · SDK: punchline.getGoal()

Parameter In Type Required Description
id path string (uuid) yes The goal's ID.

Example

curl "https://app.punchlineanalytics.com/v1/goals/398a21ce-1622-4d5d-a136-527782fc885c" \
  -H "Authorization: Bearer $PUNCHLINE_API_KEY"

Response 200:

{
  "data": {
    "id": "398a21ce-1622-4d5d-a136-527782fc885c",
    "name": "Signed up",
    "kind": "event",
    "match": "signup"
  }
}

Responses

PATCH /v1/goals/{id}

Change a goal: send only the fields to change.

Scope: admin (keys that can manage) · SDK: punchline.updateGoal()

Parameter In Type Required Description
id path string (uuid) yes The goal's ID.
Idempotency-Key header string Retries with the same key and body return the first response.

Body (JSON)

Field Type Required Description
name string
kind event, page
match string An event name, or a path (=/path for exactly that page)

Example

curl -X PATCH "https://app.punchlineanalytics.com/v1/goals/398a21ce-1622-4d5d-a136-527782fc885c" \
  -H "Authorization: Bearer $PUNCHLINE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Signup"
  }'

Response 200:

{
  "data": {
    "id": "398a21ce-1622-4d5d-a136-527782fc885c",
    "name": "Signup",
    "kind": "event",
    "match": "signup"
  }
}

Responses

DELETE /v1/goals/{id}

Delete a goal.

Scope: admin (keys that can manage) · SDK: punchline.deleteGoal()

Parameter In Type Required Description
id path string (uuid) yes The goal's ID.
Idempotency-Key header string Retries with the same key and body return the first response.

Example

curl -X DELETE "https://app.punchlineanalytics.com/v1/goals/398a21ce-1622-4d5d-a136-527782fc885c" \
  -H "Authorization: Bearer $PUNCHLINE_API_KEY"

Response 204, no body.

Responses

GET /v1/triggers

The site's no-code event triggers. Needs the admin scope.

Scope: admin (keys that can manage) · SDK: punchline.listTriggers()

Example

curl "https://app.punchlineanalytics.com/v1/triggers" \
  -H "Authorization: Bearer $PUNCHLINE_API_KEY"

Response 200:

{
  "data": [
    {
      "id": "92f7364b-89e3-43f0-aa7c-91954aa9b0af",
      "name": "Demo button",
      "kind": "click",
      "match": "#book-demo",
      "event_name": "demo_click",
      "version": 1,
      "active": true
    }
  ]
}

Responses

POST /v1/triggers

Create a trigger. Needs the admin scope.

Scope: admin (keys that can manage) · SDK: punchline.createTrigger()

Parameter In Type Required Description
Idempotency-Key header string Retries with the same key and body return the first response.

Body (JSON)

Field Type Required Description
name string
kind page_view, url_match, click, form_submit
match string A path (page_view, url_match with * wildcards) or a CSS selector (click, form_submit; needs the browser snippet)
event_name string
active boolean

Example

curl -X POST "https://app.punchlineanalytics.com/v1/triggers" \
  -H "Authorization: Bearer $PUNCHLINE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Demo button",
    "kind": "click",
    "match": "#book-demo",
    "event_name": "demo_click"
  }'

Response 201:

{
  "data": {
    "id": "92f7364b-89e3-43f0-aa7c-91954aa9b0af",
    "name": "Demo button",
    "kind": "click",
    "match": "#book-demo",
    "event_name": "demo_click",
    "version": 1,
    "active": true
  }
}

Responses

GET /v1/triggers/{id}

One trigger.

Scope: admin (keys that can manage) · SDK: punchline.getTrigger()

Parameter In Type Required Description
id path string (uuid) yes The trigger's ID.

Example

curl "https://app.punchlineanalytics.com/v1/triggers/92f7364b-89e3-43f0-aa7c-91954aa9b0af" \
  -H "Authorization: Bearer $PUNCHLINE_API_KEY"

Response 200:

{
  "data": {
    "id": "92f7364b-89e3-43f0-aa7c-91954aa9b0af",
    "name": "Demo button",
    "kind": "click",
    "match": "#book-demo",
    "event_name": "demo_click",
    "version": 1,
    "active": true
  }
}

Responses

PATCH /v1/triggers/{id}

Change a trigger: send only the fields to change.

Scope: admin (keys that can manage) · SDK: punchline.updateTrigger()

Parameter In Type Required Description
id path string (uuid) yes The trigger's ID.
Idempotency-Key header string Retries with the same key and body return the first response.

Body (JSON)

Field Type Required Description
name string
kind page_view, url_match, click, form_submit
match string A path (page_view, url_match with * wildcards) or a CSS selector (click, form_submit; needs the browser snippet)
event_name string
active boolean

Example

curl -X PATCH "https://app.punchlineanalytics.com/v1/triggers/92f7364b-89e3-43f0-aa7c-91954aa9b0af" \
  -H "Authorization: Bearer $PUNCHLINE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "match": "a.book-demo"
  }'

Response 200:

{
  "data": {
    "id": "92f7364b-89e3-43f0-aa7c-91954aa9b0af",
    "name": "Demo button",
    "kind": "click",
    "match": "a.book-demo",
    "event_name": "demo_click",
    "version": 2,
    "active": true
  }
}

Responses

DELETE /v1/triggers/{id}

Delete a trigger.

Scope: admin (keys that can manage) · SDK: punchline.deleteTrigger()

Parameter In Type Required Description
id path string (uuid) yes The trigger's ID.
Idempotency-Key header string Retries with the same key and body return the first response.

Example

curl -X DELETE "https://app.punchlineanalytics.com/v1/triggers/92f7364b-89e3-43f0-aa7c-91954aa9b0af" \
  -H "Authorization: Bearer $PUNCHLINE_API_KEY"

Response 204, no body.

Responses

GET /v1/url-parameters

The URL-parameter allowlist and rules. Needs the admin scope.

Scope: admin (keys that can manage) · SDK: punchline.getUrlParameters()

Example

curl "https://app.punchlineanalytics.com/v1/url-parameters" \
  -H "Authorization: Bearer $PUNCHLINE_API_KEY"

Response 200:

{
  "data": {
    "params": [
      {
        "name": "ref",
        "mode": "value"
      },
      {
        "name": "gclid",
        "mode": "presence"
      }
    ],
    "rules": []
  }
}

Responses

PATCH /v1/url-parameters

Replace params and/or rules (send either or both). Rules may only use allowed parameters.

Scope: admin (keys that can manage) · SDK: punchline.updateUrlParameters()

Parameter In Type Required Description
Idempotency-Key header string Retries with the same key and body return the first response.

Body (JSON)

Field Type Required Description
params list of object
rules list of object

Example

curl -X PATCH "https://app.punchlineanalytics.com/v1/url-parameters" \
  -H "Authorization: Bearer $PUNCHLINE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "params": [
      {
        "name": "ref",
        "mode": "value"
      },
      {
        "name": "gclid",
        "mode": "presence"
      }
    ]
  }'

Response 200:

{
  "data": {
    "params": [
      {
        "name": "ref",
        "mode": "value"
      },
      {
        "name": "gclid",
        "mode": "presence"
      }
    ],
    "rules": []
  }
}

Responses

DELETE /v1/url-parameters

Allow no parameters (except utm_*) and remove all rules.

Scope: admin (keys that can manage) · SDK: punchline.deleteUrlParameters()

Parameter In Type Required Description
Idempotency-Key header string Retries with the same key and body return the first response.

Example

curl -X DELETE "https://app.punchlineanalytics.com/v1/url-parameters" \
  -H "Authorization: Bearer $PUNCHLINE_API_KEY"

Response 204, no body.

Responses

Errors

Every error has one shape, and its docs_url links to the code's section below. param names the parameter at fault, when there is one; message never repeats what you sent.

{
  "error": {
    "type": "invalid_request_error",
    "code": "invalid_parameter",
    "message": "limit: must be at most 1000",
    "param": "limit",
    "docs_url": "https://app.punchlineanalytics.com/v1/errors#invalid_parameter"
  }
}
Code HTTP Type Meaning Retry?
invalid_api_key 401 authentication_error Missing or invalid API key. Send it as: Authorization: Bearer pk_live_… No: fix the request
insufficient_scope 403 permission_error This API key doesn't have the scope this endpoint needs. No: fix the request
plan_upgrade_required 403 permission_error This site's plan doesn't include this. Upgrade in the dashboard under Billing. No: fix the request
not_found 404 invalid_request_error Not found. No: fix the request
site_not_found 404 invalid_request_error No site with that ID is available to this API key. No: fix the request
invalid_json 400 invalid_request_error The request body isn't valid JSON. No: fix the request
invalid_parameter 400 invalid_request_error A parameter is invalid. No: fix the request
body_too_large 413 invalid_request_error The request body is too large. No: fix the request
feature_unavailable 409 invalid_request_error This isn't available for this site. No: fix the request
idempotency_key_reused 422 invalid_request_error This Idempotency-Key was already used with a different request body. No: fix the request
rate_limited 429 rate_limit_error Too many requests. Retry after the time in the Retry-After header. Yes, after Retry-After
internal_error 500 api_error Something went wrong on our side. Yes, with backoff

invalid_api_key

HTTP 401. Check the header is exactly Authorization: Bearer pk_live_…, with no quotes or extra spaces, and that the key hasn't been revoked in Site settings → API and AI access.

insufficient_scope

HTTP 403. The endpoint needs a scope this key doesn't have (see Scope on each endpoint). Create a key with Can send events or Can manage ticked; scopes can't be added to an existing key.

plan_upgrade_required

HTTP 403. The site's plan doesn't include this endpoint: the API and MCP need Growth, and /v1/accounts and /v1/handoffs need Business. The message names the plan. The site's owner can upgrade under Billing in the dashboard; it takes effect within a minute (plans).

not_found

HTTP 404. The ID doesn't exist on this site, or it was deleted. IDs from another site always return this.

site_not_found

HTTP 404. site_id must be the site the key belongs to. Leave it out on GET endpoints to use the key's site; GET /v1/sites shows it.

invalid_json

HTTP 400. The body didn't parse. Send Content-Type: application/json and a JSON object; check for trailing commas and unescaped quotes.

invalid_parameter

HTTP 400. param names the field and message says what's wrong with it (allowed values, ranges or lengths). The same rules apply as in the dashboard.

body_too_large

HTTP 413. Bodies are capped at 64 KB (1 MB for POST /v1/events). Send events in batches of up to 1,000, or narrow the query.

feature_unavailable

HTTP 409. The report needs something this site hasn't set up yet: a connected GA4 property for GET /v1/reconciliation, or at least one goal for conversion metrics. The message says which; set it up in Site settings.

idempotency_key_reused

HTTP 422. An Idempotency-Key can only be reused with the identical body. Use a new key for a different request.

rate_limited

HTTP 429. 120 requests a minute per key. Wait for the Retry-After seconds; RateLimit-Remaining and RateLimit-Reset on every response let you pace before you hit it.

internal_error

HTTP 500. Our side failed. Retry with exponential backoff (writes are safe to retry with an Idempotency-Key). If it persists, tell us the time and endpoint.

Ask the docs

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