ScreenshotNeo

BlogHow-to

PagePeeker Screenshot API Times Out on Slow Websites: Troubleshooting

Separate your HTTP timeout from PagePeeker’s wait limit, inspect capture headers, and use readiness polling when a slow page should not hold a request open.

By the ScreenshotNeo team4 October 20268 min read

If the PagePeeker screenshot API appears to time out on a slow website, first distinguish your client’s HTTP deadline from PagePeeker’s server-side wait parameter. For premium accounts, wait sets the maximum number of seconds PagePeeker will wait before returning a generated thumbnail or a placeholder. Your client must allow enough time for that response to arrive. If a long open request is a poor fit, use PagePeeker’s documented refresh, readiness-check, and download flow instead.

There is no published universal capture timeout or recommended retry interval in the documentation. A larger client timeout alone cannot guarantee that a slow target will finish rendering. Check the response metadata, final redirect destination, account type, and cache behavior before changing timeouts.

1. Confirm the API version and endpoint

Use PagePeeker V2, which its documentation labels current and recommended. V1 is discontinued and redirects to V2. The documented endpoint pattern is http://{entrypoint}.pagepeeker.com/v2/thumbs.php. The free service uses free.pagepeeker.com; paid and unbranded accounts use api.pagepeeker.com. See the official V2 API documentation.

Encode the target page as a URL parameter. A URL with its own query string must be encoded as the value of url, rather than appended as though it were part of the PagePeeker request.

curl -G 'http://api.pagepeeker.com/v2/thumbs.php' \
  --data-urlencode 'size=m' \
  --data-urlencode 'code=YOUR_API_KEY' \
  --data-urlencode 'wait=20' \
  --data-urlencode 'url=https://example.com/products?region=us&sort=recent' \
  -D response-headers.txt \
  -o thumbnail.jpg

wait is optional and documented for premium accounts. Omit it on accounts that do not support it. The example’s 20 seconds is an illustrative request value, not a PagePeeker recommendation or guaranteed completion time. Confirm supported options and account settings with the documentation or your account.

2. Separate the two timeout clocks

There are two different deadlines to reason about:

Deadline What it controls What to check
Client HTTP timeout How long your application waits for the HTTP response before aborting its request. Set it longer than the server-side wait you request, with room for network transit and response handling. The margin is an application choice.
PagePeeker wait For premium accounts, the maximum server wait before returning an image or placeholder when a thumbnail is not already generated. Check account eligibility and whether the parameter is present and correctly encoded.
Target page load How long the remote site takes to respond and render sufficiently for capture. Inspect capture duration, errors, and the final URL where response headers are available.

PagePeeker describes wait as a maximum wait before it returns a generated image or placeholder. It is not a promise that every page will finish within that duration. The API documentation does not publish a universal hard capture limit or numeric retry/backoff values.

3. Inspect the response before changing your timeout

Paid and unbranded responses can include diagnostic headers. Preserve these when investigating a slow or unexpected result:

Header How it helps
X-PP-Capture-Time Reports how long capture took.
X-PP-Error Indicates whether thumbnail creation errored.
X-PP-Final-URL Shows the destination after redirects, which can expose an unexpected redirect or login page.
X-PP-Capture-Method Identifies the capture engine used.
X-PP-Hash Provides a capture identifier useful when investigating a reproducible problem.
X-PP-Timestamp Gives the screenshot capture time as a Unix timestamp.

For example, inspect the saved headers from the cURL request:

cat response-headers.txt

Header availability depends on account type. A placeholder after a wait limit is not, by itself, proof of a particular underlying failure. Use the available error metadata and readiness endpoint to distinguish an unfinished capture from an error where possible.

4. Use readiness polling for long captures

If your application should not hold one connection open while a page renders, PagePeeker documents a three-step flow: trigger a refresh, check readiness, then download the thumbnail once ready. The ready endpoint returns JSON with IsReady set to 1 when the image can be retrieved and Error set to 1 if thumbnail creation failed.

  1. Call thumbs.php with refresh=1 to request a fresh render.
  2. Call thumbs_ready.php for the same size and URL until it reports ready or an error.
  3. When IsReady is 1, fetch the thumbnail from thumbs.php without forcing another refresh.

Here is a runnable Python example using a bounded polling loop. The pause and overall deadline are application choices; PagePeeker does not publish a required interval or retry policy. Keep your API key on the server.

import time
import requests

BASE = "http://api.pagepeeker.com/v2"
API_KEY = "YOUR_API_KEY"
TARGET = "https://example.com/products?region=us&sort=recent"
SIZE = "m"

session = requests.Session()
common = {"size": SIZE, "code": API_KEY, "url": TARGET}

# Start a fresh render. This call triggers generation; it is not the ready check.
start = session.get(
    f"{BASE}/thumbs.php",
    params={**common, "refresh": 1},
    timeout=(5, 20),
)
start.raise_for_status()

