Error catalog
Every error response carries a stable code in error.code. Your client should branch on that one: search by code, filter by domain, or open any row for the full explanation and how to fix it.
40 documented codes
401AUTH_REQUIREDNo Bearer token was sent, and every call requires one.
We did not see a Bearer token in your request. Every API v6 call needs an API key in the Authorization header: without it we cannot tell which organization is calling.
How to fix it
Send Authorization: Bearer hk_YOUR_API_KEY on every request. Bearer is the only accepted scheme: the old styles that sent the key in the URL or the body are no longer accepted.
40002#auth-required401AUTH_INVALID_KEYThe API key is not recognized: wrong format, revoked or expired.
The API key you sent is not recognized. Three situations produce this code: a key with the wrong format, a key that was rotated or revoked, or a key past its expiration date.
How to fix it
Check that your client is using exactly the value shown when the key was created: keys are shown only once. If you rotated keys recently, make sure you are using the new one. If the key expired, issue a new one from the customer portal.
40003#auth-invalid-key401AUTH_COST_CENTER_DISABLEDThe cost center bound to the key is disabled.
Every API key is bound to a cost center so usage can be billed and reported correctly. The cost center for this key is disabled.
How to fix it
Re-enable the cost center from the customer portal, or issue a new key bound to an active one.
401AUTH_SERVICE_NOT_ALLOWEDThe key is not allowed to use the service behind this endpoint.
This key is not allowed to call the service behind this endpoint. Keys can be restricted to specific services so that a leaked or misused key can only do what it was allowed to.
How to fix it
Issue a new key with the right service enabled, or switch to one that already has it. Diagnostic endpoints under /api/v6/utilities/ are always available regardless of the key scope.
400AUTH_SOURCE_IP_UNKNOWNReserved: kept only as a bridge from v5.
Reserved code, kept as a bridge for callers migrating from v5. API v6 does not use the source IP override mechanism that produced this error, so you should not see it on v6 traffic.
40015#auth-source-ip-unknown403ACCOUNT_NOT_ACTIVEYour organization is suspended or closed.
Your organization is not in an active state, usually because it was suspended or closed. While that lasts, every endpoint rejects calls, including the diagnostic ones.
How to fix it
Contact support to review the situation and restore the organization. The state cannot be lifted through the API.
403ACCOUNT_BLOCKEDA full block is active: every call is rejected.
Your organization is under a full block. Every call is rejected, including read-only ones, until the block is lifted.
How to fix it
Open a support ticket so we can review the case. Blocks are lifted once the underlying reason is resolved.
403ACCOUNT_READ_ONLYRead-only mode: only GET, HEAD and OPTIONS pass.
Your organization is in read-only mode. Requests that only read information (GET, HEAD, OPTIONS) keep working, but any call that creates or modifies data is rejected.
How to fix it
Until the block is lifted, pause any code that modifies data. Contact support to find out why it is active and what is needed to remove it.
403ACCOUNT_CONFIG_NOT_FOUNDWe could not load the organization behind your key.
Your API key authenticated correctly, but we could not load the organization it points to. This is a data problem on our side, not in your request.
How to fix it
Contact support and include the meta.requestId from the response: with that id we can trace the exact query that failed.
4002#account-config-not-found429RATE_DDOS_EXCEEDEDUnusually high volume from your source IP.
Your source IP produced an unusually high volume of requests in the last minute. This protection is designed to absorb attack and scanner traffic; legitimate integrations rarely get close to the limit.
How to fix it
Wait the seconds reported in Retry-After before retrying. If you are a legitimate caller behind a shared IP and hit this consistently, contact support so we can review your traffic pattern.
40001#rate-ddos-exceeded429RATE_TPS_EXCEEDEDYou exceeded your organization quota for this endpoint.
You sent more requests to this endpoint than your organization quota allows in the current window. Each endpoint has its own quota: hitting the wall on one does not affect the others.
How to fix it
Pause your client for the seconds reported in Retry-After and start again. To avoid hitting it at all, read the RateLimit-Remaining and RateLimit-Reset headers present on every response and pace yourself. The usage limits guide covers it in detail.
40001#rate-tps-exceeded400IDEMPOTENCY_KEY_INVALIDThe idempotency key does not match the format.
The Idempotency-Key is empty, too long, or contains characters outside the allowed set. It accepts 1 to 255 characters from A-Z a-z 0-9 _ -.
How to fix it
Generate the key with a UUID v4 or any random value within the allowed set. See the idempotency guide.
409IDEMPOTENCY_IN_PROGRESSThe first request with that key is still in progress.
You sent the same idempotency key while the original request is still being processed. It does not run twice: the second one waits.
How to fix it
Wait the time reported in Retry-After and retry with the same key.
422IDEMPOTENCY_KEY_REUSEDThe key already exists, but with a different body.
You used an idempotency key that already exists, with a different body. It is almost always a client bug: one key identifies one operation, not several.
How to fix it
Use a new key for the new operation. Do not retry in a loop: the result will not change.
400URL_INVALIDThe destination URL is malformed.
The url you sent isn't a valid absolute URL: we couldn't parse a scheme and host out of it.
How to fix it
Send an absolute http/https URL including the host, e.g. https://example.com/path.
400URL_SCHEME_NOT_ALLOWEDOnly http/https destinations are allowed.
The destination uses a scheme we don't shorten (e.g. javascript:, data:, ftp:, file:).
How to fix it
Only http and https destinations are accepted.
400URL_TOO_LONGThe destination URL exceeds the maximum length.
The destination URL is longer than the 2048-character maximum.
How to fix it
Shorten the destination (drop unnecessary query parameters) so it fits within 2048 characters.
400URL_BLOCKED_HOSTThe destination host is private or reserved.
The destination points at a private/reserved address or an internal hostname (e.g. 127.0.0.1, 10.0.0.0/8, localhost). We block these so short links can't be used to reach internal endpoints.
How to fix it
Point the link at a publicly reachable host.
400ALIAS_INVALIDThe custom alias doesn't match the allowed format.
The alias isn't 3-32 characters of lowercase letters, digits, hyphen or underscore.
How to fix it
Use a value matching ^[a-z0-9_-]{3,32}$, or omit alias to let the API generate a code.
409ALIAS_TAKENThe alias is already used on this domain.
Another link on the same domain already uses that alias. Codes are unique per domain.
How to fix it
Pick a different alias, use a different domain, or omit alias for an auto-generated code.
409ALIAS_RESERVEDThe alias is a reserved word.
The alias collides with a reserved word (e.g. api, stats, admin, docs).
How to fix it
Choose a non-reserved alias.
400EXPIRES_AT_INVALIDexpiresAt must be a future ISO date-time.
expiresAt couldn't be parsed as an ISO-8601 date-time, or it is in the past.
How to fix it
Send a future ISO-8601 value, e.g. 2026-12-31T23:59:59Z, or omit it for a link that never expires.
404LINK_NOT_FOUNDNo link with that domain and code.
No link with that domain and code belongs to your account. For privacy we return 404 for links owned by other accounts too, so we don't disclose their existence.
How to fix it
Check the domain and code. Deleted links also return 404.
400NOTHING_TO_UPDATENo updatable fields were sent.
The PATCH body had none of the updatable fields (active, expiresAt, longUrl).
How to fix it
Send at least one updatable field.
500CODE_GENERATION_FAILEDCould not allocate a unique code.
We couldn't allocate a unique random code after several attempts. This is rare.
How to fix it
Retry the request. If it persists, quote the meta.requestId in a support ticket.
400STATS_RANGE_INVALIDThe stats range or granularity is invalid.
The statistics window is inconsistent: from after to, a range above the cap (1 year for day, 31 days for hour), or hour requested outside the ~90-day hourly retention.
How to fix it
Use a preset range or a valid from/to, and keep hourly queries within 31 days and the retention window.
400DOMAIN_NOT_ALLOWEDThe chosen domain isn't available to your account.
The domain you requested isn't a platform-global domain nor one owned by your account (or your account has no default domain configured).
How to fix it
Call GET /api/v6/urlshortener/domains to see the domains you can use, or omit domain to use your default.
404COUNTRY_NOT_FOUNDNo country exists with that code.
The code sent to GET /api/v6/tools/countries/{code} does not match any country in the catalog. The parameter expects a two-letter ISO 3166-1 alpha-2 code.
How to fix it
Check the code against GET /api/v6/tools/countries, which returns the full catalog.
503INFRA_DB_CONNECTION_ERRORAn internal service was momentarily unreachable.
One of the services we depend on was temporarily unreachable when your request arrived. It is a problem on our side, not with what you sent.
How to fix it
Retry after the seconds reported in Retry-After, ideally with a small random jitter. If it lasts more than a couple of minutes, check the status page or open a support ticket.
40011#infra-db-connection-error504INFRA_DB_QUERY_TIMEOUTAn internal query took too long and was cut off.
An internal query took longer than we allow before returning a response, so we cut it off instead of leaving your client waiting indefinitely.
How to fix it
Retry immediately: isolated occurrences are usually transient. If you see several in a row, open a support ticket quoting the meta.requestId.
400BAD_REQUESTWe could not parse your request.
We could not parse your request. The most common causes are malformed JSON in the body, a Content-Type header that does not match the payload, or a path with invalid characters.
How to fix it
Inspect the raw payload your client is sending. Validating the JSON locally before the call usually reveals the problem right away.
403FORBIDDENGeneric 403: fallback when no `ACCOUNT_*` applies.
The request authenticated correctly but is not allowed to access this resource. When the cause is a known organization state you get an ACCOUNT_* code instead; this generic one is the fallback.
404NOT_FOUNDThe path you requested does not exist in this API.
The path you requested does not exist in this API. We also return this code, deliberately indistinguishable from a normal 404, for requests whose Host header is not on our allowed list, so the API surface cannot be probed by guessing hostnames.
How to fix it
Compare the path against the reference. Watch out for trailing slashes and casing: paths are case-sensitive.
405METHOD_NOT_ALLOWEDThe path exists but does not accept this HTTP method.
The path exists but does not accept the HTTP method you used, for example a POST to a read-only endpoint. The response includes an Allow header with the valid methods.
429TOO_MANY_REQUESTSGeneric 429: prefer the `RATE_*` codes when present.
Generic 429. When the limit you hit is one of ours we return RATE_DDOS_EXCEEDED or RATE_TPS_EXCEEDED instead, which carry more context. Branch on the specific ones when present.
500INTERNAL_SERVER_ERRORSomething went wrong on our side.
Something went unexpectedly wrong while processing your request. The full trace is stored against the same meta.requestId we returned, so we can investigate without you having to reproduce the failure.
How to fix it
Retry once with a small backoff. If you keep seeing it, open a support ticket and include the requestId.
504GATEWAY_TIMEOUTGeneric 504: a service we depend on took too long.
A service we depend on took too long to respond. When the slow component was the database you will see INFRA_DB_QUERY_TIMEOUT instead, which means the same with more context.
How to fix it
Retry with exponential backoff. Several in a row usually indicate a wider degradation: open a support ticket with the requestId.
How the numeric code is structured
Every error.legacyCode is a six-digit number: the first two identify the error domain and the remaining four the specific condition. It only appears when a bridge from v5 exists; codes introduced in v6 omit it.
| Prefix | Domain | Covers |
|---|---|---|
01xxxx | AUTH | API key, tokens, source IP, identity. |
02xxxx | RATE | Per-IP guard and per-endpoint quotas. |
03xxxx | VALIDATION | Request shape, JSON, parameters, types. |
04xxxx | ACCOUNT | Organization information, state, blocks. |
05xxxx | BILLING | Balance, payments, currency, exchange rate. |
06xxxx | SMS | SMS service specific errors. |
07xxxx | WhatsApp service. | |
08xxxx | Email service. | |
09xxxx | CALLBLASTING | Calling service. |
10xxxx | URL_SHORTENER | URL shortener. |
11xxxx | UTILITIES | Ping and other diagnostic endpoints. |
12xxxx | TOOLS | Catalogs and utilities. |
90xxxx | INFRA | Internal dependencies and availability. |
99xxxx | INTERNAL | Unhandled exceptions. |
Prefixes 13xxxx through 89xxxx are reserved for future services, to avoid renumbering.