Skip to main content

Link stats API

GET /v1/urls/{slug}/stats returns pre-aggregated analytics for one of your links. Owner-scoped, cookie-less, aggregate-only (no per-click rows, no visitor identifiers — see How we count). The /x/urls/{slug}/stats web-app route returns the identical body.

Request

GET /v1/urls/{slug}/stats?from=YYYY-MM-DD&to=YYYY-MM-DD
Authorization: Bearer YOUR_TOKEN_HERE

Generate a token in your account settings.

ParamWhereRequiredDefaultNotes
slugpathyesA link you own (else 404).
fromquerynoto − 29 daysWindow start, UTC date YYYY-MM-DD.
toquerynotoday (UTC)Window end, UTC date; never the future.

Window semantics (clamp, don't reject). from is clamped to your plan's analytics retention. A window longer than your retention is not an error — it is clamped and the effective start is echoed in range.clampedTo (with retentionDays and storedDays). A malformed window (from after to, or an unparseable date) is a 400.

Rate limit. Per subscriber, 60 requests/minute; over the limit returns 429 with a Retry-After header.

Response 200

{
"stats": {
"slug": "spring-sale",
"zoneDomain": "snipzr.li",
"range": { "from": "2026-06-14", "to": "2026-07-13", "clampedTo": "2026-06-14", "retentionDays": 30, "storedDays": 122 },
"allTime": { "total": 5120, "preTracking": 4900, "trackingSince": "2026-07-13" },
"totals": { "human": 82, "bots": 61, "requests": 143, "qr": 12, "click": 70 },
"series": [ { "date": "2026-07-12", "n": 90, "b": 40 }, { "date": "2026-07-13", "n": 53, "b": 21 } ],
"hourly": { "fromUtc": "2026-07-12T16:00:00Z", "buckets": [0,0,1,2,3,4,6,8,7,9,5,6,4,3,2,2,1,3,4,2,1,0,0,0] },
"geo": [ { "k": "US", "v": 40 }, { "k": "DE", "v": 22 }, { "k": "unknown", "v": 3 } ],
"referrers": [ { "k": "~direct", "v": 31 }, { "k": "reddit.com", "v": 18 }, { "k": "~other", "v": 4 } ],
"devices": [ { "k": "desktop", "v": 51 }, { "k": "mobile", "v": 28 } ],
"browsers": [ { "k": "chrome", "v": 40 } ],
"os": [ { "k": "windows", "v": 25 } ],
"languages": [ { "k": "en", "v": 60 } ],
"source": { "click": 70, "qr": 12 },
"freshness": "todayLive"
}
}

Fields

FieldMeaning
rangeEffective window + retention/clamp metadata (clampedTo = effective start; storedDays may exceed retentionDays).
allTimeLifetime hits decomposed into pre-tracking vs tracked era.
totalsWindow sums. human = requests − bots is the headline; requests (raw, incl. bots/monitors) — don't bill or alert on it.
seriesPer-UTC-day {date, n, b} (human = n − b), ascending, zero-days omitted.
hourlyRolling last 24 hours of human clicks by UTC hour, independent of the window (empty for a purely-historical window).
geo,referrers,devices,browsers,os,languages[{k, v}], sorted by v desc. Referrers are host-only; sentinels ~direct (no referrer) and ~other (folded tail). Country key unknown when undeterminable.
source{click, qr} split of human clicks.
freshnesstodayLive | final | degraded.

Counts are monotonic non-decreasing per UTC day except a brief dip during an hourly consolidation window — they settle within the hour. Don't treat a transient same-day decrease as data loss.

Errors

StatusWhen
400malformed window (from > to, bad date) or invalid slug
404slug not found for this owner
429over the per-token rate limit (Retry-After header)
503analytics store unreachable and no live data covers the window

A degraded 200 (store down but recent live data available) is distinct from a 503 (nothing serveable) — surface freshness: "degraded" as "recent data only".

Example

curl -H "Authorization: Bearer $SNIPZR_TOKEN" \
"https://api.snipzr.com/v1/urls/spring-sale/stats?from=2026-06-14&to=2026-07-13"

JSON Schema

The response is described by a published JSON Schema (2020-12): https://snipzr.com/schemas/v1/link-stats.schema.json. Point your validator or code generator at that URL.

Versioning

Additive, backward-compatible fields may appear without a version bump — tolerate unknown fields. Removals/renames are a new major version. The web app can export this same data as CSV (long format date,metric,key,value); the API is JSON-only.