ScreenshotNeo

BlogHow-to

ScreenshotAPI.net Rate Limits: How to Handle 429 Errors

Learn what ScreenshotAPI.net’s 429 means, how to pace requests and retry safely, and how to distinguish rate limiting from monthly quota exhaustion.

By the ScreenshotNeo team4 October 20268 min read

When ScreenshotAPI.net returns HTTP 429, your requests have exceeded the requests-per-minute allowance for your plan. Slow the request producer, queue or pace work, then retry after a pause. If the response includes Retry-After, wait for the indicated interval. The header is optional under HTTP, and ScreenshotAPI.net’s reviewed documentation does not say that every 429 response includes it. Check the HTTP status as well as the error label: the provider uses screenshots_limit_reached for both 429 rate limiting and 403 monthly quota exhaustion.

The provider publishes a plan-dependent limit range of 20–80 requests per minute. Check your account’s plan for its applicable limit before setting a production request rate; the published source does not provide a complete plan-by-plan mapping. ScreenshotAPI.net error documentation · ScreenshotAPI.net Help.

1. Identify which limit you hit

HTTP status Likely condition What to do
429 Requests-per-minute allowance exceeded. Reduce concurrency or request pace, pause, then retry with backoff.
403 Monthly screenshot quota exhausted, according to the provider’s Help page. Check account usage and plan allowance. Repeating the same request will not restore monthly quota.

Both conditions can use the error name screenshots_limit_reached, so do not diagnose from that text alone. Log the status, response body, and account usage. HTTP 429 means too many requests in a period under RFC 6585, section 4. The RFC says a response may include Retry-After; it does not require it.

2. Pace and retry requests safely

  1. Inspect the HTTP status and provider error details.
  2. Reduce simultaneous calls or place incoming work in a queue with a rate limiter. The provider documents plan-dependent RPM limits; a client-side queue is a practical way to stay below your account’s cap.
  3. If Retry-After is present, wait the specified duration before trying again.
  4. If it is absent, wait before retrying and use bounded backoff with jitter. Avoid an immediate retry loop. ScreenshotAPI.net’s retrieved documentation does not specify a numeric backoff schedule, so choose and monitor a policy appropriate to your workload.
  5. After recovery, keep concurrency and retry attempts bounded so a transient 429 does not multiply traffic.

cURL: inspect the response

Save response headers and body separately so you can verify the status, any Retry-After header, and provider error details.

curl -sS -D response-headers.txt -o response-body.json \
  -w 'HTTP %{http_code}\n' \
  'https://screenshotapi.net/api/v1/screenshot?token=YOUR_API_KEY&url=https%3A%2F%2Fexample.com'

cat response-headers.txt
cat response-body.json

Use the endpoint and required parameters from your ScreenshotAPI.net account or current API documentation. The example shows the inspection pattern; it does not assert that every account uses this exact endpoint or parameter set.

Python: bounded retry honoring Retry-After

This example uses requests, checks the status, honors either form of standard Retry-After value, and otherwise uses capped exponential backoff with jitter. Set the endpoint and parameters to match your account. It retries 429 only; it does not retry a 403 monthly quota response.

import email.utils
import random
import time

import requests

ENDPOINT = "YOUR_SCREENSHOTAPI_ENDPOINT"
PARAMS = {"token": "YOUR_API_KEY", "url": "https://example.com"}
MAX_ATTEMPTS = 5
BASE_DELAY_SECONDS = 1.0
MAX_DELAY_SECONDS = 30.0


def retry_after_seconds(value):
    if not value:
        return None
    try:
        return max(0.0, float(value))
    except ValueError:
        try:
            date = email.utils.parsedate_to_datetime(value)
            return max(0.0, date.timestamp() - time.time())
        except (TypeError, ValueError, OverflowError):
            return None


for attempt in range(MAX_ATTEMPTS):
    response = requests.get(ENDPOINT, params=PARAMS, timeout=90)
    if response.status_code != 429:
        response.raise_for_status()
        with open("screenshot.png", "wb") as output:
            output.write(response.content)
        break

    if attempt == MAX_ATTEMPTS - 1:
        response.raise_for_status()

    server_delay = retry_after_seconds(response.headers.get("Retry-After"))
    if server_delay is not None:
        delay = server_delay
    else:
        ceiling = min(MAX_DELAY_SECONDS, BASE_DELAY_SECONDS * (2 ** attempt))
        delay = random.uniform(0.0, ceiling)
    time.sleep(delay)
else:
    raise RuntimeError("No screenshot response received")

For production use, add a total job deadline, structured logging, and a queue shared across worker processes. A per-process sleep does not coordinate multiple workers and may leave the aggregate rate above the account limit.

Node.js: bounded retry honoring Retry-After

import { setTimeout as sleep } from 'node:timers/promises';

const endpoint = 'YOUR_SCREENSHOTAPI_ENDPOINT';
const params = new URLSearchParams({
  token: 'YOUR_API_KEY',
  url: 'https://example.com',
});
const maxAttempts = 5;
const baseDelayMs = 1000;
const maxDelayMs = 30000;

function retryAfterMs(value) {
  if (!value) return null;
  const seconds = Number(value);
  if (Number.isFinite(seconds) && seconds >= 0) return seconds * 1000;
  const date = Date.parse(value);
  return Number.isNaN(date) ? null : Math.max(0, date - Date.now());
}

