A JavaScript analytics tag runs only in a client that executes the page. Vercel and MERJ wrote on 2024-12-17 that ChatGPT and Claude do not execute JavaScript, so those fetches never fire the tag. Inside the Loop records the HTTP request in the Worker. writeDataPoint stores one row in the agent_hits dataset, and the Workers Analytics Engine SQL API reads it back.
Key facts
- Workers Analytics Engine, docs last updated 2026-04-23, takes data points from a Worker and answers SQL over them.
writeDataPointtakes ordered arrays. Blobs are strings, doubles are numbers, andindexesis the sampling key. A second index means the point is not stored.- One point allows at most 20 blobs, 20 doubles, and one index. Blobs together must be 16 KB or less. The index must be 96 bytes or less. One invocation may write at most 250 points. Rows are kept for three months.
- Workers Free includes 100,000 written points a day and 10,000 SQL reads a day. Workers Paid includes 10 million written points a month, then $0.25 per extra million, and 1 million SQL reads a month, then $1.00 per extra million. The pricing page, last updated 2026-04-23, still said on 2026-10-06 that billing had not started.
- The SQL API is a POST to
https://api.cloudflare.com/client/v4/accounts/<account_id>/analytics_engine/sql. The token permission is Account Analytics Read.FORMAT JSONis the default. - Deploying a Worker with
analytics_engine_datasetsfails upload with code 10089 until the dataset exists. Creating the dataset in the dashboard (Workers > Analytics Engine > Create Dataset) clears the error immediately.
How the Inside the Loop Worker writes a data point
src/worker.ts calls logAgentHit after the response is built, so the content type on the row is the one sent to the client, including text/markdown when that conversion runs.
src/agent-traffic.ts skips the write when AGENT_HITS is missing, or when the path starts with /_emdash. wrangler.jsonc binds AGENT_HITS to dataset agent_hits.
The first matching rule wins. The classes are user-fetch, search, training, assistant-tool, browser, and other. The rows below are examples, not every pattern in the file.
ChatGPT-User,Claude-User, andPerplexity-Userareuser-fetch.OAI-SearchBot,Claude-SearchBot,PerplexityBot, andGooglebotaresearch.GPTBot,ClaudeBot, andCCBotaretraining.claude-code, the substringaxios/,codex,openai-codex, a user agent that starts withcurl/, andpython-requestsareassistant-tool.- A
Mozilla/5.0agent withoutbot,crawl, orspiderisbrowser, with an empty agent name. Anything left isother.
Any user agent containing axios/ is stored as agent claude-code. The codex rule matches that substring anywhere.
The class is the only index. Cloudflare samples by index, so a busy class such as browser can be downsampled while user-fetch stays whole.
env.AGENT_HITS.writeDataPoint({
indexes: [cls],
blobs: [
agent,
url.pathname.slice(0, 256),
format(url.pathname, res.headers.get("content-type") ?? ""),
String(cf.country ?? ""),
referrer,
String(cf.verifiedBotCategory ?? ""),
ua.slice(0, 256),
],
doubles: [res.status],
});SQL names the fields from 1. index1 is the class. blob1 is the agent name. blob2 is the path, cut at 256 characters in this Worker. blob3 is the format. blob4 copies request.cf.country when that field is present. blob5 is the referrer hostname. blob6 copies request.cf.verifiedBotCategory when that field is present. blob7 is the user agent, cut at 256 characters. double1 is the HTTP status. Analytics Engine fills in timestamp.
blob3 is markdown for text/markdown, llms for a path under /llms, robots for /robots.txt, feed for /rss.xml or a path under /sitemap, html for text/html, and other otherwise. The 256 character cuts belong to this Worker. The class strings sit under the 96 byte index cap. The write is inside try/catch with an empty catch, so a failure there does not replace the response.
How to query the agent_hits dataset
The table name is the dataset name, agent_hits. It appears after the first successful write. Columns include index1, blob1 through blob20, double1 through double20, timestamp, and _sample_interval.
_sample_interval is how many original events one stored row stands for. Use SUM(_sample_interval). A bare COUNT undercounts once sampling starts. This query was not run against the live account. It matches the binding and the column order above.
curl "https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/analytics_engine/sql" \
--header "Authorization: Bearer $API_TOKEN" \
--data "SELECT index1 AS class, blob1 AS agent, SUM(_sample_interval) AS hits FROM agent_hits WHERE timestamp > NOW() - INTERVAL '1' DAY GROUP BY class, agent ORDER BY hits DESC FORMAT JSON"FORMAT JSON returns one object with meta, data, and rows. FORMAT JSONEachRow is one JSON object per line and no schema. TabSeparated is the third option. Omitting FORMAT is the same as FORMAT JSON. SHOW TIMEZONE returns Etc/UTC. A query reads one table. JOIN and UNION are not in the dialect. On 2026-01-07, Cloudflare added SQL support for HAVING filters, LIKE and ILIKE pattern matching, and conditional aggregate functions including countIf() and sumIf(). Each POST to this endpoint counts as one read query.
Workers Analytics Engine limits, price, and error 10089
The get-started documentation, read on 2026-10-06, states that datasets are created automatically when the Worker writes to them, and does not mention code 10089. Docs issue 29844, opened 2026-04-14 and still open on 2026-10-06, asks for that note.
In practice, deploying a Worker that defines an analytics_engine_datasets binding fails at script upload with workers.api.error.no_access_to_analytics_engine [code: 10089] until the dataset is provisioned. On 2026-10-06, our own initial wrangler deploy for Inside the Loop failed with code 10089. Creating the agent_hits dataset in the dashboard (Workers > Analytics Engine > Create Dataset) resolved the failure immediately: the subsequent wrangler deploy succeeded without any configuration changes.
Community threads had reported conflicting workarounds for 10089. On 2024-05-30, wrangler 3.57.2 failed a deploy with code 10089, and Cloudflare staff rohinlohe confirmed on 2024-06-25 that dashboard enablement was required. On 2025-07-14, petebacondarwin wrote on workers-sdk issue 9312 that subscription changes could strip default entitlements. On 2026-06-19, one Free account reported that creating a dataset alone did not clear the error for them, leading ryantang30 on 2026-07-07 to suggest deploying the binding in the dashboard first. But for our deployment on 2026-10-06, creating the dataset directly in the dashboard was both necessary and sufficient.
The pricing page, last updated 2026-04-23, states Cloudflare is not billing Workers Analytics Engine yet, noting billing will begin "in the coming months." Additional blobs do not increase the per-point cost.
What Cloudflare AI Crawl Control already counts
AI Crawl Control is a zone screen for known AI crawlers, with Overview, Crawlers, and Metrics tabs. Overview includes request volume, a common status code, and a popular path. Referral totals are marked paid plans only. Metrics can split activity by crawler, category, operator, and host, and it charts requested content type against served content type. Programmatic access on that page is the GraphQL Analytics API.
AI Crawl Control charts known crawlers such as GPTBot and ClaudeBot. agent_hits also stores assistant-tool rows for claude-code, the codex substring, curl/, and python-requests.
Sources
- Get started, Workers Analytics Engine: https://developers.cloudflare.com/analytics/analytics-engine/get-started/ (read 2026-10-06)
- SQL API: https://developers.cloudflare.com/analytics/analytics-engine/sql-api/ (read 2026-10-06)
- SQL statements, including FORMAT: https://developers.cloudflare.com/analytics/analytics-engine/sql-reference/statements/ (read 2026-10-06)
- Limits: https://developers.cloudflare.com/analytics/analytics-engine/limits/ (read 2026-10-06)
- Pricing: https://developers.cloudflare.com/analytics/analytics-engine/pricing/ (read 2026-10-06)
- SQL HAVING and LIKE support, 2026-01-07 changelog: https://developers.cloudflare.com/changelog/post/2026-01-07-analytics-engine-support-for-like-and-having/ (read 2026-10-06)
- workers-sdk issue 5940: https://github.com/cloudflare/workers-sdk/issues/5940 (read 2026-10-06)
- workers-sdk issue 9312: https://github.com/cloudflare/workers-sdk/issues/9312 (read 2026-10-06)
- Docs issue 29844: https://github.com/cloudflare/cloudflare-docs/issues/29844 (read 2026-10-06)
- AI Crawl Control, analyze AI traffic: https://developers.cloudflare.com/ai-crawl-control/features/analyze-ai-traffic/ (read 2026-10-06)
- Vercel and MERJ, The rise of the AI crawler, published 2024-12-17: https://vercel.com/blog/the-rise-of-the-ai-crawler (read 2026-10-06)
Last verified: 2026-10-06.