kortste.link

API

A JSON API. Reading is open; creating needs a key.

Base

Every endpoint lives on api.kortste.link. The redirect is the exception: it lives on the short domain, because that is the whole point of it.

Getting a key

Creating a link needs a key. Looking one up, following a redirect and reporting abuse do not.

There is no signup form and no dashboard, because there are no accounts — not for this API and not for the service. Mail keys@kortste.link with a line about what you are building and you get one back, issued by hand.

A new key allows 5 creates a minute and 50 a day. Small on purpose: this API is goodwill rather than a product, and someone pays for it out of pocket. Ask through the support link at the foot of this page and the allowance goes up.

Send it as a header, never in a query string. Query strings end up in server logs, browser history and referrer headers, which is a poor place to leave a credential — doubly so on a link shortener.

Authorization: Bearer kl_live_...

The key is shown once, at issuance, and only its hash is stored. Nobody can send it to you a second time, so a lost key is revoked and replaced rather than recovered. Mail the same address to have one revoked: it stops working on the next request.

Endpoints

POST /v1/links Create a short link key
curl -X POST https://api.kortste.link/v1/links \
  -H 'Authorization: Bearer kl_live_...' \
  -H 'Content-Type: application/json' \
  -d '{"url":"https://seznam.cz/m/sem/1345"}'
{
  "code": "fp9qm3",
  "shortUrl": "https://kortste.link/fp9qm3",
  "url": "https://seznam.cz/m/sem/1345",
  "createdAt": "2026-08-22T18:49:11.665Z"
}
201
Created. The body carries code, shortUrl, url and createdAt.
400
The body is not valid JSON.
401
No key, or one that is unknown or revoked.
422
The URL was rejected. The detail field says why.
429
Over this key's allowance. Retry-After says how long to wait.
GET /v1/links/{code} Look a link up without opening it

Reveals where a code points without visiting it, which is a safety feature rather than an oversight: it needs no key for the same reason.

curl https://api.kortste.link/v1/links/fp9qm3
200
The same fields as creation.
404
No such code.
410
The link was taken down.
GET /{code} The redirect itself
curl -i https://kortste.link/fp9qm3

Deliberately 302 and not 301: a permanent redirect is remembered by browsers more or less forever, which would make a link impossible to take back.

302
The Location header carries the destination.
404
No such code.
410
The link was taken down.
POST /v1/abuse Report a link for abuse

No key, on purpose. Someone reporting a phishing link is exactly the person least likely to hold one, and a credential between us and that report would be a worse trade than any abuse it prevented.

curl -X POST https://api.kortste.link/v1/abuse \
  -H 'Content-Type: application/json' \
  -d '{"code":"fp9qm3","reason":"phishing"}'
202
Recorded. A person reads it; a report alone revokes nothing.
422
Unknown code, or no reason given.
GET /healthz Status check
curl https://api.kortste.link/healthz
200
The body is the word ok.

Errors

Errors follow RFC 9457 and come back as application/problem+json with type, title, status and detail. One shape for every failure, so a client writes one handler.

A missing key, a malformed one, an unknown one and a revoked one all answer the same 401. That is deliberate: any difference between them would turn this endpoint into an oracle for probing which keys exist.

Limits

Creating through the API is limited per key, so two callers never share an allowance and no key earns a fresh one by changing address. The form on the front page holds no key and is limited per address instead. Redirects are never limited: throttling them would throttle the product.