api reference · v1

the docs.

Five endpoints, JSON everywhere, no SDK required. Base URL for everything: https://site-shortner.link/api/v1

authentication

Every request carries your key as a bearer token. Keys start with ssk_live_ and are issued by email while onboarding runs in batches — ask for one here. Treat it like a password; rotate it by asking for a new one.

auth header
Authorization: Bearer ssk_live_your_key

endpoints

POST/links

Create a link. The only required field is url; everything else has a sensible default.

fieldtypenotes
urlstring, requiredAbsolute http(s) destination.
slugstring3–24 chars, a-z 0-9 -. Omit for a random 5-char slug (lookalike characters excluded).
status301 | 307Defaults to 307. See redirect behavior before choosing 301.
expires_atISO 8601Optional. After this moment the link forwards to fallback_url.
fallback_urlstringWhere expired hits go. Defaults to a plain "this link has ended" page.
example
$ curl -X POST https://site-shortner.link/api/v1/links \ -H "Authorization: Bearer ssk_live_your_key" \ -d '{"url":"https://example.com/launch-post","slug":"launch"}' HTTP/2 201 { "slug": "launch", "short_url": "https://site-shortner.link/launch", "url": "https://example.com/launch-post", "status": 307, "created_at": "2026-10-14T18:02:11Z" }
GET/links/{slug}

Fetch a link's current state — destination, status code, expiry, timestamps. Add ?list=1 on the bare /links path to page through everything on your key (cursor pagination, 100 per page).

PATCH/links/{slug}

The endpoint this whole service exists for. Change url, status, expires_at, or fallback_url — the edit is live on the very next request. No cache purge step, because 307s are never cached. That's not luck; that's the design.

repoint a link
$ curl -X PATCH https://site-shortner.link/api/v1/links/launch \ -H "Authorization: Bearer ssk_live_your_key" \ -d '{"url":"https://example.com/launch-v2"}' HTTP/2 200 { "slug": "launch", "url": "https://example.com/launch-v2", "status": 307 }
DELETE/links/{slug}

Returns 204. The slug keeps answering with your fallback page for 30 days (so anything already shipped degrades politely), then it's released back to the pool.

GET/links/{slug}/stats

Hit totals, a daily time series, coarse geo (country level), and device class. Numbers about requests, never identities — the privacy page spells out exactly what a hit records.

response shape
{ "slug": "launch", "total": 1284, "daily": [{ "date": "2026-10-20", "hits": 211 }, …], "countries": { "US": 902, "GB": 144, … }, "devices": { "mobile": 71, "desktop": 29 } }

redirect behavior

Public hits on site-shortner.link/{slug} answer with the link's status code — 307 by default — and a Location header pointing at the current destination. We send cache-control: no-store on 307s so every hit re-checks, which is what makes edits instant. There are no interstitial pages, no frames, no link-wrapping: the visitor's address bar shows the real destination the moment they land.

  • Choose 301 only for forever. Browsers may cache a 301 permanently and stop asking us — which means a 301'd link can't reliably be edited later. Great for SEO on a true permanent move; wrong for anything that might change. (The long version.)
  • Unknown slugs return a plain 404 — never a parked-ad page.
  • HEAD is supported, so monitoring tools and link checkers behave.

errors

codemeaningyou'll see it when
400bad requestMalformed JSON, missing url, non-http(s) scheme.
401unauthorizedMissing or revoked key.
404not foundNo such slug on your key.
409conflictSlug already taken — pick another or omit it.
422unprocessableSlug has illegal characters, or expires_at is in the past.
429rate limitedSee below. Response includes retry-after.

Every error body is {"error": {"code": "...", "message": "..."}} with a message written for humans, not lawyers.

rate limits

The management API allows 60 requests/minute per key — generous for anything short of a bulk migration (use the CLI's import for that; it paces itself). Redirects themselves are not rate-limited: a link that goes viral keeps answering, full stop.