Docs
Ctrl+K Search Alt+[Alt+] Guides
Get API key

Guides

Errors

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. credits 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 Unauthorized 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 GET). server-default HTML — switch to POST JSON.
429 Too Many Requests Either rate-limited or out of credits (see below). Rate limit exceeded. Max 60 requests per minute. of 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 credits object, or on a substring match for "Monthly credit limit". See Rate limits and Credits & billing 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, limit 429, generic catch 500) always return JSON with success: false and a matching HTTP status.
  • Some JSON handlers (especially large TikTok Shop payloads) may return HTTP 200 with a body that includes business-level flags — always read the schema for that endpoint rather than assuming success tracks 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:

  1. Capture the request URL, method, headers (redact your key), and body.
  2. Capture the response status and body.
  3. Note the timestamp.
  4. Send the bundle to support — the team can correlate with server logs by user ID + timestamp.