# Poll within this application's chosen time budget.
deadline = time.monotonic() + 120
while time.monotonic() < deadline:
    ready = session.get(
        f"{BASE}/thumbs_ready.php",
        params=common,
        timeout=(5, 15),
    )
    ready.raise_for_status()
    state = ready.json()

    if str(state.get("Error")) == "1":
        raise RuntimeError("PagePeeker reports an error creating the thumbnail")
    if str(state.get("IsReady")) == "1":
        image = session.get(
            f"{BASE}/thumbs.php",
            params=common,
            timeout=(5, 30),
        )
        image.raise_for_status()
        with open("thumbnail.jpg", "wb") as output:
            output.write(image.content)
        break

    time.sleep(3)  # Example interval only; choose based on your request budget.
else:
    raise TimeoutError("Thumbnail was not ready before this application's deadline")

Each readiness check counts as an API call according to PagePeeker’s FAQ. Avoid tight polling loops: they spend quota while adding load, and no documented interval guarantees faster rendering.

5. Understand plan speed and cache context

PagePeeker publishes typical rendering speeds by plan, not a guarantee for a particular URL. Its current pages list free branded at 30–60 seconds, free unbranded and Basic at 10–20 seconds, Advanced at 5–15 seconds, and Premium at under 5 seconds. The free service and paid tiers also describe different caching windows. Treat these as vendor-published plan descriptions, not measurements of your target or your integration. See the free thumbnails details and pricing page.

A cached thumbnail can return differently from a fresh render. Use refresh=1 only when you intend to force regeneration and your account supports it; otherwise, fetching an existing cached thumbnail may be faster. PagePeeker counts displaying an existing thumbnail, creating a new one, and checking availability as API calls, so include polling and refreshes in usage estimates.

Pricing and plan descriptions can change. Check the official pricing page for current costs and account limits rather than building a cost estimate from old saved values.

6. Troubleshooting checklist

Symptom Likely check Action
Your client aborts before receiving a response The client timeout may be shorter than the configured wait, or network transit adds time. Compare both deadlines and allow an application-chosen margin. If the request should not remain open, switch to readiness polling.
wait seems ignored The docs identify it as premium-only, or the parameter may not reach the endpoint. Verify account eligibility, V2 endpoint, and query encoding. Do not assume the parameter is available on every account.
You receive a placeholder The image may not have completed within the requested server wait; a placeholder alone does not identify the cause. Check X-PP-Error if available, then query thumbs_ready.php. Do not map every placeholder to a specific failure without evidence.
Readiness stays false The target may still be rendering, the request parameters may differ between calls, or creation may have errored. Use identical size and URL parameters for refresh and readiness calls; check the JSON Error field and stop within your own budget.
The image is for the wrong page The target redirected to a different destination. Inspect X-PP-Final-URL when available and verify the submitted URL is encoded correctly.
Usage grows after adding retries Readiness checks are billable API calls under PagePeeker’s accounting description. Space polls, cap attempts, and include readiness checks and refreshes in quota planning.
Old or inconsistent results appear A cached image may be returned instead of a new render. Review the plan’s cache window; request refresh only when supported and when a new capture is actually needed.
API key appears in browser code Client-side exposure can let others consume the account quota. Make authenticated calls from your server and do not ship the key to browsers.

7. Reliability, performance, and cost practices

  • Measure the right stage. Record your client’s elapsed time, HTTP outcome, available PagePeeker headers, final URL, and readiness state. This helps separate a slow target render from your own short client deadline.
  • Bound the workflow. Give each request and the overall polling operation an application-level deadline. Stop polling when the deadline or request budget is reached.
  • Use polling deliberately. Readiness checks consume API calls, so choose a reasonable interval for your product and avoid immediate repeated requests.
  • Reuse cached work where suitable. For pages where freshness is not essential, avoid forcing regeneration on every request.
  • Keep credentials private. Store the API key on a server and avoid logging it alongside diagnostic data.
  • Escalate with evidence. For a reproducible slow URL, retain timestamps, capture time, final URL, capture method, hash, and error header when present. The official docs do not state universal retry values or a maximum duration.

8. Or skip the browser setup

ScreenshotNeo is a website screenshot API and MCP server. Its one-call API returns an image or PDF, and the API documentation covers its parameters.

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

Cookie banners, popups, and chat widgets are removed before the shot. Bot checks, blank pages, and failed loads are never billed. An MCP server lets AI agents use screenshots. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000.

Sign up free for 1,000 screenshots a month, with no card.

FAQ

Does raising my HTTP timeout make PagePeeker capture a slow page?

No. It only makes your client wait longer for a response. PagePeeker’s server-side wait behavior and the target’s rendering time are separate.

The documentation reviewed describes the refresh and readiness flow but does not specify a polling interval or retry/backoff policy. Choose one that fits your latency and API-call budget.

Does a slow response always mean the target website is slow?

No. Compare your measured client duration with X-PP-Capture-Time when available, and inspect the final URL and error metadata.

Can I put my PagePeeker key in frontend JavaScript?

PagePeeker warns that exposing the key can let third parties consume your quota. Keep it server-side.