Skip to the content

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.

The error body

{ "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).

Error codes

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 codes

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).

Validation errors

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" }.

State conflicts

{ "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.

Plan refusals

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"}

Handling errors

  • Treat 401 and 403 as 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 in Retry-After when the header is there, then retry.
  • 500, 502, 503 and network errors: retry with a growing pause, and log X-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 repeated POST /api/classify creates a second lookup, which can be charged a second time. POST /api/skus, and POST /api/skus/bulk without classify_missing, update an existing SKU and are safe to repeat; with classify_missing, a repeat classifies again every SKU that still has no code.