---
title: "Stealth browser scraping for sites that block normal scrapers (Cloudflare, DataDome, bot detection). Use operation \"scrape\" for a one-call render of a single URL; create_context → create_page → cleanup remains for multi-step work that reuses one context. Operation \"configure\" only validates a stealthConfig and returns it with the defaults filled in — it stores nothing, so pass stealthConfig on each scrape or create_context call that should use it."
method: POST
path: "/tools/stealth_mode"
tags: ["tools"]
---

# Stealth browser scraping for sites that block normal scrapers (Cloudflare, DataDome, bot detection). Use operation "scrape" for a one-call render of a single URL; create_context → create_page → cleanup remains for multi-step work that reuses one context. Operation "configure" only validates a stealthConfig and returns it with the defaults filled in — it stores nothing, so pass stealthConfig on each scrape or create_context call that should use it.

`POST /tools/stealth_mode`

## Request

- Base URL: `https://www.crawlforge.dev/api/v1`
- URL: `https://www.crawlforge.dev/api/v1/tools/stealth_mode`
- Auth: API key in header `X-API-Key`

## Request body

- StealthModeRequest
  - `operation` 'scrape' | 'configure' | 'create_context' | 'create_page' | 'get_stats' | 'cleanup': Stealth operation to perform. "scrape" creates a context, navigates to url, returns the requested formats and tears the context down, all in one billed call. "configure" validates stealthConfig and returns it with defaults filled in; nothing is stored. "create_context" / "create_page": a context to open pages in. "get_stats": the backend's open contexts, browser and proxy state. "cleanup": close the browser and every context.
  - `stealthConfig` object: Stealth browser configuration. Applies to the call it is passed on and is not remembered. Chromium applies customUserAgent, customViewport, locale and timezone per call. On camoufox customUserAgent and customViewport are not applied, locale is fixed when the browser launches, and behind a proxy locale and timezone come from the exit IP; each of these is reported in `warnings`.
    - `level` 'basic' | 'medium' | 'advanced'
    - `randomizeFingerprint` boolean
    - `hideWebDriver` boolean
    - `blockWebRTC` boolean
    - `simulateHumanBehavior` boolean
    - `customUserAgent` string
    - `locale` string
    - `timezone` string
  - `engine` 'playwright' | 'camoufox': Browser engine (camoufox only where installed on the backend)
  - `contextId` string: Browser context ID for page operations (from a create_context call)
  - `urlToTest` string: URL to navigate to when creating a page
  - `url` string: URL to scrape. Required for operation "scrape"; the other operations use urlToTest instead.
  - `formats` unknown[]: Formats operation "scrape" returns: markdown, html, text, links, metadata, screenshot. A screenshot comes back as a crawlforge://screenshot/{id} resource URI, not inline base64 — this endpoint passes that URI through without resolving it, so for now screenshot is usable only from the CrawlForge MCP server.
    - unknown
  - `wait_for` number: Extra wait after page load, in ms (0-30000), for content that renders after DOMContentLoaded
  - `verbose` boolean: Return the full generated fingerprint from create_context instead of a summary
  - `respect_robots` boolean: Respect the target site's robots.txt. Omitted, the compliant default (true) applies: a URL disallowed for CrawlForge is refused before any browser is launched and no credits are charged, and every navigation is checked the same way. The rule is matched against the CrawlForge product token even though the stealth browser presents a randomized User-Agent, so a site owner's "User-agent: CrawlForge" directive binds stealth traffic too. Setting this to false is honoured, returns a warning in the response, and is recorded against your API key.
  - `max_inline_chars` number: Largest result returned inline, in characters of its JSON (1000-10000000; env CRAWLFORGE_MAX_INLINE_CHARS sets the default). Over it, the result is stored for 1 hour and the response carries a preview, a result_handle, total_chars and truncated: true; read the rest with read_result (1 credit).
  - `redact_pii` union: Remove personal data from the text this call returns, before it is stored or sent back. true is shorthand for { mode: "fast" }: every entity, tagged. As an object: entities (any of EMAIL, PHONE, FINANCIAL, SECRET; omitted or empty means all four, and any other name is a 400 rather than a silent no-op), replace_style ("tag" → <EMAIL>, "mask" → [REDACTED], "remove" → nothing; default "tag") and mode ("fast", the default, is regex-only and costs no extra credits; "model" covers PERSON and LOCATION, needs an LLM and is rejected here — use the CrawlForge MCP server). The response carries redaction: { entities, count, mode } inside data, saying what was removed. Detection is deliberately conservative: a card number must pass Luhn and an IBAN mod-97, so a false positive cannot silently destroy real page content. URLs, queries and identifiers the response uses to name what was fetched are left intact, and counters derived from the text (content_length, word_count, character_count) describe the text as it was extracted, before redaction.
    - boolean
    - object
      - `entities` string[]
      - `replace_style` 'tag' | 'mask' | 'remove'
      - `mode` 'fast' | 'model'

## Response `200`

The tool's result; 5 credits charged (see x-credits-note and x-extra for add-ons).

- ToolSuccess: The envelope every tool returns on success. Credits are deducted only after the tool succeeded, so credits_used is the price of this call and an error response never charges.
  - `success` true, required
  - `data` object, required: The tool's result. Its shape is tool-specific and documented on the tool's docs page (x-docs-url on the operation).
  - `credits_used` integer, required: Credits charged for this call: the operation's x-credits, plus any add-on x-credits-note or x-extra describes.
  - `credits_remaining` integer, required: The account's credit balance after this call.
  - `processing_time` integer, required: Server-side processing time in milliseconds.
  - `warnings` string[]: Non-fatal notes: a robots.txt override that was recorded, a format that could not be produced, a redaction that changed the payload.

## Other responses

- `400`: VALIDATION_ERROR: the body does not match the request schema.
- `401`: MISSING_API_KEY or INVALID_API_KEY.
- `402`: INSUFFICIENT_CREDITS: the balance does not cover the tool's base price. X-Auto-Recharge-Triggered says whether an automatic recharge was started; retry once it lands.
- `429`: RATE_LIMIT_EXCEEDED: your plan's rate limit. Retry-After: 60.
- `500`: TOOL_ERROR, INTERNAL_ERROR or CREDIT_DEDUCTION_FAILED.
- `default`: Any other error. Which of 403, 404, 413, 422, 501, 502, 503 and 504 a tool can return depends on what it does (fetching a page, calling the execution backend, reading stored results); the code vocabulary is listed on the ErrorResponse schema.

---

[API](https://skmtc.dev/crawlforge/apis/crawlforge-rest-api.md) · [All operations](https://skmtc.dev/crawlforge/apis/crawlforge-rest-api/llms.txt) · [OpenAPI document](https://skmtc.dev/crawlforge/apis/crawlforge-rest-api/revisions/a5532fa8d5d5?raw)
