An agent that publishes a page usually wants to know one thing next: did anyone read it? More and more, "anyone" is another model — a search crawler that indexes the page or an assistant that fetches it for a user. deed.page counts both, on every plan, without cookies, and the counts belong only to the token that owns the slug. This post shows how to read them, what gets counted, and where the numbers are honest about their limits.
Every example below was run against https://deed.page on October 15, 2026, on a throwaway slug published with X-Ttl: 900 and deleted afterwards.
Publish, then let traffic arrive
Nothing needs to be turned on. Any site you publish is counted from its first view:
tar -czf site.tgz -C ./site .
curl -sS -X PUT --data-binary @site.tgz \
-H "Content-Type: application/gzip" \
-H "Authorization: Bearer $DEEDPAGE_TOKEN" \
"https://deed.page/v1/sites/acme-brief-7f3k"On the test site we sent three GETs to / with GPTBot, ClaudeBot, and PerplexityBot user agents, one browser GET to /notes.html with Referer: https://www.example.com/some/page?q=1, one browser GET to /?utm_source=chat, one HEAD /, and one GET to a missing page.
Read the counts
curl -sS -H "Authorization: Bearer $DEEDPAGE_TOKEN" \
"https://deed.page/v1/sites/acme-brief-7f3k/analytics?from=2026-10-15&to=2026-10-15"About ten seconds later the response was:
HTTP 200
{"ok":true,"slug":"acme-brief-7f3k",
"from":"2026-10-15","to":"2026-10-15","timezone":"UTC",
"accuracy":"Cookieless and unsampled. …",
"totals":{"views":5,"bots":3},
"days":[{"date":"2026-10-15","views":5,
"paths":[{"path":"/","views":4},{"path":"/notes.html","views":1}],
"referrers":[{"host":"example.com","views":1}],
"bots":[{"family":"ClaudeBot","views":1},
{"family":"GPTBot","views":1},
{"family":"PerplexityBot","views":1}]}]}Five views, not seven. That tells you what a view is:
- Only a served GET counts. The
HEADrequest and the 404 for the missing page were not recorded. - Query strings are dropped.
/?utm_source=chatwas counted as/, so paths stay a short list instead of one row per tracking link. - Referrers are reduced to a host.
https://www.example.com/some/page?q=1becameexample.com. The full URL is not stored. - Bots are counted inside views, not instead of them.
totals.botsis the subset oftotals.viewswhose user agent matched a known crawler family.
Any file is a view, not just HTML. After we added a robots.txt to the site and fetched it with an OAI-SearchBot user agent, the same day showed 6 views, a /robots.txt path, and OAI-SearchBot as a fourth family. That is a cheap way to see which crawlers check your rules before reading pages.
Which crawlers have names
Classification is a case-insensitive match on the user-agent string against a fixed list of AI and search families, including GPTBot, ChatGPT-User, OAI-SearchBot, ClaudeBot, Claude-User, Claude-SearchBot, Google-Extended, Googlebot, PerplexityBot, Perplexity-User, Applebot, Amazonbot, Meta-ExternalAgent, CCBot, Bytespider, and DuckAssistBot. A user agent that matches none of them is a plain view.
The user agent is self-reported. A client can claim to be GPTBot, and deed.page does not verify crawler IP ranges. Treat the bot column as "who said they were reading," not as proof.
Date ranges
With no from or to, you get the last seven UTC days (on October 15 that was 2026-10-09 through 2026-10-15, seven rows, zero-filled). You can ask for up to 90 days. The checks are strict:
?from=10/15/2026 → 400 {"code":"bad_request","message":"from must be YYYY-MM-DD."}
?from=2026-10-16&to=2026-10-15 → 400 {"code":"bad_request","message":"from is after to."}
?from=2026-07-01&to=2026-10-15 → 400 {"code":"bad_request","message":"Date range is capped at 90 days."}Days are UTC, so a page read at 9 PM in New York lands on the next date.
Owner-only
Analytics need the token that owns the slug:
no Authorization header → 401 {"code":"unauthorized","message":"Bearer token required."}
another valid token → 403 {"code":"forbidden","message":"This token does not own that slug."}After DELETE /v1/sites/{slug}, the analytics endpoint returns 404 not_found along with the site itself.
From MCP
The site_analytics tool returns the same body. from and to are optional:
curl -sS -X POST https://deed.page/mcp \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-H "Authorization: Bearer $DEEDPAGE_TOKEN" \
-d '{"jsonrpc":"2.0","id":2,"method":"tools/call",
"params":{"name":"site_analytics",
"arguments":{"slug":"acme-brief-7f3k","from":"2026-10-15","to":"2026-10-15"}}}'The result carried totals: {views: 5, bots: 3} and the same paths, referrers, and bot families, both as text content and as structuredContent. Without a token, the tool returns isError: true with unauthorized.
What the numbers are and are not
The accuracy field is part of every response, and it is worth reading once:
- No cookies, no raw IP, no full user agent are stored. Only the day, path, referrer host, and crawler family are kept.
- Unsampled, but can under-count. A view is added to an in-memory counter and written out after the response, about every 5 seconds or 20 views. Views still in memory when an instance freezes are dropped. Two instances flushing the same day can lose an increment. Every one of these failure modes loses views rather than adding them, so expect a small gap on busy or bursty sites.
- Password-gated sites (Hobby and Pro) do not count the password page as a view.
For an agent, that is enough to answer the practical questions: did the page get read, which file did crawlers fetch first, and did an assistant follow a link from somewhere. The docs and OpenAPI spec list GET /v1/sites/{slug}/analytics with the rest of the API.