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_KEYTreat 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
| Method | Path | Description |
|---|---|---|
| POST | /api/v1/links | Create a short link. |
| GET | /api/v1/links | List your links. Supports limit and offset. |
| GET | /api/v1/links/:id | Retrieve a single link. |
| PATCH | /api/v1/links/:id | Update the destination, title, expiry or disabled state. |
| DELETE | /api/v1/links/:id | Delete a link permanently. |
| GET | /api/v1/links/:id/analytics | Aggregate 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
| Code | Status | Meaning |
|---|---|---|
| validation_error | 400 | The request body failed validation. |
| invalid_url | 400 | The destination URL was rejected. |
| unauthorized | 401 | Missing, malformed or revoked API key. |
| not_found | 404 | No such link, or it belongs to another account. |
| alias_taken | 409 | That custom alias is already in use. |
| alias_reserved | 409 | That alias is reserved and cannot be used. |
| payload_too_large | 413 | The request body exceeded 16KB. |
| rate_limited | 429 | Rate limit exceeded. See Retry-After. |
| internal_error | 500 | Something 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: 2841On 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.