Skip to content

API documentation

Create and manage short links from your own applications. A small REST API with API key authentication and predictable JSON responses.

Quick start

Create a free account, generate an API key from your account page, then create your first link:

curl -X POST https://zurl.world/api/v1/links \
  -H "Authorization: Bearer zurl_sk_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://example.com/a-long-page"}'

Response:

{
  "ok": true,
  "data": {
    "id": "lnk_2f9c1a...",
    "shortCode": "a8K3xPq",
    "shortUrl": "https://zurl.world/a8K3xPq",
    "destinationUrl": "https://example.com/a-long-page",
    "title": null,
    "custom": false,
    "state": "active",
    "hasPassword": false,
    "clicks": 0,
    "createdAt": "2026-03-14T10:00:00.000Z",
    "updatedAt": "2026-03-14T10:00:00.000Z",
    "expiresAt": null,
    "lastClickedAt": null
  }
}

Authentication

Every request carries an API key as a Bearer token. Keys begin zurl_sk_ and are shown once at creation — only a hash is stored, so a lost key must be revoked and replaced rather than recovered.

Authorization: Bearer zurl_sk_YOUR_KEY

Treat a key as a password. It carries the full permissions of the account that created it, so keep it server-side and out of version control.

Response envelope

Successful responses are { "ok": true, "data": ... }. Errors are { "ok": false, "error": { "code", "message", "field?" } }. Branch on code, which is stable; message is written for humans and may change.

Endpoints

MethodPathDescription
POST/api/v1/linksCreate a short link.
GET/api/v1/linksList your links. Supports limit and offset.
GET/api/v1/links/:idRetrieve a single link.
PATCH/api/v1/links/:idUpdate the destination, title, expiry or disabled state.
DELETE/api/v1/links/:idDelete a link permanently.
GET/api/v1/links/:id/analyticsAggregate analytics. Supports a days parameter, 1 to 365.

Creating a link

url is required. Everything else is optional.

{
  "url": "https://example.com/page",   // required
  "alias": "spring-sale",              // optional custom ending
  "title": "Spring campaign",          // optional label
  "password": "hunter2",               // optional, min 4 characters
  "expiresAt": "2026-12-31T23:59:59Z"  // optional ISO 8601
}

Updating a link

Send only the fields you want to change. The short code itself cannot be changed, because doing so would break every copy already shared.

curl -X PATCH https://zurl.world/api/v1/links/lnk_2f9c1a \
  -H "Authorization: Bearer zurl_sk_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://example.com/new-destination", "disabled": false}'

Analytics

curl "https://zurl.world/api/v1/links/lnk_2f9c1a/analytics?days=30" \
  -H "Authorization: Bearer zurl_sk_YOUR_KEY"

Returns total clicks, a daily series, and breakdowns by country, referrer, device, browser and operating system. Individual visitors are not exposed, because that data is never collected — see privacy.

Errors

CodeStatusMeaning
validation_error400The request body failed validation.
invalid_url400The destination URL was rejected.
unauthorized401Missing, malformed or revoked API key.
not_found404No such link, or it belongs to another account.
alias_taken409That custom alias is already in use.
alias_reserved409That alias is reserved and cannot be used.
payload_too_large413The request body exceeded 16KB.
rate_limited429Rate limit exceeded. See Retry-After.
internal_error500Something went wrong on our side.

Rate limits

  • Link creation: 1,000 per hour, per key.
  • Reads: 2,000 per hour, per key.

Every response includes:

RateLimit-Limit: 1000
RateLimit-Remaining: 994
RateLimit-Reset: 2841

On a 429, Retry-After gives the seconds to wait. Back off rather than retrying immediately.

Versioning

The version is in the path. Breaking changes will ship as /api/v2 rather than altering v1 responses. Additive fields may appear in v1, so parse defensively and ignore unknown keys.

Frequently asked questions

How do I get an API key?
Create a free account, then generate a key from your account page. The key is shown once at creation and only a hash is stored, so it cannot be retrieved afterwards — save it somewhere safe.
Is API access included on the free plan?
Yes. Free accounts can create API keys and make up to 1,000 link creations and 2,000 read requests per hour.
What happens if I exceed the rate limit?
You receive a 429 response with a Retry-After header giving the number of seconds to wait. Every response also carries RateLimit-Limit, RateLimit-Remaining and RateLimit-Reset headers, so you can back off before hitting the limit.
Are the API and the website subject to the same rules?
Yes. Both go through the same service layer, so URL validation, reserved names and alias rules behave identically. There is no API-only behaviour to discover.
Why do I get 404 instead of 403 for a link I do not own?
Deliberately. Returning 403 would confirm that the id exists, which turns the endpoint into a way of probing for valid identifiers. Links you do not own are indistinguishable from links that do not exist.
Is there a webhook for click events?
Not yet. Analytics is available by polling the analytics endpoint. Webhooks are on the roadmap but not implemented, so do not build against them.