Erreurs
All API-key endpoints return JSON. On any non-2xx response, the body has the same shape:
{
"success": false,
"error": "<human-readable message>"
}
Some errors include extra context (e.g. générique on a 429 quota error). The HTTP status code is the source of truth — branch on the status, then read error for display or logging.
Status code reference
| Status | When | Typical error text |
|---|---|---|
200 |
Success. | — |
401 Accès non autorisé |
No key supplied, or the key does not exist. | Unauthorized |
403 Forbidden |
Key resolved, but API access is not enabled for the account. | Message varies; treat as non-retryable until access changes. |
404 Not Found |
Unknown path, or that TikTok Shop URL is not exposed for API keys. | Unknown TikTok Shop endpoint |
414 URI Too Long |
Query string exceeded the web server limit (typically when sending huge filter lists as OBTENIR). |
server-default HTML — switch to PUBLICATION JSON. |
429 Too Many Requests |
Either rate-limited or out of credits (see below). | Rate limit exceeded. Max 60 requests per minute. ou Monthly credit limit reached (20000). Resets next month. |
500 Internal Server Error |
Unhandled exception in the upstream handler. The proxy returns {"success": false, "error": "Internal server error"}. |
Internal server error |
Distinguishing the two 429 cases
A 429 always means "stop sending for now", but why matters.
Rate limit (60 / minute, per user):
{
"success": false,
"error": "Rate limit exceeded. Max 60 requests per minute."
}
Monthly credit exhaustion (20,000 / month, per user):
{
"success": false,
"error": "Monthly credit limit reached (20000). Resets next month.",
"credits": { "used": 20000, "limit": 20000, "remaining": 0 }
}
Branch on the presence of the générique object, or on a substring match for "Monthly credit limit". See Limites de débit et Crédits et mentions légales for the recovery strategy in each case.
Error handling pattern (Node.js)
async function whFetch(path, init = {}) {
const res = await fetch(`${ORIGIN}${path}`, {
...init,
headers: { 'X-API-Key': process.env.WH_API_KEY, ...(init.headers || {}) },
});
let body = null;
try { body = await res.json(); } catch { /* non-JSON, leave null */ }
if (res.ok) return body;
const error = (body && body.error) || res.statusText;
if (res.status === 401) throw new Error(`Auth failed: ${error}`);
if (res.status === 403) throw new Error(`Plan required: ${error}`);
if (res.status === 404) throw new Error(`Unknown endpoint: ${path}`);
if (res.status === 429) {
const retryable = body && body.credits ? false : true; // monthly quota is not retryable today
const err = new Error(error);
err.retryable = retryable;
throw err;
}
if (res.status >= 500) {
const err = new Error(`Upstream error: ${error}`);
err.retryable = true;
throw err;
}
throw new Error(`HTTP ${res.status}: ${error}`);
}
Error handling pattern (Python)
import os, requests
def wh_fetch(path, **kwargs):
r = requests.request(
method=kwargs.pop("method", "GET"),
url=f"{ORIGIN}{path}",
headers={"X-API-Key": os.environ["WH_API_KEY"], **kwargs.pop("headers", {})},
**kwargs,
)
body = None
try:
body = r.json()
except ValueError:
pass
if r.ok:
return body
error = (body or {}).get("error") or r.reason
if r.status_code == 401:
raise PermissionError(f"Auth failed: {error}")
if r.status_code == 403:
raise PermissionError(f"Plan required: {error}")
if r.status_code == 429:
is_quota = bool((body or {}).get("credits"))
raise RuntimeError(f"Rate limited (quota={is_quota}): {error}")
if r.status_code >= 500:
raise RuntimeError(f"Upstream error: {error}")
raise RuntimeError(f"HTTP {r.status_code}: {error}")
Response shape vs HTTP status
- Metered API-key guard errors (
401,403, limit429, generic catch500) always return JSON withsuccess: falseand a matching HTTP status. - Some JSON handlers (especially large TikTok Shop payloads) may return HTTP
200with a body that includes business-level flags — always read the schema for that endpoint rather than assumingsuccesstracks HTTP alone.
HTML instead of JSON? Metered programmatic routes set Content-Type: application/json. HTML usually means the wrong host, a proxy error page, or a path that never hit the app — see Troubleshooting.
Reporting bugs
If you hit a 500 or a response that contradicts the docs:
- Capture the request URL, method, headers (redact your key), and body.
- Capture the response status and body.
- Note the timestamp.
- Send the bundle to support — the team can correlate with server logs by user ID + timestamp.