Quickstart
The whole API is one base URL and a bearer token. If you can send a POST, you can shorten a link. Base URL: https://api.snap.link/v1.
$ export SNAP_TOKEN="sk_live_…" $ curl -s https://api.snap.link/v1/links \ -H "Authorization: Bearer $SNAP_TOKEN" \ -d '{"url":"https://example.com/very/long/path"}'
Authentication
Every request needs an Authorization: Bearer <token> header. Create tokens in your dashboard under Settings → API keys. Tokens are scoped per workspace; rotate them whenever you like — old tokens stop working the moment you revoke them.
Create a link
POST /v1/links — body is JSON.
url(required) — the destination. Must behttporhttps.slug(optional) — your custom path. Falls back to a random 6-char code.domain(optional) — a custom domain you've verified.expires_at(optional) — ISO 8601; the link 410s after this.
{
"id": "lnk_8Qx2vT",
"short": "https://snap.link/launch",
"url": "https://example.com/very/long/path",
"slug": "launch",
"qr": "https://snap.link/launch.svg",
"created_at": "2026-06-12T09:41:22Z"
}
Get a link
GET /v1/links/:id returns the same shape, plus a clicks total. Use the id or the slug — both resolve.
Read stats
GET /v1/links/:id/stats?window=30d returns a daily click series with device and country breakdowns. Windows: 24h, 7d, 30d, 1y.
{
"clicks": 38412,
"uniques": 21903,
"by_day": [ /* 30 ints */ ],
"devices": { "desktop": 58, "mobile": 36, "tablet": 6 },
"countries": [ { "code": "US", "pct": 41 } ]
}
Errors
Errors are JSON with a stable code and a human message. The HTTP status tells you the category.
400 invalid_url— the destination wasn't a valid http(s) URL.409 slug_taken— that slug already exists on this domain.401 bad_token— missing or revoked API key.429 rate_limited— slow down; checkRetry-After.
CLI
Prefer the terminal? The snap CLI wraps the same API. Install it with your package manager of choice.
$ npm i -g @snaplink/cli $ snap login $ snap shorten https://example.com/long/path --slug launch # https://snap.link/launch (copied to clipboard)
Rate limits
60 writes/minute and 600 reads/minute per token on Pro, higher on Team. Every response carries X-RateLimit-Remaining and X-RateLimit-Reset so you never have to guess.