Errors
Error codes, response format, and how to handle errors.
All error responses follow a consistent format:
{
"success": false,
"error": "Human-readable error message"
}Error codes
| HTTP Status | Code | Description |
|---|---|---|
400 | VALIDATION_ERROR | Invalid request parameters. Check the error field for details on which parameters failed validation. |
401 | AUTHENTICATION_ERROR | Missing or invalid API key. Ensure you're passing a valid key in the x-api-key header. |
403 | FORBIDDEN | Your API key doesn't have access to this resource. |
408 | TIMEOUT_ERROR | The page didn't finish loading within the configured timeoutMs. Try increasing the timeout or using a different waitFor strategy. |
429 | RATE_LIMIT_ERROR | You've exceeded your plan's rate limit. Wait and retry, or upgrade your plan for higher limits. |
429 | USAGE_LIMIT_ERROR | Your organization has run out of credits for this billing period. Purchase more credits or wait for the next billing cycle. |
500 | BROWSER_ERROR | An internal browser error occurred. This is usually transient — retry the request. |
502 | TARGET_SITE_ERROR | The target website is unreachable, returned an error, or blocked the request. Consider using a proxy. |
502 | PROXY_ERROR | The proxy connection failed. This is usually transient — retry the request. |
Handling errors
Retryable errors
These errors are typically transient and safe to retry with exponential backoff:
408 TIMEOUT_ERROR— try increasingtimeoutMs429 RATE_LIMIT_ERROR— wait and retry (checkRetry-Afterheader if present)500 BROWSER_ERROR— retry after a short delay502 PROXY_ERROR— retry after a short delay
Non-retryable errors
These errors require fixing the request before retrying:
400 VALIDATION_ERROR— fix the request parameters401 AUTHENTICATION_ERROR— check your API key403 FORBIDDEN— check your API key permissions429 USAGE_LIMIT_ERROR— add credits or wait for the next billing cycle502 TARGET_SITE_ERROR— the target site may be down, or you may need to use a proxy
Example retry logic
async function scrapeWithRetry(url, maxRetries = 3) {
for (let attempt = 0; attempt <= maxRetries; attempt++) {
const response = await fetch('https://api.pagepry.com/v1/scrape', {
method: 'POST',
headers: {
'x-api-key': process.env.PAGEPRY_API_KEY,
'Content-Type': 'application/json',
},
body: JSON.stringify({ url }),
});
if (response.ok) return response.json();
const data = await response.json();
const retryable = [408, 500, 502].includes(response.status)
|| (response.status === 429 && data.code === 'RATE_LIMIT_ERROR');
if (!retryable || attempt === maxRetries) throw new Error(data.error);
const delay = Math.min(1000 * 2 ** attempt, 30000);
await new Promise(resolve => setTimeout(resolve, delay));
}
}
