Errors
The error body of the HTS Pilot API with its machine-readable code, the status codes in use, validation errors, plan refusals and how messages follow Accept-Language.
An error is answered with an HTTP status of 4xx or 5xx and a JSON body. It is never a 200 with an error
inside.
{ "detail": "Lookup not found", "code": "not_found" }
| Field | Type | Meaning |
|---|---|---|
detail |
string | What went wrong, written for a person, in the language of the request |
code |
string | What went wrong, for a program. In every error the API writes |
request_id |
string | Only on a 500: the value of the X-Request-ID header of the answer |
details |
object | More about the error, when there is more to say. Absent otherwise |
errors |
array | Only on a 422 for invalid input: one entry per invalid field, see below |
Branch on code and show detail. code is lower snake case, the same in every language, and is not changed
once it is published. detail is a sentence: it is translated (Languages) and its wording can
change. Every answer also carries the header X-Request-ID (Request identifiers).
Most errors carry the code of their status. A few are more specific:
code |
Status | Meaning |
|---|---|---|
bad_request |
400 |
The request cannot be carried out as it was sent |
unauthenticated |
401 |
No valid API key was sent, or it was not accepted |
payment_required |
402 |
The call needs something the account has not paid for |
out_of_credits |
402 |
The credits of the account are used up |
forbidden |
403 |
The call is refused for this caller, usually because of its role |
plan_feature |
403 |
The plan of the account does not include this function. details.key names it |
plan_limit |
403 |
The call goes over a limit of the plan. details has the limit and what was asked |
not_found |
404 |
The address or the record does not exist, or belongs to another user |
method_not_allowed |
405 |
The address exists and does not take this method |
conflict |
409 |
The state of the record does not allow the call |
payload_too_large |
413 |
The body or the file sent is larger than the API accepts |
validation_error |
422 |
The input is not valid. errors or details say which field |
rate_limited |
429 |
Too many calls in a short time. Retry-After, when the answer has it, says how many seconds to wait |
internal_error |
500 |
An unexpected failure. The body also carries request_id |
upstream_error |
502 |
A service this API depends on failed or did not answer |
service_unavailable |
503 |
The function is not available at the moment |
More specific codes may be added later (Versioning and changes). Treat a code you do not know like the status it came with.
An answer produced before the request reached the API (a 502, 503 or 504 during an outage or a release, or
a refusal at the edge of the network) may have another body, or none, and no code: fall back on the HTTP
status.
| Status | When |
|---|---|
200 |
The call succeeded |
202 |
A lookup was created and is being classified in the background (Asynchronous work) |
400 |
The request cannot be processed as sent |
401 |
No valid API key was sent (Authentication) |
402 |
The account that owns the work cannot be charged for it. Whether this can occur depends on the account (Rate limits and quotas) |
403 |
The role of the key, or the plan of the account, does not allow the call |
404 |
The object does not exist, or belongs to another account and the key has the entry role |
405 |
The address exists and does not take this method |
409 |
The state of the object does not allow the call: a batch that is already running, a decision that is not there to reopen |
413 |
The body or the file sent is larger than the API accepts |
422 |
The input is not valid: a field failed validation, or a rule of the operation was not met |
429 |
Too many requests of a kind that is limited per minute |
500 |
An unexpected error on our side. The body carries a general message, code and request_id |
502 |
A service the call depends on did not answer: the classification engine or an official source |
503 |
A function is not available at the moment |
A lookup that fails while it is being classified is not an HTTP error: the lookup ends with the status error
and a message in its error field (Asynchronous work).
When a field of the request fails validation, the answer is 422 with one entry per field:
{
"detail": "Invalid input",
"code": "validation_error",
"errors": [
{ "field": "description", "message": "String should have at least 1 character" }
]
}
field is the path of the field in the request (items.0.sku for the first item of a list). The message of
an entry comes from the validation library and is in English.
When a rule of the operation is not met, the answer is also 422, with detail alone or with details:
{ "detail": "Enter the code to finalize", "code": "validation_error" }
A webhook URL that is refused names the field and the reason:
"details": { "field": "url", "reason": "webhook_private" }.
{ "detail": "The lookup is not finalized", "code": "conflict" }
409 means the request was understood and the object is in a state that does not allow it. Read the object
again before deciding what to do.
A feature the plan of the account does not include, or a limit it would exceed, answers 403 with the code
plan_feature or plan_limit and a details object that names the entitlement. The code is repeated in
details.code, where it was before code existed:
code |
Fields of details |
Meaning |
|---|---|---|
plan_feature |
key |
The plan does not include the feature key (for example feature.webhooks) |
plan_limit |
key, limit, requested |
The call would make requested, and the plan allows limit (for example batch_max_rows) |
GET /api/plans/me lists what the plan allows (Usage).
detail follows the Accept-Language header of the request, and the answer names its language in
Content-Language:
curl -i https://htspilot.com/api/usage/me -H "Accept-Language: vi"
HTTP/1.1 401 Unauthorized
Content-Language: vi
X-Request-ID: 6050d46ca7a64302a588aacbda18fdd8
{"detail":"Vui lòng đăng nhập","code":"unauthenticated"}
- Treat
401and403as configuration problems: do not retry them. 402: when it occurs, stop sending work that creates lookups until the account can be charged again. Do not retry it.404,409,422: fix the request; the same request will fail again.429: wait a minute, or the number of seconds inRetry-Afterwhen the header is there, then retry.500,502,503and network errors: retry with a growing pause, and logX-Request-ID. Reads are safe to repeat. Before repeating a call that creates something, check whether the first one went through: there is no idempotency key, so a repeatedPOST /api/classifycreates a second lookup, which can be charged a second time.POST /api/skus, andPOST /api/skus/bulkwithoutclassify_missing, update an existing SKU and are safe to repeat; withclassify_missing, a repeat classifies again every SKU that still has no code.