for (let attempt = 0; attempt < maxAttempts; attempt++) {
  const response = await fetch(`${endpoint}?${params}`);
  if (response.status !== 429) {
    if (!response.ok) {
      const details = await response.text();
      throw new Error(`Screenshot request failed: HTTP ${response.status}: ${details}`);
    }
    const image = Buffer.from(await response.arrayBuffer());
    await import('node:fs/promises').then(({ writeFile }) => writeFile('screenshot.png', image));
    break;
  }

  if (attempt === maxAttempts - 1) {
    throw new Error('Screenshot request remained rate limited after the retry limit');
  }
  const serverDelay = retryAfterMs(response.headers.get('retry-after'));
  const ceiling = Math.min(maxDelayMs, baseDelayMs * (2 ** attempt));
  const delay = serverDelay ?? Math.random() * ceiling;
  await sleep(delay);
}

Keep credentials out of source control and logs. If your provider requires a different authentication method or endpoint, adjust those request details while keeping the status and retry handling.

3. Prevent repeat 429s in production

  • Shape bursts: A queue smooths sudden batches so workers do not send the entire batch at once.
  • Limit aggregate concurrency: Coordinate the limiter across workers or services that share the same account. Independent worker limits can add up beyond the plan allowance.
  • Separate throttling from quota accounting: Track 429 and 403 counts separately, along with remaining monthly usage if available in your account.
  • Keep retries bounded: Stop after a small configured number of attempts or when the job deadline expires; route persistent failures for review.
  • Measure the actual traffic: Record request timestamps, status codes, attempt number, and wait duration. Compare peak requests per minute with your plan’s published limit.

ScreenshotAPI.net says failed screenshot attempts do not count toward plan usage limits, and cached screenshots count only when they are unique and fresh. Those are usage-accounting details; they do not establish that failed attempts are exempt from the RPM limit. Its Help page also describes usage email notifications at 80%, 90%, and 100% of monthly quota. Treat those notifications as quota monitoring, not as a substitute for request pacing.

4. Troubleshooting

Symptom Likely cause Fix
429 repeats immediately The client retries without pausing, or the aggregate request rate remains over the RPM cap. Honor Retry-After when present; otherwise apply delayed, bounded backoff. Reduce or coordinate worker concurrency.
screenshots_limit_reached appears The label is shared by the documented 429 RPM case and 403 monthly quota case. Check the actual HTTP status and account usage. Handle 429 with pacing; handle 403 by reviewing monthly allowance and plan.
Some workers still receive 429 after adding sleeps Each process may be pacing itself independently, while their combined rate exceeds the account limit. Use a shared queue or distributed rate limiter and account for all services using the same credentials.
No Retry-After header The header is optional in HTTP, and ScreenshotAPI.net’s reviewed materials do not promise it on every response. Use a bounded client-side delay with jitter; do not retry immediately.
403 continues after waiting Monthly quota may be exhausted; waiting a few seconds does not replenish it. Check account usage and plan details, then schedule work within the available allowance or review plan options.
429 occurs below a rate you expected to be allowed The published range depends on plan, and the retrieved Help page does not provide a full tier mapping. Other clients may also share the account. Verify your specific plan’s limit and account-wide traffic before raising the client rate.
Retries appear to hang A server-provided retry interval may be long, or a client may lack a total deadline. Set an overall job deadline and log chosen waits. If a valid Retry-After exceeds the deadline, defer the job rather than repeatedly retrying.

5. Reliability, throughput, and cost

Plan-dependent limits are stated as 20–80 requests per minute. That is a throughput ceiling, not a guarantee that a screenshot will complete within a particular duration. A queue improves reliability by absorbing bursts, but it cannot increase the account’s permitted sustained rate. Leave headroom for retries and traffic from other clients instead of configuring workers to run constantly at the published ceiling.

For cost planning, distinguish successful screenshot allowance from RPM. The provider says failed screenshot attempts do not count toward plan usage, while its terms make customers responsible for detecting and handling returned errors and reserve the right to limit or throttle calls for malicious activity or technical errors. Do not assume usage-accounting rules remove the RPM restriction. Check your account’s current plan, quota, and pricing before estimating a large job; the research available for this article does not establish a complete current plan price table or overage terms. ScreenshotAPI.net Terms.

6. Or skip the browser setup

ScreenshotNeo is a website screenshot API and MCP server. One GET request returns an image or PDF, without managing a browser worker. For example:

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

See the ScreenshotNeo API documentation for request options. Cookie and consent banners, newsletter popups, and chat widgets are removed before capture; each of those steps can be turned off. Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed, and responses identify page verdict and billing status. Its MCP server provides take_screenshot, get_page_info, and capture_pdf for Claude, Cursor, and other MCP clients. The free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 screenshots. All listed features are available on every plan.

Sign up for ScreenshotNeo’s free 1,000 screenshots per month, with no card required.

7. FAQ

Does ScreenshotAPI.net have a rate limit per second or minute?

Its Help page describes a limit of 20–80 requests per minute depending on subscription plan. Check your account for the applicable plan limit; the retrieved documentation does not include a complete tier-by-tier table.

Does ScreenshotAPI.net always send Retry-After?

The reviewed provider documentation does not say that it always sends the header. HTTP allows a 429 response to include it, but does not require it.

Will retrying a 403 fix a 429?

No. A 403 may indicate that the monthly screenshot quota is exhausted. Check status and usage first; rate pacing addresses an RPM 429, not depleted monthly allowance.

Do failed attempts count against usage?

ScreenshotAPI.net’s Help page says failed screenshot attempts do not count toward plan usage limits. That statement concerns plan usage accounting and does not say failed attempts bypass the RPM limit.

Sources: ScreenshotAPI.net Errors, ScreenshotAPI.net Help, ScreenshotAPI.net Terms, and RFC 6585 §4.