Skip to content
Y
YAS.SH
API Reference — OpenAPI 3.1

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.txt
Authentication
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.txt
Authentication
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 key
TypeScript
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.
🍪 Cookies & privacy. yas.sh uses only essential cookies to keep you signed in and remember your preferences. We do not run third-party trackers. See our cookie policy and privacy policy.
Settings