u4
Developer API

API reference

Send a bearer token. Bodies can be form-encoded or JSON; responses are JSON. Make a token in your dashboard.

openapi.json llms.txt

Authentication

Authorization: Bearer u4_…

Tokens are shown once and stored hashed. Revoking takes effect immediately. No valid token means 401. Every request is logged with the token and the caller's IP address, so keep tokens out of client-side code.

Create a link

POST https://u4.nz/api/links

url      required   http(s) destination
slug     optional   custom slug, 4+ characters: letters, digits, - and _ ; omit for a random one.
                    Reserved words (site paths, common brands, profanity) return 400.
                    3 characters or fewer are not available (403).
expires  optional   days until the link stops resolving (max 3650)
curl -X POST https://u4.nz/api/links \
  -H "Authorization: Bearer $U4_TOKEN" \
  -d url="https://example.com/launch" -d slug=launch -d expires=30

{
  "slug": "launch",
  "short": "https://u4.nz/launch",
  "qr": "https://u4.nz/launch.qr.svg",
  "url": "https://example.com/launch",
  "created_at": 1789425751886,
  "expires_at": 1792017751886,
  "clicks": 0
}

Bulk create

Up to 100 per call. Each one succeeds or fails on its own, so the response is 207.

curl -X POST https://u4.nz/api/links/bulk \
  -H "Authorization: Bearer $U4_TOKEN" -H "Content-Type: application/json" \
  -d '{"links":[{"url":"https://a.example"},{"url":"https://b.example","slug":"b"}]}'

{ "created": 2, "results": [ { "ok": true, "slug": "…", … }, … ] }

List, read, update, delete

GET    https://u4.nz/api/links?limit=100        # newest first, limit ≤ 1000
GET    https://u4.nz/api/links/launch
PATCH  https://u4.nz/api/links/launch           # url and/or expires (null = never)
DELETE https://u4.nz/api/links/launch           # also deletes its click history

Click stats

curl "https://u4.nz/api/links/launch/stats?days=30" -H "Authorization: Bearer $U4_TOKEN"

{
  "slug": "launch",
  "clicks": 412,                 // all-time
  "window_days": 30,
  "window_clicks": 118,
  "by_day":     [{"day":"2026-09-14","n":37}, …],
  "by_country": [{"k":"NZ","n":61}, …],
  "by_referer": [{"k":"news.ycombinator.com","n":40}, …],
  "by_device":  [{"k":"mobile","n":70}, {"k":"desktop","n":44}, {"k":"bot","n":4}]
}

Raw events: GET /api/links/:slug/clicks.csv — timestamp, country, referer, device for the last 90 days (50,000 rows max).

Account

GET https://u4.nz/api/me

{ "id": "…", "email": "…", "links": 12, "clicks": 4031, "top": [{"k":"launch","n":118}, …] }

Public extras

No token needed:

https://u4.nz/launch          302 redirect
https://u4.nz/launch+         preview page: shows the destination instead of following it
https://u4.nz/launch.qr.svg   QR code, SVG

Errors and limits

Errors look like {"error":"…"} with the status you'd expect:

400bad URL, bad slug, or bad body
401missing or invalid token
403destination host is blocked, or slug is 3 characters or fewer
404no such link (or not yours)
409slug already taken
429rate limited — 600 requests/min per account

Destinations must be http or https. Private hosts, raw IPs and u4.nz itself are refused. Redirects are never rate limited.