Authentication
Every request must carry an API key as a bearer token.
Authorization: Bearer cmpr_live_<your-key>
API keys are created and revoked from your dashboard under Settings, and are available on the Business plan. A key's full value is shown once at creation — store it somewhere safe; it can't be retrieved again.
Keys carry a scope of either read or read_write. Creating links requires a read_write key.
Rate limits
Two ceilings apply per account, and a third protects against credential guessing:
| Limit | Value | On breach |
|---|---|---|
| Per minute | 60 requests | 429 with Retry-After: 60 |
| Per day | Plan-defined cap | 429 with Retry-After: 3600 |
| Failed auth (per IP) | 20 / hour | 429 with Retry-After: 3600 |
When you hit a limit, the response includes a Retry-After header telling you how many seconds to wait. Back off and retry rather than hammering the endpoint.
Error format
Every error — at any status code — returns the same JSON shape. Branch your code on the stable code field, not on the human-readable error message (which may be reworded).
{
"ok": false,
"error": "Invalid, revoked, or expired API key.",
"code": "invalid_key"
}
Responses are always application/json and are never cached. See the full error-code reference below.
Create a link
Creates a short link for the authenticated account. Requires a read_write key.
Request body
JSON object. url is the only required field.
| Field | Type | Description | |
|---|---|---|---|
url | string | required | The destination URL. If you omit the scheme, https:// is prepended automatically. |
custom_slug | string | optional | A custom short code (3–20 letters and numbers). Must be available and pass validation; some short or premium slugs require a higher tier. Omit to get a random slug. Alias: slug. |
expiry | string | optional | One of never (default), 24h, 7d, 30d. Any other value is treated as never. |
Example request
curl -X POST https://comprs.me/v1/links \ -H "Authorization: Bearer cmpr_live_<your-key>" \ -H "Content-Type: application/json" \ -d '{"url":"https://example.com/a/very/long/path","expiry":"30d"}'
Response 201 Created
| Field | Type | Description |
|---|---|---|
ok | boolean | Always true on success. |
slug | string | The allocated short code. |
short_url | string | The full short link. |
qr_png | string | URL of a PNG QR code for the link. |
qr_svg | string | URL of an SVG QR code for the link. |
expires_at | string | null | YYYY-MM-DD HH:MM:SS (server time), or null when the link never expires. |
{
"ok": true,
"slug": "aB3xZ9",
"short_url": "https://comprs.me/aB3xZ9",
"qr_png": "https://comprs.me/qr/aB3xZ9.png",
"qr_svg": "https://comprs.me/qr/aB3xZ9.svg",
"expires_at": "2026-07-05 14:30:00"
}
List links
Returns your links, newest first. Requires a read key (a read_write key also works).
Query parameters
| Param | Type | Description | |
|---|---|---|---|
limit | integer | optional | How many to return, 1–100. Default 25. |
offset | integer | optional | How many to skip, for paging. Default 0. |
Example request
curl "https://comprs.me/v1/links?limit=25&offset=0" \ -H "Authorization: Bearer cmpr_live_<your-key>"
Response 200 OK
A data array of link objects plus a pagination block (total is the full count across all pages).
{
"ok": true,
"data": [ { /* link objects — see "Get a link" below */ } ],
"pagination": { "total": 42, "limit": 25, "offset": 0 }
}
Get a link
Fetches one link you own. Requires a read key. Returns 404 if the slug doesn't exist or belongs to another account — existence is never revealed across accounts.
Example request
curl https://comprs.me/v1/links/aB3xZ9 \
-H "Authorization: Bearer cmpr_live_<your-key>"
Response 200 OK — the link object
| Field | Type | Description |
|---|---|---|
slug | string | The short code. |
short_url | string | The full short link. |
long_url | string | The destination URL. |
qr_png / qr_svg | string | QR code image URLs. |
clicks | integer | Total recorded clicks. |
last_clicked_at | string | null | Most recent click time, or null. |
expires_at | string | null | Expiry time, or null when never. |
created_at | string | When the link was created. |
is_custom | boolean | Whether the slug was user-chosen. |
disabled | boolean | Whether the link is disabled (stops redirecting). |
flagged | boolean | Whether the link is flagged for review. |
{
"ok": true,
"slug": "aB3xZ9",
"short_url": "https://comprs.me/aB3xZ9",
"long_url": "https://example.com/path",
"qr_png": "https://comprs.me/qr/aB3xZ9.png",
"qr_svg": "https://comprs.me/qr/aB3xZ9.svg",
"clicks": 128,
"last_clicked_at": "2026-07-01 09:12:44",
"expires_at": null,
"created_at": "2026-06-20 14:30:00",
"is_custom": false,
"disabled": false,
"flagged": false
}
Delete a link
Permanently deletes a link you own, along with its click history. Requires a read_write key. This cannot be undone.
Example request
curl -X DELETE https://comprs.me/v1/links/aB3xZ9 \
-H "Authorization: Bearer cmpr_live_<your-key>"
Response 200 OK
{
"ok": true,
"deleted": true,
"slug": "aB3xZ9"
}
404 not_found.Error codes
Each error returns a stable code alongside its HTTP status.
| Status | Code | Meaning |
|---|---|---|
| 400 | invalid_body | Body wasn't a JSON object with a url field. |
| 401 | missing_auth | No API key was sent. |
| 401 | invalid_key | Key is unknown, revoked, or expired. |
| 403 | account_inactive | The account is not active. |
| 403 | api_not_in_plan | API access requires the Business plan. |
| 403 | insufficient_scope | A read-only key was used for a write. |
| 409 | slug_taken | The requested custom_slug is already in use. |
| 422 | invalid_url | Not a valid http/https URL. |
| 422 | unsafe_url | The URL was flagged by malicious-URL filtering. |
| 422 | slug_invalid | The custom slug failed validation — see the error field. |
| 429 | rate_limited | A rate limit was reached; see Retry-After. |
| 405 | method_not_allowed | Only POST is supported on this endpoint in v1. |
| 404 | not_found | No such API endpoint. |
| 503 | allocation_failed | Couldn't allocate a unique slug; transient, retry. |
| 500 | server_error | Unexpected server error. |