API reference
Build on tiny.hygr.dev
A small REST API for creating short links and rendering QR codes, plus webhooks so your systems hear about scans. Create a key under Developers.
Authentication
Send your key as a bearer token on every request. Keys look like tiny_… and are shown once when created — we only keep a hash, so a lost key has to be revoked and replaced.
Authorization: Bearer tiny_your_key_hereResponses are JSON. Errors carry an error object:401 missing or unknown key · 403 your plan does not include the API · 404 not found · 422 invalid input · 429 rate limited.
Rate limit: 120 requests per minute per key. Every response carries X-RateLimit-Remaining, and a 429 carries Retry-After. CORS is open, so you can call the API from a browser.
Endpoints
/api/v1/linksList your short links, newest first.
curl https://preview.tiny.hygr.dev/api/v1/links \
-H "Authorization: Bearer tiny_your_key_here"/api/v1/linksCreate a short link. The destination is normalised and validated.
Body: { "url": "https://example.com/page" }
curl -X POST https://preview.tiny.hygr.dev/api/v1/links \
-H "Authorization: Bearer tiny_your_key_here" \
-H "Content-Type: application/json" \
-d '{"url":"https://example.com/launch"}'
# 201 Created
{
"data": {
"slug": "abc1234",
"shortUrl": "https://preview.tiny.hygr.dev/abc1234",
"url": "https://example.com/launch",
"clicks": 0,
"createdAt": "2026-09-15T08:00:00.000Z"
}
}/api/v1/links/{slug}Fetch one link, including its click count.
curl https://preview.tiny.hygr.dev/api/v1/links/abc1234 \
-H "Authorization: Bearer tiny_your_key_here"/api/v1/links/{slug}Re-point a link. This is how you change where an already-printed dynamic QR code goes.
Body: { "url": "https://example.com/new-page" }
curl -X PATCH https://preview.tiny.hygr.dev/api/v1/links/abc1234 \
-H "Authorization: Bearer tiny_your_key_here" \
-H "Content-Type: application/json" \
-d '{"url":"https://example.com/autumn"}'/api/v1/links/{slug}Delete a link. Anything pointing at it, including printed codes, stops working.
curl -X DELETE https://preview.tiny.hygr.dev/api/v1/links/abc1234 \
-H "Authorization: Bearer tiny_your_key_here"/api/v1/qrRender a QR code as SVG. Returns the SVG directly, or JSON with a data URL when format is json.
Body: { "data": "https://example.com", "style": { … }, "format": "svg" | "json" }
curl -X POST https://preview.tiny.hygr.dev/api/v1/qr \
-H "Authorization: Bearer tiny_your_key_here" \
-H "Content-Type: application/json" \
-d '{"data":"https://example.com","style":{"dotColor":"#121358"}}' \
-o code.svgWebhooks
Add an endpoint under Developers and we will POST JSON to it. Events: link.created, link.scanned, qr.created.
Payload
POST https://your-endpoint.example.com
X-Tiny-Signature: <hex hmac>
X-Tiny-Timestamp: 1789000000
{
"event": "link.scanned",
"createdAt": "2026-09-15T08:00:00.000Z",
"data": { "slug": "abc1234", "url": "https://example.com/launch" }
}Verifying a delivery
The signature is an HMAC-SHA256 of <timestamp>.<raw body> using your endpoint’s signing secret. Compare it in constant time, and reject anything with a timestamp more than a few minutes old so a captured delivery cannot be replayed.
import { createHmac, timingSafeEqual } from "node:crypto";
export function verify(rawBody, headers, secret) {
const signature = headers["x-tiny-signature"];
const timestamp = headers["x-tiny-timestamp"];
if (Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) return false;
const expected = createHmac("sha256", secret)
.update(`${timestamp}.${rawBody}`)
.digest("hex");
const a = Buffer.from(expected);
const b = Buffer.from(String(signature));
return a.length === b.length && timingSafeEqual(a, b);
}Respond with a 2xx quickly. Deliveries are fire-and-forget with a short timeout, so a slow endpoint is recorded as a failure rather than holding anything up.