Skip to content

How to handle API errors

Error handling is an essential part of any high-quality application. Make sure you handle all errors returned by the ScreenshotOne API correctly and provide informative explanations to your customers or react to them accordingly.

Following HTTP standards

The ScreenshotOne API is on-purpose built using HTTP protocol and follows HTTP standards as much as possible. To make sure the API will be stable and can support customers for years to come.

In general, if the resulting status code is in the range of 400-599, then we are dealing with errors.

GET https://api.screenshotone.com/?[options]
Content-Type: application/json
{
"is_successful":false,
"error_code": "an_error_code",
"error_message": "An error message"
}

Errors caused by API consumers

All errors caused by invalid requests, absent credentials, or any reasons caused by you, an API consumer, are marked with status codes 400-499. It means, that until you find a way to fix them, the request won’t be executed successfully.

API internal errors

Errors with status codes in the range 500-599 are caused by the API internal reasons and you have almost little influence over that.

But you can safely retry them. Except in a few cases. When you get a network error (network_error), it might be because the site blocks the API, and there is no sense in retrying the request. Or if the site returned an error (host_returned_error) and it is an error within the range 400-499, it means that the API is either blocked, or you need to change the request to the site.

Showing errors to your end users

In general, you can try to just return the error message provided by the API, but if you want to show a more user-friendly message, you can use the following code:

const response = await fetch("https://api.screenshotone.com/...");
// after retries and other processing, once you decide to show an error to your end user
if (!response.ok) {
const errorData = await response.json();
const errorMessage = generateUserFriendlyErrorMessage(errorData);
// show the error to your end user in your UI, CLI or any other way
showErrorToUser(errorMessage);
}
function generateUserFriendlyErrorMessage(error) {
// these are error messages for your public users, not for you
switch (error.error_code) {
case "screenshots_limit_reached":
return "The screenshot rendering is not available. Please, retry later.";
case "concurrency_limit_reached":
case "temporary_unavailable":
return "Please try again in a moment.";
case "request_not_valid":
return "Please, make sure your request is valid and try again.";
case "selector_not_found":
return "The target element was not found on the page";
case "name_not_resolved":
return "Unable to resolve the domain name. Check that there is no typo in the URL. If this is a new site, please wait for DNS propagation.";
case "network_error":
return "Unable to connect to the requested URL. The site may be blocking access or temporarily down.";
case "host_returned_error":
if ([401, 403, 429].includes(error.returned_status_code)) {
return "The target website blocks automated screenshot rendering.";
}
if (error.returned_status_code >= 500) {
return "The target website is temporarily down. Please, retry later.";
}
if (error.returned_status_code == 404) {
return "The target website returned a 404 HTTP error—the page not found.";
}
return `The target website returned a ${error.returned_status_code} HTTP error.`;
case "timeout_error":
return "The screenshot rendering timed out. Please, try again.";
case "storage_returned_transient_error":
case "internal_application_error":
case "request_aborted":
return "Failed to render the screenshot. Please try your request again";
case "access_key_required":
case "access_key_invalid":
case "signature_is_required":
case "signature_is_not_valid":
case "invalid_storage_configuration":
case "script_triggers_redirect":
case "storage_access_denied":
case "content_contains_specified_string":
case "invalid_cookie_parameter":
case "resulting_image_too_large":
// these are errors that often are not caused by the end user action,
// and they need to be fixed on your or our side
return "The screenshot rendering failed. Please, reach out to support.";
default:
// return a generic error message
// or the message provided by the API error.error_message
return "The screenshot rendering failed. Please, reach out to support.";
}
}

These are the most common errors often caused by end users. If you want to process more codes, check out all our errors.

When to retry with a proxy

Do not send every request through a residential or premium proxy by default. Proxies are slower, more expensive, and introduce another service that can fail. The practical strategy is:

  1. Try the request without a proxy.
  2. If the response suggests an IP reputation, location, rate-limit, or routing issue, retry the same request once with a proxy.
  3. If the proxied request also fails, inspect the error instead of looping forever.

Use proxies only for pages you own, pages you are authorized to test, or pages where automated access is allowed. Do not use proxy retries to access content you are not allowed to access, evade account restrictions, bypass paywalls, ignore robots or site terms, or work around a clear “no automated access” decision by the target website.

