Skip to content
Language: English

API for developers

API error format and error codes

Updated 1 November 2026 5 min read

How LinkHarbor API error responses are structured, what the most important error codes mean and how your code should react to them.

Every error response from the API has the same format. Your code should rely on the error code in the error field, not on the text in message – the code stays stable, while the text can change and is translated into the account language.

Structure of an error response

Example: 422 validation_failed
{
  "error": "validation_failed",
  "message": "Some of the information provided is invalid. Please check and try again.",
  "details": [
    { "field": "title", "reason": "minLength", "message": "…" }
  ],
  "request_id": "6b1f0c2e-…"
}
error
Machine-readable code, for example not_found. For most errors it is also in the X-Error-Code header – but rely on the field in the body.
message
Human-readable description in the account language: with an API key that of the workspace owner, with a connected app that of the person who connected it. Without a valid token – for example 401 from the token check – the language follows Accept-Language, otherwise it is English. The Content-Language header names the language.
details
Only present when there is more to say. For 422 from input validation, a list with field, reason and message for each problem; for 422 from business logic and all other errors, an object, for example with reason, retry_after_sec or a list of issues. When a size is exceeded it contains field, reason and limit, and for 413 request_too_large max_bytes and max_kb. So check whether details is a list.
request_id
The ID of the request. It is also in the X-Request-Id header. Include it when you contact support.

Exceptions: the 429 rate_limited response has the field retry_after_sec instead of details; with 429 and 403 insufficient_scope, request_id is missing from the body. 401 from the token check and 422 from the schema check have no X-Error-Code header, and with the schema check only the top-level message is translated; the texts inside details stay in English. The X-Request-Id header is always set.

The most important codes

HTTPerrorMeaningWhat to do
400invalid_jsonThe body is not valid JSONCheck the JSON
400bad_requestWrong Content-Type or malformed requestSet Content-Type: application/json
401missing_bearer_tokenNo token in the Authorization headerSet the header Authorization: Bearer …
401invalid_tokenToken expired or invalid, or key deletedGet a new token; if that does not help, check the key
401invalid_api_keyWrong key ID or secret in the token exchangeCheck the credentials
402plan_limit_reachedPlan limit reached, for example for short linksUpgrade or delete entries
403insufficient_scopeScope missing or endpoint not enabled for API keysCreate a key with the right scope
403forbiddenNo permission for this actionDo it in the dashboard
403feature_not_availableFeature not included in the planCheck the plan
403block_type_requires_planBlock type only available in a higher planChoose a different block type or upgrade
403template_lockedPremium template not available in the current planChoose a template from GET /v1/templates/available
403workspace_suspended, workspace_read_only, workspace_grace_expiredWorkspace locked or read-only because of an outstanding paymentSort out the payment in the dashboard
404not_foundResource does not exist or does not belong to your workspaceCheck the ID
409conflictGeneral conflict with the current stateReload the state, change the input
409workspace_template_in_useWorkspace template is still used by pages; the number is in details.countSwitch the pages to another template, then delete it
409handle_taken, slug_takenHandle or short link slug is already takenChoose a different value
409form_in_use, page_not_private, page_not_published, never_publishedConflict with the state: form still on a page, page not private or not published, never publishedChange the state, then try again
409page_limit_reached, link_limit_reached, block_limit_reached, form_limit_reached, asset_limit_reached, asset_storage_limit_reached, workspace_template_limit_reachedThe plan's quantity or storage limit reachedRemove entries or upgrade
409draft_not_publishableThe draft has problems; the list is in details.issuesFix the problems, then publish again
413request_too_largeBody larger than allowed for this endpoint (64 KB, 1 MB or 2 MB); the limit is in details.max_kbSend less data per request, see Rate limits
413payload_too_largeUploaded file larger than 10 MBMake the file smaller
422validation_failedInvalid input or unknown field; specifics in detailsCorrect the fields as described in details
422handle_reserved, target_url_invalid, target_url_scheme, target_url_hostHandle reserved, or target URL invalid or not allowedChoose a different value
422workspace_template_disabledThe selected workspace template is disabledEnable the template under “Domains & Templates” or choose another one
422template_css_external, template_section_invalidWorkspace template: CSS loads from external servers, or a {{#if}}/{{#unless}}/{{#each}} section is placed incorrectlyRemove external addresses, or close the section within the same tag or text
422custom_css_externalCustom CSS loads something from external serversRemove external addresses from the CSS
429rate_limited, api_key_lockedToo many requests, or key locked after failed attemptsWait as long as Retry-After or details.retry_after_sec says
500internal_errorUnexpected errorTry again later, note the request_id
503service_unavailableService temporarily unavailableTry again after a pause

Reading input errors

With 422 validation_failed, details lists every problem separately: field is the path of the field (with slashes for nested objects, for example theme_config/button_style), reason is the rule that was broken, such as required, minLength, maxLength, enum or additionalProperties. With additionalProperties, the message says which fields are unknown – often a typo in the field name.

  • 400, 413 and 422: do not retry, fix the request instead
  • 401 invalid_token: get a new token once and retry
  • 403: do not retry; check the scope, the plan or who is allowed to do this
  • 404: check the ID; after a deletion this is normal
  • 409: reload the state and decide whether the action is still needed
  • 429: wait as long as Retry-After says, then retry – see Rate limits
  • 500 and 503: retry a few times with increasing pauses

Nothing found?

Tell us what you need, and our support team will help you personally.

Contact support