{P}

Error Codes

HTTP status codes and application error types returned by the API.

Error response format

All errors follow a consistent JSON structure:

{
  "error": true,
  "statusCode": 400,
  "message": "The 'url' field is required.",
  "code": "MISSING_URL",
  "requestId": "req_01j8x..."
}
FieldTypeDescription
errortrueAlways true for error responses
statusCodenumberHTTP status code
messagestringHuman-readable description
codestringMachine-readable error code
requestIdstringInclude this when contacting support

HTTP status codes

400 Bad Request

The request body is malformed or missing required fields.

CodeMessage
MISSING_URLThe url field is required
INVALID_URLURL must start with https://
INVALID_BATCHurls array must contain 1–20 items
INVALID_STRATEGYUnknown strategy name in strategies array
INVALID_REQUEST_IDrequestId contains invalid characters

401 Unauthorized

{
  "error": true,
  "statusCode": 401,
  "message": "Invalid or missing API key.",
  "code": "UNAUTHORIZED"
}

Check that you're passing the key correctly:

-H "Authorization: Bearer pp_live_your_key"

Common mistakes:

  • Forgot the Bearer prefix
  • Copied extra whitespace around the key
  • Used a pp_test_ key against production (or vice versa)

403 Forbidden

{
  "error": true,
  "statusCode": 403,
  "code": "PLAN_LIMIT",
  "message": "This feature requires the Pro plan."
}

Returned when a Pro/Scale-only parameter (includeScreenshot, forceRefresh, etc.) is used on a Free plan key.


422 Unprocessable Entity

The URL resolved successfully but didn't contain extractable product data.

CodeMessage
NOT_A_PRODUCT_PAGEURL appears to be a category, homepage, or search page
PAGE_LOAD_FAILEDThe page returned an error (4xx/5xx from the target server)
EXTRACTION_FAILEDAll extraction strategies returned no usable data
LOW_CONFIDENCEData was found but confidence is below the minimum threshold

If you're getting NOT_A_PRODUCT_PAGE on a valid product URL, try adding an extractionHint describing what you're looking for, or use strategies: ["visual"] to force visual extraction.


429 Too Many Requests

{
  "error": true,
  "statusCode": 429,
  "code": "RATE_LIMIT",
  "message": "Rate limit exceeded. Retry after 12 seconds.",
  "retryAfter": 12
}

The Retry-After response header and retryAfter body field both indicate the number of seconds to wait before retrying.

Implement exponential back-off for batch processing:

async function parseWithRetry(url, retries = 3) {
  for (let i = 0; i < retries; i++) {
    try {
      return await parseProduct(url)
    } catch (err) {
      if (err.statusCode === 429 && i < retries - 1) {
        const wait = (err.retryAfter ?? 5) * 1000 * Math.pow(2, i)
        await new Promise(r => setTimeout(r, wait))
        continue
      }
      throw err
    }
  }
}

500 Internal Server Error

{
  "error": true,
  "statusCode": 500,
  "code": "INTERNAL_ERROR",
  "message": "An unexpected error occurred.",
  "requestId": "req_01j8x..."
}

These are rare. Check the status page first. If the issue persists, open a support ticket with the requestId and the URL you were trying to parse.


Handling errors in code

JavaScript

const res = await fetch('https://api.productparse.dev/v1/parse', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${process.env.PP_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({ url }),
})

if (!res.ok) {
  const err = await res.json()
  // err.code, err.message, err.requestId
  throw new Error(`[${err.code}] ${err.message}`)
}

const data = await res.json()

Python

import httpx

try:
    response = client.post("/v1/parse", json={"url": url})
    response.raise_for_status()
    return response.json()
except httpx.HTTPStatusError as e:
    err = e.response.json()
    raise ValueError(f"[{err['code']}] {err['message']}")