Errors
The single error envelope of the API and every error code with its HTTP status and the right way to handle it.
Updated August 13, 2026
Every error, on every endpoint, uses one envelope:
{
"error": {
"code": "not_found",
"message": "Link not found",
"doc_url": "https://linkprofit.com/docs/api/errors"
}
}
Branch on error.code, show error.message to humans. Validation errors add
a details array with per-field messages:
{
"error": {
"code": "validation_failed",
"message": "Request failed validation",
"doc_url": "https://linkprofit.com/docs/api/errors",
"details": [
{ "path": "url", "message": "Destination URL is not a valid address" }
]
}
}
Error codes
| Code | HTTP | Meaning | What to do |
|---|---|---|---|
| unauthorized | 401 | Missing, unknown, revoked or expired key | Check the header; issue a new key |
| forbidden | 403 | Wrong key kind for the endpoint, or the operation is not allowed | Use a workspace / partner key as documented |
| insufficient_scope | 403 | The key lacks a required scope | Create a key with the scope named in the message |
| plan_restricted | 403 | The plan does not include this capability | Upgrade the plan or remove the feature from the request |
| not_found | 404 | The resource does not exist — or belongs to another tenant | Verify the id; foreign resources are indistinguishable from missing ones |
| invalid_request | 400 | Malformed JSON, bad cursor, broken business rule | Fix the request |
| validation_failed | 422 | The body or query failed schema validation | Fix the fields listed in details |
| conflict | 409 | State conflict: taken slug, reused Idempotency-Key with a different body | Pick another value or a fresh idempotency key |
| quota_exceeded | 422 | A plan quota is exhausted (links, domains, clients) | Upgrade the plan or free the quota |
| rate_limited | 429 | Too many requests for this key | Wait Retry-After seconds; see Rate limits |
| not_configured | 503 | The capability is not configured in this environment | Retry later or contact support |
| internal | 500/502/504 | Unexpected platform error | Retry with backoff; report if persistent |
| invalid_click_id | 422 | The click_id is forged, tampered with, or was issued in another workspace | Pass the value of the click-ID parameter exactly as it appeared in the landing URL |
| attribution_expired | 422 | The click is older than the attribution window of the workspace | Nothing to retry; adjust the window in the dashboard if needed |
Handling rules that pay off
- 404 is also isolation. An id from another workspace answers
not_found, neverforbidden— the API does not reveal whether a foreign id exists. - Retry only what is safe. Retry
internalandrate_limitedwith backoff. Never blind-retryconflictorvalidation_failed— the request itself is wrong. UseIdempotency-Keyto make mutation retries safe. GET /linksnever 404s. Empty filters return an empty list; 404 on the collection would be ambiguous.