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

# Browser Rendering

> Fetch with full JavaScript rendering. Returns cookies, screenshots, and supports custom JS evaluation.

## Overview

The `/browser` endpoint fetches a URL with full JavaScript rendering. Unlike `/fetch` (which returns the raw HTTP response), `/browser` waits for dynamic content and returns the fully rendered page.

**When to use `/browser` instead of `/fetch`:**

* The page requires JavaScript to load content (SPAs, dynamic sites)
* You need all cookies, including those set by client-side scripts
* You want to execute custom JavaScript to extract data
* You need a screenshot of the rendered page

The response format is the same as `/fetch` (`meta`, `raw`, `raw_json`), with additional fields for cookies, screenshots, and JS evaluation results.

## Endpoint

```
GET https://scraping-api.55-tech.com/browser
```

Works just like `/fetch` — all parameters are passed via headers.

## Request headers

| Header              | Required | Default | Description                                                                                 |
| ------------------- | -------- | ------- | ------------------------------------------------------------------------------------------- |
| `X-API-Key`         | Yes      | —       | Your API key                                                                                |
| `X-Target-URL`      | Yes      | —       | Target URL to render                                                                        |
| `X-Wait-Strategy`   | No       | `load`  | `load`, `networkidle`, or `selector` ([details](#wait-strategies))                          |
| `X-Wait-Selector`   | No       | —       | CSS selector to wait for (with `selector` strategy)                                         |
| `X-Timeout`         | No       | `30`    | Timeout in seconds (max 300 for `/browser`)                                                 |
| `X-JS-Expression`   | No       | —       | JavaScript to evaluate after the page is ready                                              |
| `X-Screenshot`      | No       | `false` | Capture screenshot (`1` or `true`)                                                          |
| `X-Expect-Selector` | No       | —       | CSS selector that must exist — retries on a different node if missing                       |
| `X-Expect-Contains` | No       | —       | Substring that must exist in the body — retries if missing                                  |
| `X-Proxy`           | No       | —       | Route through a proxy (`http://` or `socks5://`)                                            |
| `X-Steps`           | No       | —       | JSON array of sequential browser actions ([details](#steps))                                |
| `X-Block-Resources` | No       | —       | Comma-separated resource types to block: `image,font,stylesheet,media`. No default blocking |
| `X-Geo`             | No       | —       | Country filter for node selection (e.g. `US`, `DE,AT`)                                      |
| `X-Agent`           | No       | —       | Pin to a specific node (e.g. `de1`, `us3`)                                                  |
| `Cookie`            | No       | —       | Cookies to inject (`name=value; name2=value2`)                                              |
| `X-Cookies`         | No       | —       | Cookies as JSON array for full control ([format](#cookies))                                 |

### Wait strategies

| Strategy      | Description                                                                         |
| ------------- | ----------------------------------------------------------------------------------- |
| `load`        | Wait for `DOMContentLoaded` event (fastest, works for server-rendered pages)        |
| `networkidle` | Wait until there are no more than 2 network connections for 500ms (best for SPAs)   |
| `selector`    | Wait for `X-Wait-Selector` to appear in the DOM (most precise for specific content) |

### Cookies

**Simple cookies** — use the standard `Cookie` header:

```
Cookie: session=abc123; token=xyz
```

**Full cookie objects** — use the `X-Cookies` header with a JSON array when you need domain, path, or httpOnly control:

```
X-Cookies: [{"name":"session","value":"abc","domain":".example.com","secure":true}]
```

Cookie object fields: `name`, `value`, `domain`, `path`, `secure`, `httpOnly`, `sameSite`, `expires`.

### Steps

Execute sequential browser actions after the page loads — login flows, multi-page navigation, form filling, clicking through to specific content.

Pass a JSON array via the `X-Steps` header:

```
X-Steps: [{"action":"type","selector":"#email","value":"user@example.com"},{"action":"click","selector":"#submit"},{"action":"waitNavigation"}]
```

**Available actions:**

| Action           | Params                       | Description                                     |
| ---------------- | ---------------------------- | ----------------------------------------------- |
| `navigate`       | `url`, `waitUntil`           | Navigate to a new URL                           |
| `click`          | `selector`                   | Click an element (human-like mouse movement)    |
| `type`           | `selector`, `value`, `delay` | Type text into an input (auto-clears first)     |
| `clear`          | `selector`                   | Clear an input field                            |
| `select`         | `selector`, `value`          | Select a dropdown option by value               |
| `wait`           | `selector`, `timeout`        | Wait for an element to appear                   |
| `waitNavigation` | `waitUntil`                  | Wait for page navigation to complete            |
| `waitHidden`     | `selector`                   | Wait for an element to disappear                |
| `scroll`         | `selector` or `x`, `y`       | Scroll to an element or by pixels               |
| `hover`          | `selector`                   | Hover over an element                           |
| `press`          | `key`                        | Press a keyboard key (`Enter`, `Tab`, `Escape`) |
| `focus`          | `selector`                   | Focus an element                                |
| `evaluate`       | `expression`                 | Run JavaScript in the page                      |
| `screenshot`     | `fullPage`                   | Capture a screenshot (returned in step result)  |
| `sleep`          | `ms`                         | Wait a fixed number of milliseconds             |

Each step runs after the previous completes. Add `"continueOnError": true` to a step to keep going if it fails.

**Example: Login → navigate → capture**

```bash theme={null}
curl -H "X-API-Key: YOUR_API_KEY" \
  -H "X-Target-URL: https://example.com/login" \
  -H 'X-Steps: [
    {"action": "wait", "selector": "#login-form"},
    {"action": "type", "selector": "#email", "value": "user@example.com"},
    {"action": "type", "selector": "#password", "value": "secret"},
    {"action": "click", "selector": "#submit"},
    {"action": "waitNavigation"},
    {"action": "navigate", "url": "https://example.com/dashboard"},
    {"action": "wait", "selector": ".data-table"}
  ]' \
  -H "X-Timeout: 120" \
  https://scraping-api.55-tech.com/browser
```

**Example: Login then stream live data**

```bash theme={null}
curl -N -H "X-API-Key: YOUR_API_KEY" \
  -H "X-Target-URL: https://example.com/login" \
  -H "X-Capture: ws,network" \
  -H 'X-Steps: [
    {"action": "type", "selector": "#email", "value": "user@example.com"},
    {"action": "type", "selector": "#password", "value": "secret"},
    {"action": "click", "selector": "#submit"},
    {"action": "waitNavigation"},
    {"action": "navigate", "url": "https://example.com/live-feed"},
    {"action": "wait", "selector": ".feed-container"}
  ]' \
  https://scraping-api.55-tech.com/browser/stream
```

Steps work on both `/browser` (capture after all steps complete) and `/browser/stream` (stream during and after steps — step results arrive as `step_ok` / `step_error` events).

### Resource blocking

Block specific resource types to speed up rendering:

```
X-Block-Resources: image,font,stylesheet
```

Blocking images and fonts can reduce render time by 50%+ on media-heavy pages.

## Response

```json theme={null}
{
  "meta": {
    "status": 200,
    "final_url": "https://example.com/",
    "http_version": "",
    "elapsed_ms": 3200,
    "blocked": false,
    "headers": { "content-type": "text/html; charset=utf-8" },
    "agent": { "id": "scraping-de5" },
    "bytes": 45210
  },
  "raw": "<!DOCTYPE html><html>...</html>",
  "raw_json": null,
  "cookies": [
    {
      "name": "session_id",
      "value": "a1b2c3...",
      "domain": ".example.com",
      "path": "/",
      "secure": true,
      "httpOnly": true,
      "sameSite": "Lax",
      "expires": 1735689600
    }
  ],
  "screenshot": null,
  "js_result": null
}
```

| Field        | Description                                                               |
| ------------ | ------------------------------------------------------------------------- |
| `meta`       | Same as `/fetch` — status, final URL, headers, timing, node ID            |
| `raw`        | Rendered page body as text (HTML or other). `null` if body was valid JSON |
| `raw_json`   | Parsed JSON object if the response was valid JSON, otherwise `null`       |
| `cookies`    | All cookies set during rendering, including `httpOnly` cookies            |
| `screenshot` | Base64-encoded PNG of the full page (`null` if not requested)             |
| `js_result`  | Return value of `X-JS-Expression` (`null` if not provided)                |

### Response validation

Use `X-Expect-Selector` and `X-Expect-Contains` to verify the rendered page has the content you expect. If validation fails, the API automatically retries on a different node before returning an error.

## Examples

### Render a JavaScript-heavy page

```bash theme={null}
curl -H "X-API-Key: YOUR_API_KEY" \
  -H "X-Target-URL: https://example.com" \
  -H "X-Wait-Strategy: networkidle" \
  https://scraping-api.55-tech.com/browser
```

### Wait for specific content

```bash theme={null}
curl -H "X-API-Key: YOUR_API_KEY" \
  -H "X-Target-URL: https://example.com/dashboard" \
  -H "X-Wait-Strategy: selector" \
  -H "X-Wait-Selector: #data-table" \
  -H "X-Timeout: 45" \
  https://scraping-api.55-tech.com/browser
```

### Extract data with JavaScript

```bash theme={null}
curl -H "X-API-Key: YOUR_API_KEY" \
  -H "X-Target-URL: https://example.com" \
  -H "X-Wait-Strategy: networkidle" \
  -H "X-JS-Expression: JSON.stringify({title: document.title, links: document.querySelectorAll('a').length})" \
  https://scraping-api.55-tech.com/browser
```

The `js_result` field in the response contains the return value:

```json theme={null}
{
  "js_result": "{\"title\":\"Example\",\"links\":42}"
}
```

### Capture a screenshot

```bash theme={null}
curl -H "X-API-Key: YOUR_API_KEY" \
  -H "X-Target-URL: https://example.com" \
  -H "X-Screenshot: 1" \
  -H "X-Block-Resources: image,font" \
  https://scraping-api.55-tech.com/browser
```

### With cookies

```bash theme={null}
curl -H "X-API-Key: YOUR_API_KEY" \
  -H "X-Target-URL: https://example.com" \
  -H "Cookie: session=abc123; token=xyz" \
  -H "X-Wait-Strategy: networkidle" \
  https://scraping-api.55-tech.com/browser
```

### Route through a proxy

```bash theme={null}
curl -H "X-API-Key: YOUR_API_KEY" \
  -H "X-Target-URL: https://geo-restricted-site.com" \
  -H "X-Proxy: http://user:pass@proxy.example.com:8080" \
  -H "X-Geo: US" \
  https://scraping-api.55-tech.com/browser
```

### Validate response content

If the page doesn't contain the expected content, the API retries on a different node:

```bash theme={null}
curl -H "X-API-Key: YOUR_API_KEY" \
  -H "X-Target-URL: https://example.com/products" \
  -H "X-Wait-Strategy: selector" \
  -H "X-Wait-Selector: .product-list" \
  -H "X-Expect-Selector: .product-list" \
  -H "X-Expect-Contains: price" \
  https://scraping-api.55-tech.com/browser
```

### Python

```python theme={null}
import requests

resp = requests.get("https://scraping-api.55-tech.com/browser", headers={
    "X-API-Key": "YOUR_API_KEY",
    "X-Target-URL": "https://example.com",
    "X-Wait-Strategy": "networkidle",
    "X-JS-Expression": "document.title",
    "X-Block-Resources": "image,font",
})

data = resp.json()
print(data["meta"]["status"])       # 200
print(data["raw"][:200])            # rendered HTML
print(data["js_result"])            # "Example Domain"

for c in data["cookies"]:
    print(f"{c['name']}={c['value']} (httpOnly={c['httpOnly']})")
```

### JavaScript

```javascript theme={null}
const resp = await fetch("https://scraping-api.55-tech.com/browser", {
  headers: {
    "X-API-Key": "YOUR_API_KEY",
    "X-Target-URL": "https://example.com",
    "X-Wait-Strategy": "networkidle",
    "X-JS-Expression": "document.title",
  },
});

const data = await resp.json();
console.log(data.meta.status);       // 200
console.log(data.raw.slice(0, 200)); // rendered HTML
console.log(data.js_result);         // "Example Domain"
console.log(data.cookies.length);    // number of cookies captured
```

***

## Browser Stream (SSE)

For live, long-running sessions, use `/browser/stream`. Instead of capturing a single snapshot, the browser stays open and streams events in real-time via Server-Sent Events.

Only data-carrying requests (XHR/Fetch API calls) are captured — full HTML pages and JavaScript bundles are filtered out. No resources are blocked by default so pages load fully and widgets initialize correctly. Default wait strategy is `networkidle`. All data is gzip-compressed.

**Use cases:**

* Capture WebSocket frames that the page receives (live data feeds)
* Monitor XHR/Fetch API calls the page makes (the actual data endpoints)
* Watch DOM elements for changes (price updates, content changes)

### Endpoint

```
GET https://scraping-api.55-tech.com/browser/stream
```

### Headers

Same headers as `/browser`, with different defaults: `X-Wait-Strategy` defaults to `networkidle`, `X-Timeout` defaults to `0` (runs until disconnect, 24h safety cap), no resource blocking. Plus:

| Header             | Default              | Description                                                                     |
| ------------------ | -------------------- | ------------------------------------------------------------------------------- |
| `X-Capture`        | `network,ws,console` | Comma-separated event types to stream                                           |
| `X-DOM-Selector`   | —                    | CSS selector to watch for DOM mutations (requires `dom` in capture)             |
| `X-Network-Filter` | —                    | Regex filter for network URLs (only matching are streamed)                      |
| `X-WS-Filter`      | —                    | Regex filter for WebSocket URLs (only matching are streamed)                    |
| `X-JS-After-Load`  | —                    | JavaScript to execute after page load (e.g. click a button, trigger navigation) |

### SSE events

| Event        | Description                                                              |
| ------------ | ------------------------------------------------------------------------ |
| `connected`  | Browser loaded, streaming started (includes cookies)                     |
| `network`    | HTTP response captured (XHR/Fetch) — includes body, status, content-type |
| `ws_open`    | Page opened a WebSocket connection                                       |
| `ws_message` | WebSocket frame received by the page                                     |
| `ws_close`   | Page WebSocket closed                                                    |
| `dom`        | DOM mutation on watched selector                                         |
| `console`    | Console output (warn/error)                                              |
| `error`      | Error, stream ends                                                       |
| `done`       | Session ended (timeout, if set, or client disconnect)                    |

### Example: Capture page WebSocket feed

<CodeGroup>
  ```bash Request theme={null}
  curl -N -H "X-API-Key: YOUR_API_KEY" \
    -H "X-Target-URL: https://example.com/live" \
    -H "X-Capture: ws" \
    -H "X-Timeout: 120" \
    https://scraping-api.55-tech.com/browser/stream
  ```

  ```text SSE Response Stream theme={null}
  event: connected
  data: {"type":"connected","url":"https://example.com/live","agent_id":"scraping-us5","timestamp":1775511253069}

  event: ws_open
  data: {"type":"ws_open","url":"wss://feed.example.com/ws","ws_id":"req-1234","timestamp":1775511254100}

  event: ws_message
  data: {"type":"ws_message","data":"{\"price\":1.95,\"market\":\"moneyline\",\"event\":\"update\"}","ws_id":"req-1234","timestamp":1775511254500}

  event: ws_message
  data: {"type":"ws_message","data":"{\"price\":2.10,\"market\":\"moneyline\",\"event\":\"update\"}","ws_id":"req-1234","timestamp":1775511255200}

  event: ws_message
  data: {"type":"ws_message","data":"{\"price\":1.85,\"market\":\"spread\",\"event\":\"update\"}","ws_id":"req-1234","timestamp":1775511256800}
  ```
</CodeGroup>

<ResponseExample>
  ```json connected theme={null}
  {
    "type": "connected",
    "url": "https://example.com/live",
    "status": 200,
    "agent_id": "scraping-us5",
    "data": "{\"title\":\"Live Feed\",\"cookies\":[{\"name\":\"session\",\"value\":\"abc\"}]}",
    "timestamp": 1775511253069
  }
  ```

  ```json ws_open theme={null}
  {
    "type": "ws_open",
    "url": "wss://feed.example.com/ws",
    "ws_id": "req-1234",
    "timestamp": 1775511254100
  }
  ```

  ```json ws_message theme={null}
  {
    "type": "ws_message",
    "data": "{\"price\":1.95,\"market\":\"moneyline\",\"event\":\"update\"}",
    "ws_id": "req-1234",
    "timestamp": 1775511254500
  }
  ```

  ```json network theme={null}
  {
    "type": "network",
    "url": "https://api.example.com/v1/odds?eventId=12345",
    "method": "GET",
    "status": 200,
    "data": "{\"odds\":[{\"market\":\"moneyline\",\"home\":1.95,\"away\":2.10}]}",
    "content_type": "application/json",
    "timestamp": 1775511255000
  }
  ```

  ```json dom theme={null}
  {
    "type": "dom",
    "url": "https://example.com/live",
    "data": "<div id=\"price-table\"><span class=\"price\">1.95</span></div>",
    "timestamp": 1775511256000
  }
  ```

  ```json done theme={null}
  {
    "type": "done",
    "data": "closed",
    "timestamp": 1775511300000
  }
  ```
</ResponseExample>

<Note>
  The SSE stream runs until you disconnect. Each event arrives as `event: type\ndata: json\n\n`. Use `curl -N` (no buffering) to see events in real-time.
</Note>

### Example: Monitor network API calls

```bash theme={null}
curl -N -H "X-API-Key: YOUR_API_KEY" \
  -H "X-Target-URL: https://example.com/dashboard" \
  -H "X-Capture: network" \
  -H "X-Network-Filter: api\\.example\\.com" \
  https://scraping-api.55-tech.com/browser/stream
```

### Example: Watch DOM element for changes

```bash theme={null}
curl -N -H "X-API-Key: YOUR_API_KEY" \
  -H "X-Target-URL: https://example.com/prices" \
  -H "X-Capture: dom" \
  -H "X-DOM-Selector: #price-table" \
  -H "X-Wait-Strategy: selector" \
  -H "X-Wait-Selector: #price-table" \
  https://scraping-api.55-tech.com/browser/stream
```

### Python

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

resp = requests.get("https://scraping-api.55-tech.com/browser/stream", headers={
    "X-API-Key": "YOUR_API_KEY",
    "X-Target-URL": "https://example.com/live",
    "X-Capture": "ws",
}, stream=True)

for line in resp.iter_lines():
    if line.startswith(b"data: "):
        event = json.loads(line[6:])
        if event["type"] == "ws_message":
            print(event["data"])
```

## Next steps

<Columns cols={2}>
  <Card title="HTTP Fetch" icon="bolt" href="/scraping-api/quickstart">
    For pages that don't need JavaScript rendering, use the faster `/fetch` endpoint.
  </Card>

  <Card title="Error handling" icon="triangle-exclamation" href="/scraping-api/errors">
    Status codes, block detection, and retry strategies.
  </Card>
</Columns>
