Skip to content
LinkProfit

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, never forbidden — the API does not reveal whether a foreign id exists.
  • Retry only what is safe. Retry internal and rate_limited with backoff. Never blind-retry conflict or validation_failed — the request itself is wrong. Use Idempotency-Key to make mutation retries safe.
  • GET /links never 404s. Empty filters return an empty list; 404 on the collection would be ambiguous.