# Universal Web Scraper API (`fayoussef/universal-scraper-api`) Actor

Scrape any URL through a global, anti-bot-resistant Scraper API. JS rendering, geo-targeting, residential proxies, CSS-selector extraction, and optional LLM processing.

- **URL**: https://apify.com/fayoussef/universal-scraper-api.md
- **Developed by:** [youssef farhan](https://apify.com/fayoussef) (community)
- **Categories:** Developer tools, Automation, AI
- **Stats:** 1 total users, 0 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.00 / 1,000 results

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.

Learn more: https://docs.apify.com/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are a software tools running on the Apify platform, for all kinds of web data extraction and automation use cases.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

In JavaScript/TypeScript projects, use official [JavaScript/TypeScript client](https://docs.apify.com/api/client/js.md):

```bash
npm install apify-client
```

In Python projects, use official [Python client library](https://docs.apify.com/api/client/python.md):

```bash
pip install apify-client
```

In shell scripts, use [Apify CLI](https://docs.apify.com/cli/docs.md):

````bash
# MacOS / Linux
curl -fsSL https://apify.com/install-cli.sh | bash
# Windows
irm https://apify.com/install-cli.ps1 | iex
```bash

In AI frameworks, you might use the [Apify MCP server](https://docs.apify.com/platform/integrations/mcp.md).

If your project is in a different language, use the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).


# README

## Universal Web Scraper API — Extract Any URL With Anti-Bot Proxies + AI

A universal web scraper API that turns any URL into clean HTML, plain text, and structured JSON, using global anti-bot proxies in 46 countries. Built for developers, researchers, growth teams, and AI agents that need reliable web data without managing proxies or browsers. Add CSS selectors or an LLM prompt and get structured output in one run.

### What you get

Every URL produces one dataset record with these fields:

- `url` — the page that was scraped.
- `statusCode` — HTTP status returned by the target.
- `ok` — `true` when the status was 2xx.
- `error` — error message for failed URLs (null on success).
- `scrapedAt` — ISO 8601 UTC timestamp of the scrape.
- `html` — full raw HTML (toggle off with `storeHtml`).
- `text` — readable text with scripts, styles, and markup stripped.
- `textLength` — character count of `text`.
- `extractedFields` — your CSS-selector fields as a key/value object.
- `llm` — optional AI result: `output` (text or parsed JSON), `model`, `usage`.
- `options` — echo of `render`, `geoCode`, and `superProxy` used per request.

### Sample output

```json
{
  "url": "https://quotes.toscrape.com",
  "statusCode": 200,
  "ok": true,
  "html": "<!DOCTYPE html><html>...</html>",
  "text": "Quotes to Scrape. The world as we have created it is a process of our thinking.",
  "textLength": 4213,
  "extractedFields": {
    "title": "Quotes to Scrape",
    "firstQuote": "The world as we have created it is a process of our thinking.",
    "firstAuthor": "Albert Einstein"
  },
  "llm": {
    "model": "google/gemini-3.1-flash-lite",
    "output": { "topic": "inspirational quotes", "itemCount": 10 },
    "usage": { "prompt_tokens": 1820, "completion_tokens": 24, "total_tokens": 1844 }
  },
  "options": { "render": true, "geoCode": "us", "superProxy": false },
  "scrapedAt": "2026-06-25T12:00:00.000000+00:00",
  "error": null
}
````

### Use cases

- ✓ Data engineers feeding clean page text and JSON into RAG and LLM pipelines.
- ✓ Price researchers monitoring competitor product pages across multiple countries.
- ✓ Growth and lead-gen teams pulling structured data from directories at scale.
- ✓ AI agent builders calling this web scraper API over MCP for live web data.
- ✓ SEO and content teams auditing pages behind anti-bot protection.
- ✓ Analysts extracting named fields from any site with simple CSS selectors.

### Pricing

This Actor is **pay-per-result** — you only pay for records pushed to the dataset.

| Event | Price per 1,000 | Real example |
|---|---|---|
| Scraped result (one URL) | `[PLACEHOLDER: $X.XX per 1,000 results]` | Scraping 10,000 URLs ≈ `[PLACEHOLDER: $XX]` |

Free tier: your first `[PLACEHOLDER: N]` results are free — no credit card needed to try it. You also use your own Scraper API key, and you can **[get 1,000 free monthly scraping credits here](https://scrape.do/?fpr=automationbyexperts)** to cover the underlying requests.

### How it works

- **Input:** a list of `startUrls` plus your Scraper API token.
- **Fetch:** each URL is routed through rotating anti-bot proxies, with optional JavaScript rendering and geo-targeting.
- **Process:** apply CSS `extractRules` and/or an LLM prompt to each page.
- **Output:** structured records land in the Apify dataset (export as JSON, CSV, Excel, or XML).
- **Automate:** run on a schedule and trigger webhooks on finish — built into the Apify platform.

### Why this web scraper API

- **Use your own key:** pay the scraping provider directly, with 1,000 free monthly credits to start.
- **Global anti-bot coverage:** residential/premium proxies and geo-targeting across 46 countries.
- **Built-in AI:** summarize, classify, or extract JSON via OpenRouter — pay with Apify credits or your own OpenRouter key.
- **Resumable, migration-safe runs:** state is persisted automatically, so a migrated run resumes without re-scraping or duplicating data.
- **Auto-scaled concurrency:** parallelism is sized to a percentage of your live plan limit, so you never trip rate limits.

### Input example

```json
{
  "apiKey": "YOUR_API_TOKEN",
  "startUrls": [{ "url": "https://quotes.toscrape.com" }],
  "render": true,
  "geoCode": "us",
  "extractRules": { "title": "h1", "firstQuote": ".quote .text" }
}
```

Omit `extractRules` and `llm*` fields to just return clean HTML and text. Set `concurrencyPercentage` to control speed.

### FAQ

**Does it handle anti-bot protection and proxies?**
Yes. Every request is routed through a rotating proxy network, with optional residential/premium proxies and per-country geo-targeting.

**What output formats are supported?**
The dataset exports to JSON, CSV, Excel, and XML, or via the Apify API.

**Can I scrape one URL or many?**
Both. Pass a single URL or thousands in `startUrls`; they are scraped concurrently.

**Does it support scheduling and webhooks?**
Yes — use Apify schedules to run it automatically and webhooks to push results downstream.

**Is the data live or cached?**
Live. Each run fetches the current page in real time.

**How do I extract specific fields?**
Provide `extractRules` as a `{ "fieldName": "css selector" }` map, or set an `llmPrompt` for AI extraction.

**Can AI agents call it?**
Yes — it's available via the Apify REST API and as an MCP server for Claude, ChatGPT, and Cursor (see below).

**Can I request custom fields or a different site?**
Yes — see the custom scraper section at the bottom.

### Use via API or MCP

Run it programmatically via the Apify REST API:

```
POST https://api.apify.com/v2/acts/fayoussef~universal-scraper-api/runs?token=YOUR_TOKEN
```

Or connect it as an MCP server so AI agents can call it directly:

```
https://mcp.apify.com/actors/fayoussef~universal-scraper-api
```

### Need a custom scraper?

Need different fields, a specific site, or a fully managed pipeline? Visit [automationbyexperts](https://automationbyexperts.com).

```text
PLACEHOLDERS TO FILL IN:
- Pay-per-result price per 1,000 results
- Real-run cost example (e.g. 10,000 URLs ≈ $XX)
- Free-tier amount (number of free results)
- Confirm custom-scraper CTA URL (automationbyexperts.com)
```

# Actor input Schema

## `apiKey` (type: `string`):

Your personal Scraper API token — usage is billed to your own API account. 👉 Don't have one? **[Create a free account and get 1,000 free monthly credits](https://scrape.do/?fpr=automationbyexperts)** — no credit card required. Paste your token from the dashboard once you sign up.

## `startUrls` (type: `array`):

List of URLs to scrape. Each URL is fetched through the Scraper API and pushed to the dataset as a structured record.

## `render` (type: `boolean`):

Enable a real headless browser to execute JavaScript before returning HTML. Turn ON for SPAs / dynamic sites (React, Vue, Angular). Costs more API credits per request.

## `geoCode` (type: `string`):

Route the request through a proxy located in the selected country. Choose "Worldwide" for automatic selection.

## `superProxy` (type: `boolean`):

Route requests through the residential / premium proxy pool. Best for heavily protected targets. Costs more credits per request.

## `extractRules` (type: `object`):

Optional mapping of field name → CSS selector. For every record the Actor returns the text of the first element matching each selector under `extractedFields`. Example: {"title": "h1", "price": ".price", "author": ".author span"}.

## `llmEnabled` (type: `boolean`):

After scraping each page, run an LLM over its content (summarize, classify, or extract structured data with your own prompt). Powered by OpenRouter. Leave OFF to skip LLM processing entirely.

## `llmModel` (type: `string`):

OpenRouter model used to process each page, listed cheapest to most expensive. The flash / flash-lite models with 1M context are ideal for reading and extracting from long scraped pages at low cost. Need a model not listed here? Use "Custom model slug" below.

## `llmModelCustom` (type: `string`):

Optional. Any OpenRouter model slug (e.g. "anthropic/claude-opus-4.8"). When set, this takes priority over the dropdown above — useful for models not in the list or newly released ones. See https://openrouter.ai/models for all slugs.

## `llmPrompt` (type: `string`):

Instruction applied to each page's content. Examples: "Summarize this page in 3 bullet points." or "Extract the product name, price and availability." The scraped page text is appended automatically.

## `llmJsonOutput` (type: `boolean`):

Ask the model to return a strict JSON object (sets response\_format=json\_object) and parse it into `llmResult`. Best paired with a prompt that describes the exact JSON fields you want.

## `llmMaxInputChars` (type: `integer`):

The page text is truncated to this many characters before being sent to the model, to control token cost. Increase for fuller context.

## `llmMaxTokens` (type: `integer`):

Maximum number of tokens in the LLM response.

## `llmTemperature` (type: `string`):

Sampling temperature 0.0–2.0. Lower is more deterministic (good for extraction); higher is more creative. Leave empty for the model default.

## `concurrencyPercentage` (type: `integer`):

How much of your plan's concurrent-request limit to use, as a percentage. The Actor reads your live plan limit from the API and scrapes with that percentage of your allowed concurrency. Example: a plan allowing 40 concurrent requests at 50% → 20 parallel requests. Default 100 = full speed (your entire plan limit).

## `maxRetries` (type: `integer`):

How many times to retry a failing URL (on network errors, rate limits, or 5xx responses) with exponential backoff before giving up.

## `requestTimeout` (type: `integer`):

Per-request timeout in seconds when calling the Scraper API.

## `customHeaders` (type: `object`):

Optional HTTP headers forwarded to the target site (e.g. a specific User-Agent or Accept-Language).

## `storeHtml` (type: `boolean`):

Include the full raw HTML in each dataset record. Disable to keep datasets small when you only need cleaned text or extracted fields.

## `telegramAlert` (type: `boolean`):

When enabled, a run report (counts, config, account status, and errors) is sent to Telegram when the Actor finishes.

## `telegramBotToken` (type: `string`):

Optional. Override the default Telegram bot token. Leave empty to use the built-in default.

## `telegramChatId` (type: `string`):

Optional. Override the default Telegram chat ID that receives the report. Leave empty to use the built-in default.

## `openRouterApiKey` (type: `string`):

Optional. Only used when LLM processing is enabled. Provide your own OpenRouter API key to be billed directly by OpenRouter at their rates. Leave EMPTY to use Apify's hosted OpenRouter (https://apify.com/apify/openrouter) and pay with your Apify credits instead — no extra signup needed. Get an OpenRouter key at https://openrouter.ai/keys.

## Actor input object example

```json
{
  "startUrls": [
    {
      "url": "https://httpbin.org/anything"
    },
    {
      "url": "https://quotes.toscrape.com"
    }
  ],
  "render": false,
  "geoCode": "",
  "superProxy": false,
  "extractRules": {
    "title": "h1",
    "firstQuote": ".quote .text"
  },
  "llmEnabled": false,
  "llmModel": "google/gemini-3.1-flash-lite",
  "llmPrompt": "Summarize the key information on this page in 3-5 concise bullet points.",
  "llmJsonOutput": false,
  "llmMaxInputChars": 12000,
  "llmMaxTokens": 1024,
  "concurrencyPercentage": 100,
  "maxRetries": 3,
  "requestTimeout": 60,
  "storeHtml": true,
  "telegramAlert": true
}
```

# Actor output Schema

## `scrapedPages` (type: `string`):

One structured record per scraped URL — status, clean text, raw HTML, CSS-extracted fields and optional AI output.

## `planSnapshot` (type: `string`):

Scraper API plan limits and remaining quota captured at run start.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "startUrls": [
        {
            "url": "https://httpbin.org/anything"
        },
        {
            "url": "https://quotes.toscrape.com"
        }
    ],
    "extractRules": {
        "title": "h1",
        "firstQuote": ".quote .text"
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("fayoussef/universal-scraper-api").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "startUrls": [
        { "url": "https://httpbin.org/anything" },
        { "url": "https://quotes.toscrape.com" },
    ],
    "extractRules": {
        "title": "h1",
        "firstQuote": ".quote .text",
    },
}

# Run the Actor and wait for it to finish
run = client.actor("fayoussef/universal-scraper-api").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "startUrls": [
    {
      "url": "https://httpbin.org/anything"
    },
    {
      "url": "https://quotes.toscrape.com"
    }
  ],
  "extractRules": {
    "title": "h1",
    "firstQuote": ".quote .text"
  }
}' |
apify call fayoussef/universal-scraper-api --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=fayoussef/universal-scraper-api",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

```json
{
    "openapi": "3.0.1",
    "info": {
        "title": "Universal Web Scraper API",
        "description": "Scrape any URL through a global, anti-bot-resistant Scraper API. JS rendering, geo-targeting, residential proxies, CSS-selector extraction, and optional LLM processing.",
        "version": "1.0",
        "x-build-id": "5QM97UybXcB1V7d3e"
    },
    "servers": [
        {
            "url": "https://api.apify.com/v2"
        }
    ],
    "paths": {
        "/acts/fayoussef~universal-scraper-api/run-sync-get-dataset-items": {
            "post": {
                "operationId": "run-sync-get-dataset-items-fayoussef-universal-scraper-api",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for its completion, and returns Actor's dataset items in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        },
        "/acts/fayoussef~universal-scraper-api/runs": {
            "post": {
                "operationId": "runs-sync-fayoussef-universal-scraper-api",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor and returns information about the initiated run in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/runsResponseSchema"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/acts/fayoussef~universal-scraper-api/run-sync": {
            "post": {
                "operationId": "run-sync-fayoussef-universal-scraper-api",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for completion, and returns the OUTPUT from Key-value store in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        }
    },
    "components": {
        "schemas": {
            "inputSchema": {
                "type": "object",
                "required": [
                    "apiKey",
                    "startUrls"
                ],
                "properties": {
                    "apiKey": {
                        "title": "Scraper API Token",
                        "type": "string",
                        "description": "Your personal Scraper API token — usage is billed to your own API account. 👉 Don't have one? **[Create a free account and get 1,000 free monthly credits](https://scrape.do/?fpr=automationbyexperts)** — no credit card required. Paste your token from the dashboard once you sign up."
                    },
                    "startUrls": {
                        "title": "Start URLs",
                        "type": "array",
                        "description": "List of URLs to scrape. Each URL is fetched through the Scraper API and pushed to the dataset as a structured record.",
                        "items": {
                            "type": "object",
                            "required": [
                                "url"
                            ],
                            "properties": {
                                "url": {
                                    "type": "string",
                                    "title": "URL of a web page",
                                    "format": "uri"
                                }
                            }
                        }
                    },
                    "render": {
                        "title": "Render JavaScript",
                        "type": "boolean",
                        "description": "Enable a real headless browser to execute JavaScript before returning HTML. Turn ON for SPAs / dynamic sites (React, Vue, Angular). Costs more API credits per request.",
                        "default": false
                    },
                    "geoCode": {
                        "title": "Geo-targeting location",
                        "enum": [
                            "",
                            "ae",
                            "al",
                            "ar",
                            "at",
                            "au",
                            "br",
                            "ca",
                            "ch",
                            "cl",
                            "cn",
                            "cr",
                            "cy",
                            "cz",
                            "de",
                            "dk",
                            "ee",
                            "eg",
                            "es",
                            "fi",
                            "fr",
                            "gb",
                            "gr",
                            "hr",
                            "ie",
                            "it",
                            "jp",
                            "lt",
                            "lv",
                            "mt",
                            "nl",
                            "no",
                            "pk",
                            "pl",
                            "pt",
                            "ro",
                            "rs",
                            "ru",
                            "se",
                            "sg",
                            "si",
                            "sk",
                            "tr",
                            "ua",
                            "us",
                            "za"
                        ],
                        "type": "string",
                        "description": "Route the request through a proxy located in the selected country. Choose \"Worldwide\" for automatic selection.",
                        "default": ""
                    },
                    "superProxy": {
                        "title": "Use residential / premium proxy",
                        "type": "boolean",
                        "description": "Route requests through the residential / premium proxy pool. Best for heavily protected targets. Costs more credits per request.",
                        "default": false
                    },
                    "extractRules": {
                        "title": "Structured extraction rules",
                        "type": "object",
                        "description": "Optional mapping of field name → CSS selector. For every record the Actor returns the text of the first element matching each selector under `extractedFields`. Example: {\"title\": \"h1\", \"price\": \".price\", \"author\": \".author span\"}."
                    },
                    "llmEnabled": {
                        "title": "🤖 Enable LLM processing",
                        "type": "boolean",
                        "description": "After scraping each page, run an LLM over its content (summarize, classify, or extract structured data with your own prompt). Powered by OpenRouter. Leave OFF to skip LLM processing entirely.",
                        "default": false
                    },
                    "llmModel": {
                        "title": "LLM model (cheapest → most capable)",
                        "enum": [
                            "deepseek/deepseek-v4-flash",
                            "qwen/qwen3.6-flash",
                            "google/gemini-3.1-flash-lite",
                            "deepseek/deepseek-v4-pro",
                            "qwen/qwen3.6-max-preview",
                            "x-ai/grok-4.3",
                            "google/gemini-3.5-flash",
                            "mistralai/mistral-medium-3.5",
                            "anthropic/claude-fable-5",
                            "openai/gpt-5.5-pro",
                            "openrouter/fusion"
                        ],
                        "type": "string",
                        "description": "OpenRouter model used to process each page, listed cheapest to most expensive. The flash / flash-lite models with 1M context are ideal for reading and extracting from long scraped pages at low cost. Need a model not listed here? Use \"Custom model slug\" below.",
                        "default": "google/gemini-3.1-flash-lite"
                    },
                    "llmModelCustom": {
                        "title": "Custom model slug (optional, overrides the dropdown)",
                        "type": "string",
                        "description": "Optional. Any OpenRouter model slug (e.g. \"anthropic/claude-opus-4.8\"). When set, this takes priority over the dropdown above — useful for models not in the list or newly released ones. See https://openrouter.ai/models for all slugs."
                    },
                    "llmPrompt": {
                        "title": "LLM prompt / instruction",
                        "type": "string",
                        "description": "Instruction applied to each page's content. Examples: \"Summarize this page in 3 bullet points.\" or \"Extract the product name, price and availability.\" The scraped page text is appended automatically.",
                        "default": "Summarize the key information on this page in 3-5 concise bullet points."
                    },
                    "llmJsonOutput": {
                        "title": "Force JSON output",
                        "type": "boolean",
                        "description": "Ask the model to return a strict JSON object (sets response_format=json_object) and parse it into `llmResult`. Best paired with a prompt that describes the exact JSON fields you want.",
                        "default": false
                    },
                    "llmMaxInputChars": {
                        "title": "Max input characters sent to LLM",
                        "minimum": 500,
                        "maximum": 100000,
                        "type": "integer",
                        "description": "The page text is truncated to this many characters before being sent to the model, to control token cost. Increase for fuller context.",
                        "default": 12000
                    },
                    "llmMaxTokens": {
                        "title": "Max output tokens",
                        "minimum": 1,
                        "maximum": 16000,
                        "type": "integer",
                        "description": "Maximum number of tokens in the LLM response.",
                        "default": 1024
                    },
                    "llmTemperature": {
                        "title": "LLM temperature",
                        "type": "string",
                        "description": "Sampling temperature 0.0–2.0. Lower is more deterministic (good for extraction); higher is more creative. Leave empty for the model default."
                    },
                    "concurrencyPercentage": {
                        "title": "Concurrency (% of your plan)",
                        "minimum": 1,
                        "maximum": 100,
                        "type": "integer",
                        "description": "How much of your plan's concurrent-request limit to use, as a percentage. The Actor reads your live plan limit from the API and scrapes with that percentage of your allowed concurrency. Example: a plan allowing 40 concurrent requests at 50% → 20 parallel requests. Default 100 = full speed (your entire plan limit).",
                        "default": 100
                    },
                    "maxRetries": {
                        "title": "Max retries per URL",
                        "minimum": 0,
                        "maximum": 10,
                        "type": "integer",
                        "description": "How many times to retry a failing URL (on network errors, rate limits, or 5xx responses) with exponential backoff before giving up.",
                        "default": 3
                    },
                    "requestTimeout": {
                        "title": "Request timeout (seconds)",
                        "minimum": 10,
                        "maximum": 180,
                        "type": "integer",
                        "description": "Per-request timeout in seconds when calling the Scraper API.",
                        "default": 60
                    },
                    "customHeaders": {
                        "title": "Custom request headers",
                        "type": "object",
                        "description": "Optional HTTP headers forwarded to the target site (e.g. a specific User-Agent or Accept-Language)."
                    },
                    "storeHtml": {
                        "title": "Store raw HTML in dataset",
                        "type": "boolean",
                        "description": "Include the full raw HTML in each dataset record. Disable to keep datasets small when you only need cleaned text or extracted fields.",
                        "default": true
                    },
                    "telegramAlert": {
                        "title": "Send Telegram report on finish",
                        "type": "boolean",
                        "description": "When enabled, a run report (counts, config, account status, and errors) is sent to Telegram when the Actor finishes.",
                        "default": true
                    },
                    "telegramBotToken": {
                        "title": "Telegram bot token (optional)",
                        "type": "string",
                        "description": "Optional. Override the default Telegram bot token. Leave empty to use the built-in default."
                    },
                    "telegramChatId": {
                        "title": "Telegram chat ID (optional)",
                        "type": "string",
                        "description": "Optional. Override the default Telegram chat ID that receives the report. Leave empty to use the built-in default."
                    },
                    "openRouterApiKey": {
                        "title": "OpenRouter API key (optional)",
                        "type": "string",
                        "description": "Optional. Only used when LLM processing is enabled. Provide your own OpenRouter API key to be billed directly by OpenRouter at their rates. Leave EMPTY to use Apify's hosted OpenRouter (https://apify.com/apify/openrouter) and pay with your Apify credits instead — no extra signup needed. Get an OpenRouter key at https://openrouter.ai/keys."
                    }
                }
            },
            "runsResponseSchema": {
                "type": "object",
                "properties": {
                    "data": {
                        "type": "object",
                        "properties": {
                            "id": {
                                "type": "string"
                            },
                            "actId": {
                                "type": "string"
                            },
                            "userId": {
                                "type": "string"
                            },
                            "startedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "finishedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "status": {
                                "type": "string",
                                "example": "READY"
                            },
                            "meta": {
                                "type": "object",
                                "properties": {
                                    "origin": {
                                        "type": "string",
                                        "example": "API"
                                    },
                                    "userAgent": {
                                        "type": "string"
                                    }
                                }
                            },
                            "stats": {
                                "type": "object",
                                "properties": {
                                    "inputBodyLen": {
                                        "type": "integer",
                                        "example": 2000
                                    },
                                    "rebootCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "restartCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "resurrectCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "computeUnits": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "options": {
                                "type": "object",
                                "properties": {
                                    "build": {
                                        "type": "string",
                                        "example": "latest"
                                    },
                                    "timeoutSecs": {
                                        "type": "integer",
                                        "example": 300
                                    },
                                    "memoryMbytes": {
                                        "type": "integer",
                                        "example": 1024
                                    },
                                    "diskMbytes": {
                                        "type": "integer",
                                        "example": 2048
                                    }
                                }
                            },
                            "buildId": {
                                "type": "string"
                            },
                            "defaultKeyValueStoreId": {
                                "type": "string"
                            },
                            "defaultDatasetId": {
                                "type": "string"
                            },
                            "defaultRequestQueueId": {
                                "type": "string"
                            },
                            "buildNumber": {
                                "type": "string",
                                "example": "1.0.0"
                            },
                            "containerUrl": {
                                "type": "string"
                            },
                            "usage": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "integer",
                                        "example": 1
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "usageTotalUsd": {
                                "type": "number",
                                "example": 0.00005
                            },
                            "usageUsd": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "number",
                                        "example": 0.00005
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
```
