Simple Analytics
Privacy-friendly page statistics stored directly in the POD. No Google Analytics, no cookie banners, no external scripts. One line of code.
Setup
Add the tracking snippet to your Astro layout (or any HTML page):
<script src="https://YOUR-ADMIN-URL/t.js" data-api="https://YOUR-ADMIN-URL/api/hit" defer></script>
Replace YOUR-ADMIN-URL with the URL where your Orbiter admin runs. The snippet is also available as a copy-to-clipboard button on the Analytics page in the admin.
Do-Not-Track. Uses sendBeacon for zero performance impact, with an image pixel fallback.What's tracked
| Field | Source | Notes |
|---|---|---|
| Page path | location.pathname | e.g. /blog/my-post |
| Referrer | document.referrer | Only external referrers (same-host filtered) |
| Screen width | screen.width | For device type estimation |
| Language | Accept-Language header | First language only |
| Bot flag | User-Agent pattern match | Googlebot, GPTBot, ClaudeBot, etc. |
Not tracked: IP addresses, cookies, browser fingerprints, session duration, scroll depth, click events.
Bot detection
The tracking endpoint automatically flags known bot and AI agent user agents. The analytics dashboard shows human vs. bot traffic separately — so you can see what percentage of your readers are AI systems.
Detected agents include: Googlebot, Bingbot, GPTBot, ClaudeBot, PerplexityBot, FacebookExternalHit, LinkedInBot, TwitterBot, and others.
Dashboard
Open Analytics from the admin sidebar (or g + y in Station mode). The dashboard shows:
- Stats bar — total views, human views, bot views, top page
- Daily chart — bar chart with human (purple) and bot (gold) views per day
- Top pages — most viewed pages ranked by total views
- Referrers — top traffic sources
- Period filter — 7 days, 30 days, or 90 days
Data retention
Analytics data is stored in the _analytics table in the POD. To clean up old data:
DELETE /api/analytics/prune?days=90
This deletes all records older than the specified number of days. The default is 90 days.
API
Track a pageview (public, no auth)
POST /api/hit?p=/page-path&r=https://referrer.com&w=1920 GET /api/hit?p=/page-path&r=https://referrer.com&w=1920 (returns 1×1 GIF)
Get analytics (auth required)
GET /api/analytics?days=30&path=/blog
{
"total": 1234,
"humans": 980,
"bots": 254,
"topPages": [{ "path": "/", "views": 456, "human_views": 400 }, ...],
"topReferrers": [{ "referrer": "https://google.com", "count": 89 }, ...],
"daily": [{ "date": "2026-06-20", "views": 42, "human_views": 35 }, ...]
} Prune old data (auth required)
DELETE /api/analytics/prune?days=90
{ "ok": true, "deleted": 1500 }