retailerapi / docs

Errors

Status codes, error shape, and how to handle each kind.

Error response shape

Errors return a JSON object with at minimum:

{
  "error": "human-readable message",
  "code": "machine-readable code"
}

The HTTP status code drives behavior; the error message is for logs; the code is what you branch on.

404 not-found responses and the name_not_supported 400 also carry extra, additive fields to help you (or your agent) self-correct: identifier_type (the type we inferred), reason (a more specific machine code, e.g. upc_not_in_catalog or item_id_not_found), hint (a human-readable did-you-mean), and docs_url. code is unchanged, so existing branching keeps working.

Status code reference

StatuscodeMeaningRetry?
400bad_requestInvalid identifier or query paramNo (fix client)
400name_not_supportedA product name / free text was sent to this ID-and-URL endpoint (no search-by-name)No (send a UPC/ID/URL)
401unauthorizedMissing / malformed bearerNo (check key)
403forbiddenKey revoked, scope mismatch, or out of quotaNo
404not_foundIdentifier not in our catalog (body carries identifier_type + reason + hint)No
404scrape_pendingNot cached yet; a live fetch was started for this product URL (body carries retry_after_seconds)Yes (retry in ~30-60s)
402payment_requiredAccount past dueAfter billing fixed
429rate_limitedThrottle or monthly quota exceededYes (Retry-After)
500internal_errorOur problem; check status pageYes (with backoff)
502upstream_unavailableWalmart backend transient issueYes (with backoff)
503service_unavailableMaintenance or capacityYes (with backoff)
504upstream_timeoutWalmart backend slowYes (with backoff)

Customer-safe upstream messages

retailerapi never echoes raw upstream error messages back to your client. We map upstream failures to canonical messages:

  • 404 upstream — "Product not found"
  • 401 / 403 upstream — "Service authentication failed" (you don't see our internal auth issues)
  • 429 upstream — "Rate limited, retry shortly"
  • 5xx upstream — "Service temporarily unavailable"

Recommended retry policy

async function callWithRetry(url, opts, maxRetries = 3) {
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
    const r = await fetch(url, opts);
    if (r.status < 500 && r.status !== 429) return r; // not retriable

    if (r.status === 429) {
      const retryAfter = Number(r.headers.get('Retry-After') ?? '5');
      await sleep(retryAfter * 1000);
      continue;
    }
    if (r.status >= 500 && attempt < maxRetries) {
      await sleep(2 ** attempt * 500); // exponential backoff
      continue;
    }
    return r; // give up
  }
}

Sentry / error tracking

retailerapi's own server errors are tracked in Sentry. We do NOT log your request bodies, only stack traces and route names. If you encounter a persistent 500 on a specific identifier, email hello@retailerapi.com with the identifier and we'll investigate.