API — 6 groups • Live & documented
Base /api/v1 · Auth via cookie yas_session or Bearer yas_live_… · Errors application/problem+json (RFC9457) · RateLimit headers · openapi.json
Auth
Cookie for dashboard, Bearer for servers. Scopes links:read,links:write,analytics:read. 10 keys max.
Rate limits
Links create 60/min per user + 120/min per IP. Login 15/min, register 20/min. Headers RateLimit-* / Retry-After.
Errors
type, title, status, code, detail, retryAfter. Codes: UNAUTHORIZED, FORBIDDEN, NOT_FOUND, VALIDATION_ERROR, RATE_LIMITED, CONFLICT, etc.
LINKS
POST/v1/linksCreate link — random base62 or custom alias, optional password/expiry, idempotent via Idempotency-Key
Request — curl
curl -X POST https://yas.sh/api/v1/links \
-H "Authorization: Bearer yas_live_abc123" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: req-001" \
-d '{"originalUrl":"https://example.com?utm_source=newsletter","customAlias":"launch","title":"Launch"}'Authentication
Bearer or Cookie
Status codes
201 Created, 400 Validation, 401 Unauthorized, 409 Conflict (alias taken), 429 Rate limited (60/min per user, 120/min per IP)
Parameters
originalUrl (url, https, ≤2048), customAlias (3-30, ^[a-z0-9-_]+$, optional), title, expiresAt (ISO datetime), password (8-128)
Response
{
"shortCode": "launch",
"url": "https://yas.sh/launch",
"link": { "id":"clx…","shortCode":"launch","originalUrl":"https://example.com","clicks":0,"createdAt":"2026-08-08T10:00:00Z" }
}JavaScript
import { Yas } from "@yas/sdk"
const yas = new Yas({ apiKey: "yas_live_..." })
const { url } = await yas.links.create({ originalUrl: "https://example.com", customAlias: "launch" })TypeScript
await fetch("/api/v1/links", { method:"POST", headers:{ "Authorization":"Bearer yas_live_…","Content-Type":"application/json" }, body: JSON.stringify({ originalUrl }) })Python:
requests.get("https://yas.sh/api/v1/links", headers={"Authorization":"Bearer yas_live_…"})GET/v1/links?search=&limit=20&cursor=List links — paginated, searchable (originalUrl, shortCode, alias, title), owner-scoped
Request — curl
curl -H "Authorization: Bearer yas_live_..." "https://yas.sh/api/v1/links?search=example&limit=20"
Authentication
Bearer or Cookie
Status codes
200 OK { data: Link[], nextCursor: string|null }, 401 Unauthorized
Parameters
search (string), limit (1-100, default 20), cursor (id)
Response
{
"data": [{ "id":"clx1","shortCode":"launch","originalUrl":"https://example.com","clicks":12, "_count":{"clickEvents":12} }],
"nextCursor": "clx1"
}JavaScript
const { data, nextCursor } = await yas.links.list("?search=example&limit=20")TypeScript
await fetch("/api/v1/links?search=example", { headers:{ Authorization:"Bearer yas_live_…" } })Python:
requests.get("https://yas.sh/api/v1/links?search=&limit=20&cursor=", headers={"Authorization":"Bearer yas_live_…"})GET/v1/links/:idGet one link — owner only, includes click count
Request — curl
curl -H "Authorization: Bearer yas_live_..." https://yas.sh/api/v1/links/clx123
Authentication
Bearer or Cookie
Status codes
200 OK, 401, 404 Not Found (or not owner)
Parameters
id (cuid, path)
Response
{ "link": { "id":"clx123","shortCode":"launch","originalUrl":"https://example.com","clicks":42 } }JavaScript
await yas.links.get("clx123")TypeScript
await fetch("/api/v1/links/clx123", { headers:{ Authorization:"Bearer …" } })Python:
requests.get("https://yas.sh/api/v1/links/:id", headers={"Authorization":"Bearer yas_live_…"})PATCH/v1/links/:idUpdate link — originalUrl, title, alias (atomically checked)
Request — curl
curl -X PATCH https://yas.sh/api/v1/links/clx123 \
-H "Authorization: Bearer yas_live_..." \
-d '{"customAlias":"launch-2026"}'Authentication
Bearer or Cookie (owner)
Status codes
200 OK, 400 Invalid alias/protocol, 401, 404, 409 Conflict
Parameters
originalUrl (optional, https), title, customAlias (optional, 3-30)
Response
{ "link": { "id":"clx123","shortCode":"launch-2026","customAlias":"launch-2026" } }JavaScript
await yas.links.update("clx123", { customAlias:"launch-2026" })TypeScript
fetch("/api/v1/links/clx123", { method:"PATCH", body: JSON.stringify({ customAlias }) })Python:
requests.get("https://yas.sh/api/v1/links/:id", headers={"Authorization":"Bearer yas_live_…"})DELETE/v1/links/:idDelete link + cascade clickEvents
Request — curl
curl -X DELETE -H "Authorization: Bearer yas_live_..." https://yas.sh/api/v1/links/clx123
Authentication
Bearer or Cookie (owner)
Status codes
200 {ok:true}, 401, 404
Parameters
id (path)
Response
{ "ok": true }JavaScript
await yas.links.delete("clx123")TypeScript
fetch("/api/v1/links/clx123", { method:"DELETE" })Python:
requests.get("https://yas.sh/api/v1/links/:id", headers={"Authorization":"Bearer yas_live_…"})REDIRECT
GET/:code302 redirect — lookup by shortCode or customAlias, expiry + password enforced, bot filtered
Request — curl
curl -i https://yas.sh/launch # or password: https://yas.sh/secret?password=MyPass123
Authentication
Public (no auth)
Status codes
302 Location, 401 Password required (HTML form), 404 Not found, 410 Gone (expired/click limit)
Parameters
code (path, 3-30), ?password= (if protected)
Response
HTTP/1.1 302 Found Location: https://example.com # body: ClickEvent inserted async, Link.clicks ++ if not bot
JavaScript
await fetch("https://yas.sh/launch", { redirect:"manual" })TypeScript
fetch("https://yas.sh/launch").then(r=> r.headers.get("location"))Python:
requests.get("https://yas.sh/api/:code", headers={"Authorization":"Bearer yas_live_…"})ANALYTICS
GET/v1/analytics/overview?days=30Totals + 30-day series + breakdowns by country/device/referrer/top links
Request — curl
curl -H "Authorization: Bearer yas_live_..." "https://yas.sh/api/v1/analytics/overview?days=30"
Authentication
Bearer or Cookie
Status codes
200, 401
Parameters
days (7|30|90, default 30)
Response
{
"totalLinks": 42,
"totalClicks": 1248,
"series": [{ "date":"2026-08-01","clicks": 41 }],
"byCountry": [{ "country":"US","clicks":320 }],
"byDevice": [{ "device":"mobile","clicks":800 }],
"topLinks": [{ "shortCode":"launch","clicks":320 }]
}JavaScript
const { totalClicks, series } = await yas.analytics.overview({ days:30 })TypeScript
fetch("/api/v1/analytics/overview?days=30", { headers:{ Authorization:"Bearer …" } })Python:
requests.get("https://yas.sh/api/v1/analytics/overview?days=30", headers={"Authorization":"Bearer yas_live_…"})GET/v1/analytics/:linkIdPer-link clicks series & events
Request — curl
curl -H "Authorization: Bearer ..." https://yas.sh/api/v1/analytics/clx123?days=30
Authentication
Bearer or Cookie (owner)
Status codes
200, 401, 404
Parameters
linkId (path), days (query)
Response
{ "linkId":"clx123","clicks":42,"series":[...] }JavaScript
await yas.analytics.byLink("clx123", 30)TypeScript
fetch("/api/v1/analytics/clx123")Python:
requests.get("https://yas.sh/api/v1/analytics/:linkId", headers={"Authorization":"Bearer yas_live_…"})QR
GET/v1/qr?url=&size=512&format=png|svgGenerate QR — http/https only, size validation, 1h cache
Request — curl
curl "https://yas.sh/api/v1/qr?url=https://yas.sh/demo&size=512" -o qr.png # svg: &format=svg
Authentication
Public (no auth, optional)
Status codes
200 image/png or image/svg+xml, 400 Only http/https or missing url
Parameters
url (https, required), size (128|256|512|1024, default 512), format (png|svg)
Response
# binary PNG or SVG string <svg ...>...</svg>
JavaScript
const qrUrl = yas.qr("https://yas.sh/demo", 512) // "/api/v1/qr?url=...&size=512"TypeScript
fetch("/api/v1/qr?url="+encodeURIComponent(url)+"&size=512")Python:
requests.get("https://yas.sh/api/v1/qr?url=&size=512&format=png|svg", headers={"Authorization":"Bearer yas_live_…"})AUTH & KEYS
POST/v1/auth/registerRegister — creates user + yas_session cookie (30d, httpOnly, Lax)
Request — curl
curl -X POST https://yas.sh/api/v1/auth/register \
-H "Content-Type: application/json" \
-d '{"email":"you@yas.sh","password":"Strong123!","name":"You"}' -c cookies.txtAuthentication
Public
Status codes
201 {user}, 400 Validation, 409 Email taken, 429 20/min per IP
Parameters
email (≤255), password (8-128), name (1-100)
Response
{ "user": { "id":"clx…","email":"you@yas.sh","name":"You" } }JavaScript
await yas.auth.register({ email, password, name })TypeScript
fetch("/api/v1/auth/register", { method:"POST", body: JSON.stringify({ email, password }) })Python:
requests.get("https://yas.sh/api/v1/auth/register", headers={"Authorization":"Bearer yas_live_…"})POST/v1/auth/loginLogin — anti-enumeration timing, rate 15/min per IP
Request — curl
curl -X POST https://yas.sh/api/v1/auth/login \
-H "Content-Type: application/json" \
-d '{"email":"you@yas.sh","password":"Strong123!"}' -c cookies.txtAuthentication
Public
Status codes
200 {user}, 401 Invalid credentials, 429
Parameters
email, password
Response
{ "user": { "id":"clx…","email":"you@yas.sh" } }JavaScript
await yas.auth.login({ email, password })TypeScript
fetch("/api/v1/auth/login", { method:"POST", body: JSON.stringify({ email, password }) })Python:
requests.get("https://yas.sh/api/v1/auth/login", headers={"Authorization":"Bearer yas_live_…"})POST/v1/api-keysCreate API key — random 32 hex, SHA-256 hash stored, prefix shown
Request — curl
curl -X POST https://yas.sh/api/v1/api-keys \
-H "Cookie: yas_session=..." \
-H "Content-Type: application/json" \
-d '{"name":"prod","scopes":["links:write"]}'Authentication
Bearer or Cookie
Status codes
201 {id,key,prefix,warning:'Copy now'}, 401, 403 limit 10, 400 name required
Parameters
name (≥2), scopes (array, default links:read,links:write,analytics:read)
Response
{ "id":"clk…","key":"yas_live_abc123…","prefix":"yas_live_abc...","warning":"Copy now — shown once" }JavaScript
const { key } = await yas.keys.create({ name:"prod" }) // save keyTypeScript
fetch("/api/v1/api-keys", { method:"POST", body: JSON.stringify({ name:"prod" }) })Python:
requests.get("https://yas.sh/api/v1/api-keys", headers={"Authorization":"Bearer yas_live_…"})DELETE/v1/api-keys/:idRevoke key — immediate
Request — curl
curl -X DELETE -H "Authorization: Bearer yas_live_..." https://yas.sh/api/v1/api-keys/clk123
Authentication
Bearer or Cookie (owner)
Status codes
200, 401, 404
Parameters
id (path)
Response
{ "ok": true }JavaScript
await yas.keys.revoke("clk123")TypeScript
fetch("/api/v1/api-keys/clk123", { method:"DELETE" })Python:
requests.get("https://yas.sh/api/v1/api-keys/:id", headers={"Authorization":"Bearer yas_live_…"})TOOLS
POST/v1/tools/:slugExecute tool server-side — base64, hash, uuid, url-encode, case-convert, etc.
Request — curl
curl -X POST https://yas.sh/api/v1/tools/hash-calculator \
-H "Content-Type: application/json" \
-d '{"input":"hello","algo":"sha256"}'Authentication
Public
Status codes
200 {result}, 400 Validation
Parameters
slug (path), input (string), mode/algo (per tool)
Response
{ "slug":"hash-calculator","algo":"sha256","result":"2cf24dba5fb0a30..." }JavaScript
await fetch("/api/v1/tools/hash-calculator", { method:"POST", body: JSON.stringify({ input:"hello", algo:"sha256" }) })TypeScript
fetch("/api/v1/tools/base64-text", { method:"POST", body: JSON.stringify({ input:"hello", mode:"encode" }) })Python:
requests.get("https://yas.sh/api/v1/tools/:slug", headers={"Authorization":"Bearer yas_live_…"})All 145 upcoming endpoints are stubbed as
501 not_implemented in openapi.json but gated. UI change never breaks this contract. Generate SDK: npx openapi-typescript /api/v1/openapi -o sdk.ts.