docs
how it counts
- visitors
- a salted hash of site, day, IP and user agent. the day is part of the hash, so tuesday's visitor and wednesday's visitor are two unrelated strings and nothing on our side can join them.
- sessions
- page views from one visitor less than 30 minutes apart.
- what's skipped
- non-GET requests, non-HTML responses, known bots, asset paths, and any status outside 2xx, redirects and 404. all optional.
sveltekit
install @addilytics/sveltekit and export the handle from your hooks file. on cloudflare it hands delivery to waitUntil
so the response isn't held up.
// src/hooks.server.ts
import { addilytics } from '@addilytics/sveltekit';
import { env } from '$env/dynamic/private';
export const handle = addilytics({
endpoint: 'https://addilytics.app',
siteKey: env.ADDILYTICS_KEY
});wintercg runtimes
workers, deno, bun, vercel edge. if it has a fetch(request, env, ctx) handler, wrap it.
import { withAddilytics } from '@addilytics/wintercg';
export default {
fetch: withAddilytics(handler, {
endpoint: 'https://addilytics.app',
siteKey: 'ak_...'
})
};framework adapters
each adapter reads the final response through that framework's public hook and skips json, redirects, bots and data requests.
@addilytics/astro- outermost Astro middleware plus astro:page-load tracking.
@addilytics/hono- outermost Hono middleware with a same-origin browser relay.
@addilytics/react-router- Framework Mode middleware and committed-location tracking.
@addilytics/nextjs- Route Handler wrappers and committed App Router tracking.
@addilytics/nuxt- Nitro response capture and Nuxt page:finish tracking.
@addilytics/express- middleware that reads final metadata after the response finish event.
@addilytics/fastify- a plugin that reads final metadata in onResponse.
same rule as sveltekit. client mode for prerendered pages and CDN hits, hybrid when the server always sees the first document. data loads and prefetches never count as a page view.
anything else
the core package runs on any javascript server. or skip the packages and post json straight to the ingest endpoint with your site key as a bearer token.
POST https://addilytics.app/api/ingest
authorization: Bearer ak_...
content-type: application/json
{
"path": "/blog/hello",
"status": 200,
"referrer": "https://news.ycombinator.com/",
"userAgent": "...",
"language": "en-US,en;q=0.9",
"ip": "203.0.113.4",
"country": "US",
"host": "example.com"
}custom events
page views are automatic. for anything else, call track from a server action. props are optional.
// src/hooks.server.ts
export const handle = addilytics({ endpoint: 'https://addilytics.app', siteKey: env.ADDILYTICS_KEY });
// src/routes/signup/+page.server.ts
import { handle } from '../../hooks.server';
export const actions = {
default: async (event) => {
await createAccount(event);
await handle.track(event, 'signup', { props: { plan: 'free' } });
}
}; the wintercg wrapper exposes fetch.client.track. on the raw endpoint, set a name field.
funnels
an ordered list of pages and events. a visitor counts for a step only after hitting the previous one within the selected range, so the numbers can only go down as you read left to right.
active users
fingerprints rotate daily, so on their own they can't follow a person across days. if your app has accounts, tell the hook who's signed in and the users tab starts counting daily, weekly and monthly actives plus weekly retention.
// src/hooks.server.ts
export const handle = addilytics({
endpoint: 'https://addilytics.app',
siteKey: env.ADDILYTICS_KEY,
user: (event) => event.locals.user?.id
});
// per call, if you'd rather
await handle.track(event, 'upgrade', { userId: session.userId }); the hook hashes the id with your site key before it leaves your server, and the ingest
endpoint hashes it again on arrival, so the original never reaches us. the core package
takes the same callback as identify. on the raw endpoint, set a userId field. we keep one row
per user per day, so this history survives event pruning.
event properties
click an event in the breakdown and each prop key gets its own breakdown. strings, numbers and booleans work. nested objects get dropped.
public dashboards
one read-only link per site, from settings. you pick which breakdowns are visible. totals and the chart always are. the link is noindex and dies the moment you revoke it.
retention and rollups
a nightly job rolls each finished utc day into daily counts, then prunes raw events past the retention window. unfiltered ranges read the rollups, so they go back as far as you've had the site. filters and the 24 hour range need raw events, so they stop at the retention window.
options
- mode
- server, hybrid or client. server is the default.
- relayPath
- same-origin browser endpoint. defaults to /__addilytics.
- ignorePaths
- extra strings or regexes to skip.
- trackBots
- count known crawlers. off by default.
- trackStatuses
- which server statuses count. defaults to 2xx, 3xx redirects and 404.
- trustProxy
- trust forwarded IP and country headers.
- deliveryTimeoutMs
- max ingest wait. defaults to 5000.
- onError
- receives delivery and filtering errors.
- fetch
- override the fetch used to send events.
ready? add a site.