Allscreenshots
Features

Stealth mode

Capture sites protected by Cloudflare, DataDome, and other bot detection

Stealth mode

Capture screenshots of sites that block ordinary automated browsers — pages behind Cloudflare, DataDome, PerimeterX, and similar bot protection that normally return a challenge page, an "access denied" wall, or a captcha instead of the content you asked for.

Stealth mode is included on Pro and up — and available on Starter for a limited time as an introduction offer (up to 100 stealth captures a month). Every capture is slower and more resource-intensive than a standard one, so turn it on only for sites that block standard captures — leave it off everywhere else.

When to use it

Reach for stealth mode when a normal capture comes back as something other than the page you wanted:

  • A Cloudflare interstitial — "Checking your browser before you continue…"
  • A DataDome, PerimeterX, or similar bot-protection block page
  • An "Access denied" / "You don't have permission" wall
  • A captcha (reCAPTCHA, hCaptcha, Turnstile)
  • A near-empty or stripped-down page on a site that loads fine in your own browser

Those are signs the site is rejecting the request based on the client, not a problem with your parameters. If a standard capture already returns the right page, you don't need stealth mode — and shouldn't pay for it.

Enabling it

Add stealthMode: true to any capture request:

{
  "url": "https://heavily-protected-site.com",
  "stealthMode": true
}
curl -X POST 'https://api.allscreenshots.com/v1/screenshots' \
  -H 'X-API-Key: YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{
    "url": "https://heavily-protected-site.com",
    "stealthMode": true
  }' --output capture.png

The flag works the same way on the async, bulk, and scheduled endpoints — set stealthMode: true in the options.

How it works

When stealthMode is on, the capture runs through a path built to look like a real person on a real browser rather than an automated one. At a high level it:

  • Routes the request through residential IP addresses, so it doesn't arrive from an obvious datacenter range.
  • Presents a hardened, realistic browser fingerprint.
  • Interacts with the page the way a person would before capturing.
  • Verifies it captured the real page, not a block, and recovers on its own when a site pushes back — so you don't get a screenshot of a challenge wall.

That's all you need to manage — the single stealthMode flag. We intentionally don't publish the exact techniques: detection vendors adapt to anything documented, so the specifics stay private and change over time.

What to expect

  • It's slower. A stealth capture does more work and may retry, so it takes longer than a standard one. Give it a generous timeout.
  • It's not a guarantee. Stealth mode dramatically raises the success rate on protected sites, but a site sitting behind an unsolved interactive captcha can still refuse the capture. When that happens you get a clear error (below) rather than a screenshot of the wall.
  • Ad and cookie blocking still apply, so a successful stealth capture is just as clean as a standard one. See Blocking.

Errors

If a stealth capture can't succeed, the API returns a specific error instead of a screenshot of a block page:

StatusCodeMeaningWhat to do
403CAPABILITY_NOT_AVAILABLEYour plan doesn't include stealth modeUpgrade to Pro (or grab the limited-time Starter offer), or drop stealthMode
402STEALTH_PROMO_LIMIT_REACHEDYou've used this month's Starter intro-offer stealth allowanceUpgrade to Pro for unlimited stealth, or wait for next month's reset
422TARGET_BLOCKEDThe site served a bot-protection block or captcha that we couldn't get pastRetry later; some sites can't be captured
422STEALTH_CAPTURE_FAILEDThe stealth capture failed for another reasonCheck the reason in the response; retry
502STEALTH_UNAVAILABLEStealth capture is temporarily unavailableRetry shortly
503SERVICE_BUSYWorkers are busyRetry after a short delay

TARGET_BLOCKED means the page was a wall, not your screenshot. It's worth a retry — protected sites are inconsistent — but treat repeated blocks on the same URL as "this site can't be captured right now" rather than a transient error.

Best practices

Use it selectively

Stealth mode is the exception, not the default. Capture normally first, and switch on stealthMode only for the specific URLs that come back blocked. Sending every request through stealth mode is slower and burns the Pro feature on sites that never needed it.

Give it more time

Because stealth captures can retry, raise the timeout for protected sites:

{
  "url": "https://heavily-protected-site.com",
  "stealthMode": true,
  "timeout": 45000,
  "waitUntil": "networkidle"
}

Handle blocks in code

Treat 422 and 503 as retryable. A small backoff and one or two retries clears most transient blocks:

async function captureWithStealth(url, attempts = 3) {
  for (let i = 0; i < attempts; i++) {
    const res = await fetch('https://api.allscreenshots.com/v1/screenshots', {
      method: 'POST',
      headers: { 'X-API-Key': process.env.API_KEY, 'Content-Type': 'application/json' },
      body: JSON.stringify({ url, stealthMode: true, timeout: 45000 }),
    });
    if (res.ok) return Buffer.from(await res.arrayBuffer());
    if (res.status === 422 || res.status === 503) {
      await new Promise((r) => setTimeout(r, 2000 * (i + 1)));
      continue;
    }
    throw new Error(`Capture failed: ${res.status}`);
  }
  throw new Error('Site stayed blocked after retries');
}

Examples

Cloudflare-protected page

curl -X POST 'https://api.allscreenshots.com/v1/screenshots' \
  -H 'X-API-Key: YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{
    "url": "https://example.com/protected",
    "stealthMode": true,
    "fullPage": true,
    "timeout": 45000
  }' --output protected.png

Wait for content after the challenge clears

curl -X POST 'https://api.allscreenshots.com/v1/screenshots' \
  -H 'X-API-Key: YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{
    "url": "https://example.com/listings",
    "stealthMode": true,
    "waitFor": ".results-loaded",
    "waitUntil": "networkidle",
    "timeout": 50000
  }' --output listings.png

Scheduled stealth capture

curl -X POST 'https://api.allscreenshots.com/v1/schedules' \
  -H 'X-API-Key: YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{
    "name": "Protected competitor page",
    "url": "https://competitor.com/pricing",
    "schedule": "daily",
    "options": { "stealthMode": true, "fullPage": true }
  }'

On this page