API docs
Create and manage short links programmatically — bring short-code.io into your own products, scripts, and workflows.
Getting started
The short-code.io API lets you create, list, update, and delete short links without touching the dashboard — useful for provisioning links from your own app, CI pipeline, or CMS.
API access is available on the Pro plan and above. To create a key, open the dashboard, click your avatar in the top right, and choose API keys. You can have up to 10 keys per account at a time.
The full key is shown once, right after creation — copy it somewhere safe. Only a short prefix is kept visible afterwards so you can tell your keys apart. You can revoke any key at any time; requests using a revoked key are rejected immediately.
Authentication
Send your key as a bearer token on every request:
Authorization: Bearer sc_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxBase URL
All endpoints below are relative to:
https://api.short-code.ioEndpoints
List every short link on your account, including the same click analytics the dashboard shows: totals for today, the last 7 days, and the last 30 days, a per-day series covering the last 30 days (by_day, days without clicks omitted), and the percentage share per country of origin (by_origin, ISO 3166-1 alpha-2 codes).
$ curl https://api.short-code.io/v1/links \
-H 'Authorization: Bearer sc_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'{
"links": [
{
"uuid": "9c3f1e2a-4b7d-4a10-9e2f-6d8c1a5b7e90",
"domain": "srtco.de",
"code": "x9K2mQ7z",
"named": false,
"short_url": "https://srtco.de/x9K2mQ7z",
"destination": "https://acme.com/launch",
"clicks": {
"count": { "total": 128, "day": 3, "week": 24, "month": 97 },
"by_day": [
{ "date": "2026-07-17", "clicks": 21 },
{ "date": "2026-07-18", "clicks": 3 }
],
"by_origin": { "DE": 67, "US": 33 }
},
"created": "2026-06-02T09:14:22.000Z",
"updated": "2026-07-18T15:02:11.000Z"
}
]
}Create a new short link. destination is required and must be a valid http(s) URL. domain is optional and defaults to the shared short domain (srtco.de) — to use a custom domain, add it in the dashboard first. code is optional: supply it to create a named link with a code you choose, or omit it for a generated one.
On the shared domain, a code is 5–32 characters; on your own domain, it is 1–32 characters. Codes may contain a-z, A-Z, 0-9, - and _, with no leading, trailing or repeated separator. The case you send is preserved, but codes are compared case-insensitively, so /Menu and /menu are the same link. Some codes are reserved. On the shared domain, codes containing inappropriate language or common obfuscations are also rejected. A code is permanent — it can't be changed later, so a printed QR keeps working. A code you choose on the shared domain is a named link: it counts against your plan's allowance and stops resolving if your subscription lapses. On your own domain codes are yours to hand out — unlimited, outside that allowance, and returned with "named": false.
$ curl -X POST https://api.short-code.io/v1/links \
-H 'Authorization: Bearer sc_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' \
-H 'Content-Type: application/json' \
-d '{ "destination": "https://acme.com/launch" }'$ curl -X POST https://api.short-code.io/v1/links \
-H 'Authorization: Bearer sc_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' \
-H 'Content-Type: application/json' \
-d '{ "domain": "go.acme.com", "destination": "https://acme.com/launch" }'$ curl -X POST https://api.short-code.io/v1/links \
-H 'Authorization: Bearer sc_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' \
-H 'Content-Type: application/json' \
-d '{ "code": "summer-sale", "destination": "https://acme.com/launch" }'{
"uuid": "9c3f1e2a-4b7d-4a10-9e2f-6d8c1a5b7e90",
"domain": "srtco.de",
"code": "summer-sale",
"named": true,
"short_url": "https://srtco.de/summer-sale",
"destination": "https://acme.com/launch",
"clicks": {
"count": { "total": 0, "day": 0, "week": 0, "month": 0 },
"by_day": [],
"by_origin": {}
},
"created": "2026-07-23T10:41:03.000Z",
"updated": "2026-07-23T10:41:03.000Z"
}Fetch a single link by its UUID, with the same click analytics as the list endpoint.
$ curl https://api.short-code.io/v1/links/9c3f1e2a-4b7d-4a10-9e2f-6d8c1a5b7e90 \
-H 'Authorization: Bearer sc_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'{
"uuid": "9c3f1e2a-4b7d-4a10-9e2f-6d8c1a5b7e90",
"domain": "srtco.de",
"code": "x9K2mQ7z",
"named": false,
"short_url": "https://srtco.de/x9K2mQ7z",
"destination": "https://acme.com/launch",
"clicks": {
"count": { "total": 128, "day": 3, "week": 24, "month": 97 },
"by_day": [
{ "date": "2026-07-17", "clicks": 21 },
{ "date": "2026-07-18", "clicks": 3 }
],
"by_origin": { "DE": 67, "US": 33 }
},
"created": "2026-06-02T09:14:22.000Z",
"updated": "2026-07-18T15:02:11.000Z"
}Update a link's destination. destination is required and must be a valid http(s) URL. The short code is immutable — sending a code that differs from the current one returns 400 invalid_request, so a read-modify-write round trip is safe.
$ curl -X PATCH https://api.short-code.io/v1/links/9c3f1e2a-4b7d-4a10-9e2f-6d8c1a5b7e90 \
-H 'Authorization: Bearer sc_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' \
-H 'Content-Type: application/json' \
-d '{ "destination": "https://acme.com/summer-sale" }'{
"uuid": "9c3f1e2a-4b7d-4a10-9e2f-6d8c1a5b7e90",
"domain": "srtco.de",
"code": "x9K2mQ7z",
"named": false,
"short_url": "https://srtco.de/x9K2mQ7z",
"destination": "https://acme.com/summer-sale",
"clicks": {
"count": { "total": 128, "day": 3, "week": 24, "month": 97 },
"by_day": [
{ "date": "2026-07-17", "clicks": 21 },
{ "date": "2026-07-18", "clicks": 3 }
],
"by_origin": { "DE": 67, "US": 33 }
},
"created": "2026-06-02T09:14:22.000Z",
"updated": "2026-07-23T10:52:47.000Z"
}Permanently delete a link. Existing short URLs stop resolving immediately.
$ curl -X DELETE https://api.short-code.io/v1/links/9c3f1e2a-4b7d-4a10-9e2f-6d8c1a5b7e90 \
-H 'Authorization: Bearer sc_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'(empty body)Errors
Every non-2xx response has the same JSON envelope:
{
"error": {
"code": "invalid_request",
"message": "destination is required and must be a valid http(s) URL"
}
}| Status | Code | When it happens |
|---|---|---|
| 401 | unauthorized | Missing, invalid, or revoked API key. |
| 403 | forbidden | The account is on the free plan — API access requires a paid plan. |
| 403 | plan_limit_reached | POST /v1/links would exceed the account's link limit, or its named-link allowance on the shared domain. |
| 400 | invalid_request | destination is missing or not a valid http(s) URL; or code is malformed, reserved or disallowed on the shared domain; or a PATCH tried to change code. |
| 409 | code_taken | The requested code is already in use on that domain. |
| 404 | not_found | The link (or, for domain on create, the domain) doesn't exist or isn't accessible to your account. |
| 429 | rate_limited | Too many requests — see rate limits below. |
Rate limits
Requests are limited per account over a rolling 60-second interval:
| Plan | Requests / minute |
|---|---|
| Pro | 60 |
| Enterprise | 600 |
Every authenticated response includes these headers:
| Header | Meaning |
|---|---|
X-RateLimit-Limit | Requests allowed in any rolling 60-second interval for your plan. |
X-RateLimit-Remaining | Requests still available in the active rolling interval. |
X-RateLimit-Reset | The relevant quota refresh time as a Unix epoch timestamp (seconds). On a 429, this is when another request can be accepted. |
If you exceed your limit, the request is rejected with 429 rate_limited and a Retry-After header (seconds until you can retry).