Errors
The API uses standard HTTP status codes. A 2xx response means the request succeeded; a 4xx response means something about the request needs to change before it will succeed; a 5xx response means the problem was on our side, and the same request may succeed if retried later.
Most error responses include a body describing what went wrong. Its format depends on where the error happened, so check the status code first and the body second.
Error response format
Errors from the API itself use the vnd.error format: a total count and one or more errors embedded under fx:errors.
HTTP/1.1 400 Bad Request
Content-Type: application/hal+json
{
"total": 1,
"_links": {
"curies": [
{
"name": "fx",
"href": "https://api.foxycart.com/rels/{rel}",
"templated": true
}
]
},
"_embedded": {
"fx:errors": [
{
"logref": "id-1790888757",
"message": "The FOXY-API-VERSION request header is required in order to use the API. Please include the following header with all requests: FOXY-API-VERSION: 1"
}
]
}
} | Field | Description |
|---|---|
total | The number of errors in fx:errors. |
fx:errors[].message | A human-readable description of the problem. Messages can change; don't parse them. |
fx:errors[].logref | A reference for this error. Include it when you contact support about a failed request. |
fx:errors[]._links.help | Present on some errors: a link to the documentation for the resource involved. |
Content type
The error body follows the content type you asked for in your Accept header:
| You requested | Error Content-Type | Body |
|---|---|---|
application/hal+json (the default) | application/hal+json | JSON, as above |
application/vnd.siren+json | application/vnd.error+json | JSON, as above |
application/hal+xml | application/hal+xml | XML, as below |
<?xml version="1.0" encoding="utf-8"?>
<resource>
<total>1</total>
<resource logref="id-1790888757">
<message>The FOXY-API-VERSION request header is required in order to use the API. ...</message>
</resource>
</resource> Authentication errors
When the access token is missing, invalid or expired, the response is a 401 Unauthorized in the OAuth 2.0 format instead, with error and error_description fields. Errors from the /token endpoint use the same format.
HTTP/1.1 401 Unauthorized
Content-Type: application/json; charset=utf-8
{"error":"invalid_token","error_description":"The access token provided is invalid"} error | Meaning | Action required |
|---|---|---|
missing_token | No access token was sent. | Send the token in an Authorization: Bearer header. |
invalid_token | The token isn't valid, or it has expired. | If it has expired, use your refresh token to get a new one. Otherwise, obtain a new token. |
Errors during the OAuth authorization flow itself are redirected back to your application. See 3rd Party Integrations.
Errors from our edge
Some errors are returned before a request reaches the API: by the CDN, the firewall, or the web server in front of the API. These don't use the formats above, and the body may be HTML or empty. Handle them by status code alone.
Status codes
| Status | Meaning | Retry? |
|---|---|---|
400 Bad Request | The request isn't valid: a missing FOXY-API-VERSION header, a property that doesn't exist or has an invalid value, query parameters that can't be applied, or a path or method the API doesn't support. The message says which. | No. Fix the request first. |
401 Unauthorized | The access token is missing, invalid or expired (OAuth format, above), or the token doesn't have access to the resource you requested (vnd.error format), for example a store the user no longer has access to, or a scope that doesn't cover the resource. See Permissions. | Only after refreshing an expired token. |
403 Forbidden | The action isn't allowed for this store or account, or the request was blocked by our firewall. | No. |
404 Not Found | The resource you requested doesn't exist, or no longer exists. | No. |
409 Conflict | The change isn't allowed in the resource's current state. | No. The message explains what's preventing it. |
413 Payload Too Large | The request body is larger than we accept. | No. |
429 Too Many Requests | This API client has too many requests in progress at once. See Concurrency limits. | Yes, after the Retry-After delay and with fewer requests in parallel. |
500 Internal Server Error | Something went wrong on our side. | Yes, with backoff. If it persists, contact support with the logref. |
502 Bad Gateway503 Service Unavailable | The API is temporarily unavailable. | Yes, with backoff. |
504 Gateway Timeout | The request took longer than 30 seconds and was stopped. This is almost always a request for too much at once. | Yes, but make the request smaller first: a lower limit, fewer zoom levels, or a narrower filter. Retrying the same request will usually time out again. |
Concurrency limits
Each API client (each access token) can have a limited number of requests in progress at the same time. Requests beyond that limit are rejected immediately with a 429 Too Many Requests response in the usual error format, with a Retry-After header giving the number of seconds to wait. The limit is on simultaneous requests, not on requests per minute: sequential requests, however many, are never limited.
HTTP/1.1 429 Too Many Requests
Content-Type: application/hal+json
Retry-After: 5
{
"total": 1,
"_links": { "curies": [ ... ] },
"_embedded": {
"fx:errors": [
{
"logref": "rate_limit_concurrency-3a9f54dc4b6e57ca45152c1638821ef7",
"message": "Too many concurrent requests for this API client. Send fewer requests in parallel or request smaller pages (a lower limit or fewer zoom levels), then retry with backoff."
}
]
}
} The logref of a concurrency rejection always starts with rate_limit_concurrency, so you can detect it without parsing the message.
Retrying
- Retry only
429and5xxresponses. Other4xxresponses will fail the same way until the request changes. - Back off between attempts. Wait longer after each failure (exponential backoff), add a random delay (jitter) so parallel workers don't retry in lockstep, and stop after a few attempts. For a
429, wait at least as long asRetry-After. - Make slow requests smaller instead of retrying them. A request that timed out will usually time out again. Request fewer results per page, and zoom only the relations you need.
- Limit your own parallelism. Running a few requests at a time, and paging through large collections in sequence, is faster overall than many requests at once that end in
429or504responses. - Be careful retrying writes. A POST, PATCH or PUT that ended in a
5xxor a timeout may still have been applied. Check the resource before retrying, so you don't create a duplicate.