shortcode
Log in Sign upSign up
Developers

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_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Keys are secrets. Never embed an API key in client-side or browser code, mobile app bundles, or public repositories — anyone with the key can manage links on your account. Call the API only from your own backend.

Base URL

All endpoints below are relative to:

https://api.short-code.io

Endpoints

GET/v1/links

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).

Request
$ curl https://api.short-code.io/v1/links \
    -H 'Authorization: Bearer sc_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
Response · 200 OK
{
  "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"
    }
  ]
}
POST/v1/links

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.

Request · default domain
$ 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" }'
Request · custom domain
$ 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" }'
Request · named link
$ 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" }'
Response · 201 Created
{
  "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"
}
GET/v1/links/:uuid

Fetch a single link by its UUID, with the same click analytics as the list endpoint.

Request
$ curl https://api.short-code.io/v1/links/9c3f1e2a-4b7d-4a10-9e2f-6d8c1a5b7e90 \
    -H 'Authorization: Bearer sc_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
Response · 200 OK
{
  "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"
}
PATCH/v1/links/:uuid

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.

Request
$ 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" }'
Response · 200 OK
{
  "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"
}
DELETE/v1/links/:uuid

Permanently delete a link. Existing short URLs stop resolving immediately.

Request
$ curl -X DELETE https://api.short-code.io/v1/links/9c3f1e2a-4b7d-4a10-9e2f-6d8c1a5b7e90 \
    -H 'Authorization: Bearer sc_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
Response · 204 No Content
(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"
  }
}
StatusCodeWhen it happens
401unauthorizedMissing, invalid, or revoked API key.
403forbiddenThe account is on the free plan — API access requires a paid plan.
403plan_limit_reachedPOST /v1/links would exceed the account's link limit, or its named-link allowance on the shared domain.
400invalid_requestdestination 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.
409code_takenThe requested code is already in use on that domain.
404not_foundThe link (or, for domain on create, the domain) doesn't exist or isn't accessible to your account.
429rate_limitedToo many requests — see rate limits below.

Rate limits

Requests are limited per account over a rolling 60-second interval:

PlanRequests / minute
Pro60
Enterprise600

Every authenticated response includes these headers:

HeaderMeaning
X-RateLimit-LimitRequests allowed in any rolling 60-second interval for your plan.
X-RateLimit-RemainingRequests still available in the active rolling interval.
X-RateLimit-ResetThe 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).