Error or signalRetry without a proxy?Retry with a proxy?Notes
temporary_unavailableYes, with backoffNo, not firstThis is usually ScreenshotOne-side load or a transient internal issue. A proxy does not help.
internal_application_errorYes, with backoffNo, not firstRetry the same request. If it repeats, contact support.
concurrency_limit_reachedYes, after concurrency.resetNoUse the usage endpoint and queue requests. A proxy does not change your ScreenshotOne request limit.
storage_returned_transient_errorYes, with backoffNoThe target page rendered, but your storage returned a transient error.
request_abortedYes, after checking client/serverless timeoutsUsually noIncrease your HTTP client timeout first. Retry with a proxy only if the abort happens while using your own proxy and you are testing a different proxy endpoint.
network_errorSometimesYes, if permitted and repeatedUseful when the target blocks ScreenshotOne IPs, has regional routing issues, or your chosen proxy has access to the target host.
timeout_errorSometimesSometimesFirst reduce page weight, adjust timeout or navigation_timeout, or use async requests. Try a proxy if the timeout looks like IP-based throttling or blocking.
host_returned_error with returned_status_code=403Usually noSometimesRetry with a proxy only when you are allowed to access the page and the 403 looks like IP/location reputation. If it is an authentication or permission error, fix authentication or stop.
host_returned_error with returned_status_code=429Yes, after waitingSometimesRespect rate limits. A proxy may help only when you are allowed to retry and the target expects traffic from a different approved route.
host_returned_error with returned_status_code=502, 503, or 504Yes, with backoffSometimesRetry normally first. A proxy can help when a CDN or edge route behaves differently by location or IP range.
content_contains_specified_stringNoSometimesUse this only when you intentionally configured fail_if_content_contains to detect a known block page or challenge page.
content_missing_specified_stringNoSometimesUse this only when you intentionally configured fail_if_content_missing to detect that expected content did not load.
name_not_resolvedAfter DNS changes, yesUsually noFix the URL or wait for DNS propagation. A proxy is not the first fix for a bad or unavailable hostname.
request_not_valid, access_key_invalid, signature_is_not_valid, selector_not_found, storage_access_denied, resulting_image_too_largeNoNoChange the request, credentials, selector, storage configuration, or image options.

An example of retrying with a proxy only for selected errors:

const apiUrl = "https://api.screenshotone.com/take";
const accessKey = process.env.SCREENSHOTONE_ACCESS_KEY;
const proxyUrl = process.env.SCREENSHOT_PROXY_URL; // http://user:pass@host:port
if (!accessKey) {
throw new Error("SCREENSHOTONE_ACCESS_KEY is required");
}
function shouldRetryWithProxy(error) {
if (error.error_code === "network_error") {
return true;
}
if (error.error_code === "timeout_error") {
return true;
}
if (error.error_code === "host_returned_error") {
const status = Number(error.returned_status_code);
return [403, 429, 502, 503, 504].includes(status);
}
return [
"content_contains_specified_string",
"content_missing_specified_string",
].includes(error.error_code);
}
function buildScreenshotUrl(options) {
const url = new URL(apiUrl);
url.searchParams.set("access_key", accessKey);
for (const [key, value] of Object.entries(options)) {
if (Array.isArray(value)) {
for (const item of value) {
url.searchParams.append(key, item);
}
} else if (value !== undefined && value !== null) {
url.searchParams.set(key, String(value));
}
}
return url;
}
async function takeScreenshotWithOptionalProxy(options) {
const response = await fetch(buildScreenshotUrl(options));
if (response.ok) {
return await response.arrayBuffer();
}
const error = await response.json();
if (!proxyUrl || !shouldRetryWithProxy(error)) {
throw new Error(error.error_message || "Screenshot rendering failed");
}
const retryOptions = {
...options,
proxy: proxyUrl,
};
const proxyResponse = await fetch(buildScreenshotUrl(retryOptions));
if (!proxyResponse.ok) {
const proxyError = await proxyResponse.json();
throw new Error(
proxyError.error_message || "Screenshot rendering failed with proxy"
);
}
return await proxyResponse.arrayBuffer();
}
const screenshot = await takeScreenshotWithOptionalProxy({
url: "https://example.com",
format: "png",
});

Reporting errors

All ScreenshotOne API errors are logged and we are acknowledged by them. And will react to them as fast as possible.

In general, you don’t need to report to us any errors. But if it blocks your work or you want us to prioritize fixing them, please, feel free to report an error at support@screenshotone.com.

Error reference

All API errors are listed in the error reference.