> ## 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.

# Introduction

> webscrape.ai API reference (v1).

Every endpoint shares a common base URL, auth header, and response envelope. Always check `status` first, then read `data` (success) or `error` (failure).

## Features

<CardGroup cols={3}>
  <Card title="SmartScraper" icon="brain" href="/docs/api-reference/smartscraper">
    Structured JSON from any URL.
  </Card>

  <Card title="Scrape" icon="globe" href="/docs/api-reference/scrape">
    Raw HTML, cleaned markdown, or PDF, with optional stealth.
  </Card>

  <Card title="SmartBrowse" icon="hand-pointer" href="/docs/api-reference/smartbrowse/dispatch">
    Replay clicks, typing, and pagination on real Chrome.
  </Card>
</CardGroup>

## Base URL

All endpoints are served from a single base URL:

```
https://api.webscrape.ai/v1
```

## Authentication

Every request needs your API key in the `X-API-Key` header. Generate one from [the dashboard](https://webscrape.ai/app/api-keys) — keys look like `wsg_live_<32 base62 chars>`.

<CodeGroup>
  ```bash cURL theme={null}
  curl https://api.webscrape.ai/v1/scrape \
    -H "X-API-Key: wsg_live_..." \
    -H "Content-Type: application/json" \
    -d '{"website_url": "https://example.com"}'
  ```

  ```python Python theme={null}
  import requests

  requests.post(
      "https://api.webscrape.ai/v1/scrape",
      headers={"X-API-Key": "wsg_live_..."},
      json={"website_url": "https://example.com"},
  )
  ```

  ```js Node theme={null}
  await fetch("https://api.webscrape.ai/v1/scrape", {
    method: "POST",
    headers: { "X-API-Key": "wsg_live_...", "Content-Type": "application/json" },
    body: JSON.stringify({ website_url: "https://example.com" }),
  });
  ```

  ```go Go theme={null}
  package main

  import (
      "bytes"
      "net/http"
  )

  func main() {
      body := bytes.NewBufferString(`{"website_url":"https://example.com"}`)
      req, _ := http.NewRequest("POST", "https://api.webscrape.ai/v1/scrape", body)
      req.Header.Set("X-API-Key", "wsg_live_...")
      req.Header.Set("Content-Type", "application/json")
      http.DefaultClient.Do(req)
  }
  ```

  ```rust Rust theme={null}
  use serde_json::json;

  #[tokio::main]
  async fn main() -> Result<(), Box<dyn std::error::Error>> {
      reqwest::Client::new()
          .post("https://api.webscrape.ai/v1/scrape")
          .header("X-API-Key", "wsg_live_...")
          .json(&json!({ "website_url": "https://example.com" }))
          .send()
          .await?;
      Ok(())
  }
  ```
</CodeGroup>

See [Authentication](/docs/concepts/authentication) for rotation, revocation, and the full error table.

## Response envelope

Every response has a top-level `status` field. Check it first, then read `data` (success or queued) or `error` (failure).

<CardGroup cols={3}>
  <Card title="completed" icon="circle-check">
    Synchronous success. Read `data`, `credits_used`, `credits_remaining`.
  </Card>

  <Card title="queued" icon="hourglass-half">
    Async dispatch (SmartBrowse). Read `data.run_id` and poll.
  </Card>

  <Card title="error" icon="circle-exclamation">
    Branch on `error.code` (stable). Log `error.message`, which can change.
  </Card>
</CardGroup>

<CodeGroup>
  ```json Synchronous success theme={null}
  {
    "status": "completed",
    "data": { "...": "..." },
    "credits_used": 5,
    "credits_remaining": 495,
    "request_id": "req_aB3xY9Kp"
  }
  ```

  ```json Async dispatch theme={null}
  {
    "status": "queued",
    "data": {
      "run_id": "k7Xb9dRmQ2p",
      "recipe_id": "m3Yc2tFvN8q",
      "run_status": "running",
      "poll_url": "/v1/smartbrowse/runs/k7Xb9dRmQ2p",
      "created_at": "2026-05-21T17:42:01Z"
    },
    "request_id": "req_aB3xY9Kp"
  }
  ```

  ```json Error theme={null}
  {
    "status": "error",
    "error": {
      "code": "validation_failed",
      "message": "Output did not match the requested schema after one repair attempt.",
      "details": { "...": "optional structured context" }
    },
    "request_id": "req_aB3xY9Kp"
  }
  ```
</CodeGroup>

Every response also returns the `request_id` as the `X-Request-ID` response header. Include it when you contact support.

## Status codes

| Code                       | When                               | `error.code`                                           |
| -------------------------- | ---------------------------------- | ------------------------------------------------------ |
| `200 OK`                   | Synchronous success                | n/a                                                    |
| `202 Accepted`             | Async dispatch (SmartBrowse)       | n/a                                                    |
| `400 Bad Request`          | Malformed request                  | `invalid_request`                                      |
| `401 Unauthorized`         | Missing or revoked API key         | `unauthorized`                                         |
| `402 Payment Required`     | Out of credits or unverified email | `insufficient_credits` / `email_verification_required` |
| `403 Forbidden`            | Authenticated but not allowed      | `forbidden`                                            |
| `404 Not Found`            | Resource doesn't exist             | `not_found`                                            |
| `409 Conflict`             | Resource state disallows action    | `conflict` / `account_deletion_pending`                |
| `422 Unprocessable Entity` | Schema validation failed           | `validation_failed`                                    |
| `429 Too Many Requests`    | Rate limited. Back off             | `rate_limited`                                         |
| `500 / 502`                | Internal failure. Safe to retry    | `internal_error` / `service_unavailable`               |

See [Errors](/docs/concepts/errors) for the full retry policy. Failed requests cost **0 credits**.

## Conventions

* **Headers**: `Content-Type: application/json` on every request with a body. `X-API-Key: wsg_live_...` for auth.
* **Timestamps**: ISO 8601 UTC, e.g. `2026-05-21T17:42:01Z`.
* **IDs**: SmartBrowse runs and recipes use opaque string IDs (\~11 base62 chars, e.g. `k7Xb9dRmQ2p`). Treat them as opaque — don't parse them, sort them, or assume a fixed length.
* **Async results**: dispatch with `POST`, poll the matching `GET`, or subscribe via [webhooks](https://webscrape.ai/app/settings).
* **Stealth mode**: a per-request flag (`"stealth": true`) on the endpoints that support it. See [Stealth mode](/docs/concepts/stealth-mode).

## OpenAPI spec

The full spec lives at [`/openapi.yaml`](/docs/openapi.yaml). Drop it into Postman, OpenAPI Generator, or anything else that speaks OpenAPI 3.1.
