# Counter (counter.dev) > Open-source, privacy-friendly web analytics. A ~1.1 KB browser snippet posts a single aggregated hit per visit to a public collect endpoint, and a token-authenticated data feed returns aggregated stats. No cookies, no IP storage, no per-visitor records. AGPL-3.0 and self-hostable; the hosted service is pay-what-you-want. Generated by API Evangelist on 2026-08-13. Counter publishes no llms.txt of its own — https://counter.dev/llms.txt and https://t.counter.dev/llms.txt both return 404. This file is generated from apis.yml and the artifacts in this repository, all of which are derived from the AGPL-3.0 source and live probes rather than from a vendor developer portal. ## What an agent needs to know first - Counter does not describe itself as offering a developer API. The HTTP surface below is real and callable, but it is the dashboard's own backend, documented here from source rather than from a published reference. Treat it as unversioned and subject to change without notice. - Two hosts serve the identical endpoint set: `https://t.counter.dev` (the tracking snippet's default `data-server`) and `https://counter.dev` (the dashboard). Verified 2026-08-13. - Authentication for reads is a read-only token pair passed as query parameters: `?user=&token=`. There is no OAuth, no scopes, and no header-based option. The token is per-account and singular — resetting it revokes every share link. - There is a PUBLIC DEMO. Append `demo=1` to `/query` or any non-empty `demo` to `/dump` and you get real aggregated data with no credential at all. Use this to explore before asking a user for a token. - Errors are bare plain-text strings, not JSON. A 403 body is literally `Forbidden` whether the token was missing or wrong. Do not parse error bodies as structured data. - `GET /query` returns JSON but labels it `text/plain; charset=utf-8`. Parse it as JSON regardless of the Content-Type header. - Nothing here is idempotent-protected and there are no rate-limit headers. Do not retry writes. ## API surface ### Stats (read) - `GET /query?from=YYYY-MM-DD&to=YYYY-MM-DD` — operationId `query`. Aggregated archived visit data for a date range. Returns one JSON object keyed by site host, then by dimension (browser, country, date, device, hour, lang, page, ref, screen, weekday, platform), then value → count. Auth: session cookie, or `user`+`token`, or `demo=1`. - `GET /dump` — operationId `dump`. Server-Sent Events stream of live and archived stats. Emits typed frames `{"type":..., "payload":...}`: `oldest-archive-date`, then `archive`, then repeating `dump` frames (throttled to one per second), or a single `nouser` frame when unauthenticated. Note `nouser` arrives inside a 200 response — check the frame type, not the status code. Auth as above; `utcoffset` controls day/hour bucketing. ### Tracking (write — browser only in practice) - `GET /track` — operationId `track`. Records one aggregated visit. Requires an `Origin` header, which is how the site id is derived. Parameters: `id` (the account UUID from the dashboard), `user`/`site` (fallbacks), `referrer`, `screen`, `utcoffset`, `country`. Bots and localhost origins are silently dropped. NOT idempotent — each call increments counters. - `POST /trackpage` — operationId `trackpage`. Records a pageview counter for a path. `application/x-www-form-urlencoded` with `id` and `page`. Requires an `Origin` header. Returns 204. ### Account (dashboard-backing) - `POST /login` — operationId `login`. Form-encoded `user` + `password`; sets the `swa` session cookie. - `POST /register` — operationId `register`. Form-encoded `user` + `password`, optional `mail` and `utcoffset`. - `POST /resettoken` — operationId `resetToken`. Rotates the read-only token, invalidating the previous one. DESTRUCTIVE — this breaks every existing share link and should be human-confirmed. - `POST /logout` — operationId `logout`. Clears the session. ## Integration The only consumer-facing component is the tracking snippet, placed before ``: ```html ``` `data-server` overrides the collect host and is the documented hook for pointing a site at a self-hosted instance. A visitor can opt out entirely by setting a `doNotTrack` key in `sessionStorage` or `localStorage` — the snippet returns immediately. ## What Counter does NOT have Stated plainly so an agent does not go looking: no OpenAPI or AsyncAPI published by the provider, no MCP server, no A2A agent card, no `/.well-known/` documents of any kind, no webhooks or callbacks, no OAuth or scopes, no client SDK in any package registry, no pagination, no idempotency keys, no rate-limit headers or documented limits, no request/correlation id, no status page, no changelog, no versioning scheme, no deprecation policy, no SLA, and no certifications. ## Pricing Pay-what-you-want for the hosted service — free to use, no credit card, users choose whether and how much to pay. Self-hosting costs nothing beyond your own infrastructure (Redis plus a small Go server). There is no metered or per-call pricing and no published tier table. ## Data and privacy posture No per-visitor entity exists anywhere in the system. A visit is folded into Redis counters on arrival and the transient record is discarded. Only a two-letter country code is derived from the request (via `CF-IPCountry`); the IP itself is never stored. Referrers are reduced to their host. Short-window buckets expire automatically; only a daily SQL archive persists. Data is hosted with Linode LLC in Frankfurt, Germany. ## Links - Website: https://counter.dev/ - Demo dashboard: https://counter.dev/app#demo - Help: https://counter.dev/help/ - Integration guide: https://counter.dev/help/integration.html - Blog: https://counter.dev/blog - Source (AGPL-3.0): https://github.com/ihucos/counter.dev - Self-hosting: https://github.com/ihucos/counter.dev-selfhost - Issues / support: https://github.com/ihucos/counter.dev/issues - Privacy policy: https://counter.dev/pages/privacy.html - Imprint: https://counter.dev/pages/imprint.html - Contact: hey@counter.dev ## API Evangelist artifacts in this repository - openapi/ — three derived OpenAPI 3.0.1 documents (account, stats, tracking) - asyncapi/counter-dev-stats-asyncapi.yml — the /dump SSE stream - authentication/, conventions/, errors/, lifecycle/, conformance/, data-model/ - sandbox/ — the public demo account - packages/, cli/, components/ — what Counter actually distributes - plans/, rate-limits/, finops/, security/, agentic-access/, skills/