> ## Documentation Index
> Fetch the complete documentation index at: https://webscrape.ai/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Stealth mode

> When to set stealth: true, how it works, and what it costs.

The default fetcher is fast and cheap, and works on most public sites. **Stealth mode** swaps it for a real Chrome session — the right tool when a site throws challenges, returns empty shells, or sits behind a bot wall.

## How to enable

Add `"stealth": true` to the request body of any endpoint that supports it:

```json theme={null}
{
  "website_url": "https://example.com",
  "stealth": true
}
```

Endpoints that accept `stealth`:

* `POST /v1/scrape`
* `POST /v1/smartscraper`

`POST /v1/smartbrowse/recipes/:id/run` already runs in real Chrome, so stealth is built in.

## When to use it

<CardGroup cols={2}>
  <Card title="Use stealth when…" icon="circle-check" iconType="solid">
    * You're getting 403 / 429 / "Just a moment…" challenge pages
    * The page is client-rendered and the default fetch returns an empty shell
    * You need to get past a CAPTCHA or challenge interstitial
  </Card>

  <Card title="Skip stealth when…" icon="circle-xmark" iconType="solid">
    * The default fetch already returns what you need (most marketing, news, listing, and product pages)
    * You're hitting a JSON API or RSS feed
    * You're at high volume and the default fetcher is working
  </Card>
</CardGroup>

## What it costs

Stealth adds a per-endpoint surcharge **on top of** the base cost — see [Credits](/docs/concepts/credits) for the full table. Most endpoints roughly double in cost with stealth on.

## Try the cheap path first

A common pattern: try without stealth, retry with it on failure. Because the first call is free when it fails, the fallback only spends the surcharge when stealth was needed.

<CodeGroup>
  ```python Python theme={null}
  import requests

  API = "https://api.webscrape.ai/v1/scrape"
  HDR = {"X-API-Key": "wsg_live_..."}

  def fetch(url):
      env = requests.post(API, headers=HDR, json={"website_url": url}).json()
      if env.get("status") != "completed":
          env = requests.post(API, headers=HDR, json={"website_url": url, "stealth": True}).json()
      return env
  ```

  ```js Node theme={null}
  const API = "https://api.webscrape.ai/v1/scrape";
  const HDR = { "X-API-Key": "wsg_live_...", "Content-Type": "application/json" };

  async function fetchUrl(url) {
    let env = await fetch(API, {
      method: "POST",
      headers: HDR,
      body: JSON.stringify({ website_url: url }),
    }).then(r => r.json());

    if (env.status !== "completed") {
      env = await fetch(API, {
        method: "POST",
        headers: HDR,
        body: JSON.stringify({ website_url: url, stealth: true }),
      }).then(r => r.json());
    }
    return env;
  }
  ```

  ```go Go theme={null}
  func fetchURL(url string) map[string]any {
      post := func(stealth bool) map[string]any {
          body, _ := json.Marshal(map[string]any{"website_url": url, "stealth": stealth})
          req, _ := http.NewRequest("POST", "https://api.webscrape.ai/v1/scrape", bytes.NewReader(body))
          req.Header.Set("X-API-Key", "wsg_live_...")
          req.Header.Set("Content-Type", "application/json")
          resp, _ := http.DefaultClient.Do(req)
          defer resp.Body.Close()
          var env map[string]any
          json.NewDecoder(resp.Body).Decode(&env)
          return env
      }
      env := post(false)
      if env["status"] != "completed" {
          env = post(true)
      }
      return env
  }
  ```

  ```rust Rust theme={null}
  async fn fetch_url(url: &str) -> Result<serde_json::Value, reqwest::Error> {
      let client = reqwest::Client::new();
      let post = |stealth: bool| {
          client
              .post("https://api.webscrape.ai/v1/scrape")
              .header("X-API-Key", "wsg_live_...")
              .json(&serde_json::json!({ "website_url": url, "stealth": stealth }))
              .send()
      };
      let env: serde_json::Value = post(false).await?.json().await?;
      if env["status"] != "completed" {
          return Ok(post(true).await?.json().await?);
      }
      Ok(env)
  }
  ```
</CodeGroup>
