# Stealth Browser Agent — Anti-Detect AI Browser Automation (`shelvick/stealth-browser-agent`) Actor

Stealth web automation agent and AI browser agent for hard-to-automate sites: computer use and browser use on the web. Send a URL and a plain-language task; it navigates, fills and submits forms, runs multi-step flows, and returns structured JSON. Anti-detection fingerprinting, residential proxy.

- **URL**: https://apify.com/shelvick/stealth-browser-agent.md
- **Developed by:** [Scott Helvick](https://apify.com/shelvick) (community)
- **Categories:** AI, Agents, Automation
- **Features:** Limited permissions
- **Stats:** 153 total users, 36 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $17.00 / 1,000 step completeds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
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.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

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

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

AI agents need to interact with web pages that block automation. This Actor provides a hosted stealth browser built for sites that run bot detection — send a URL and a natural-language task, and an LLM-driven browser navigates, clicks, types, fills and submits forms, and automates multi-step flows, extracting structured JSON from bot-defended sites.

### What this does

- **Stealth browsing** — anti-detection fingerprinting passes real-world bot detection systems. Pages see a genuine browser, not automation tooling.
- **Residential proxy** — optional geo-targeted routing through residential IPs. Datacenter IPs are a fingerprint signal; residential routing eliminates that vector.
- **LLM-driven interaction** — an AI copilot reads screenshots, plans actions, and drives browser tools (click, type, navigate, scroll, select, wait). No automation scripts to write.
- **Structured extraction** — the agent returns results as JSON. Provide an output schema and the result conforms to it; omit it and the agent returns best-effort JSON.
- **Evidence for every answer** — beside the result, each field shows where its value appears on the pages the agent visited: verbatim, normalized (reformatted), ambiguous or not found, with the step, page URL and a short excerpt. Fields the agent couldn't get say why (login wall, human verification, not on the page). On every run, at no extra charge. Ambiguous means the page shows the number in a form that could be read two ways.
- **Batch mode** — pass `urls` instead of `url` to run the same task and output schema on a list of pages in one run. URLs run one after another, each gets its own row as it finishes, and a URL the run can't cover returns a free "not started" row.
- **Action log + screenshot** — every run produces a step-by-step action log and a final screenshot for debugging and audit.

Use cases:

- Extract product data from bot-defended e-commerce pages
- Fill and submit multi-step forms on behalf of an agent workflow
- Navigate paginated catalogs — click through pages, extract across them
- Extract structured data from JavaScript-heavy SPAs that block HTTP fetchers
- Verify page state after interaction (confirmation pages, submission results)

### Why stealth matters

Most browser automation works fine on cooperative sites. But a growing share of the web uses bot detection — fingerprinting browser characteristics, checking IP reputation, analyzing interaction patterns. Standard headless browsers get flagged on first request.

The subtler problem: even when requests succeed, bot-detection systems serve degraded content to suspected bots. Different prices, missing inventory, placeholder text. Your agent extracts data that looks correct but isn't.

This Actor runs a browser with realistic fingerprinting that passes detection systems in production. Pages see a real browser session. When combined with residential proxy routing, the browser's network fingerprint matches its claimed identity — no datacenter IP giving away the automation.

### How it compares to alternatives

| Approach | Stealth | Interaction | Structured output | Cost model |
|---|---|---|---|---|
| Headless browser (self-hosted) | None — detected immediately | Full (you write scripts) | Manual extraction | Your infrastructure |
| Stealth fetch service | Anti-detection | None — page content only | Raw HTML/markdown | Per-page |
| Browser-as-a-service (no stealth) | None | Full (LLM-driven) | LLM-extracted | Per-step or flat |
| **Stealth Browser Agent** | Anti-detection + residential proxy | Full (LLM-driven) | Structured JSON | Per-step |

Stealth fetch services return rendered content from defended pages but can't interact — if you need to click a button before the data appears, you're stuck. Browser-as-a-service tools provide LLM-driven interaction but use standard browsers that get fingerprinted on defended sites. This Actor combines both: stealth browsing with LLM-driven interaction and structured extraction.

### Input

| Field | Type | Required | Default | Description |
|---|---|---|---|---|
| `url` | string | Yes | -- | Starting URL. The agent navigates here first, then executes the task. |
| `task` | string | Yes | -- | What to do on the page, in plain language. Can include interaction steps (click, fill, navigate) and extraction goals. The agent plans and executes actions, then returns structured results. |
| `outputSchema` | object | No | -- | JSON Schema for the desired result shape. When provided, the agent structures its extraction to match. When omitted, returns best-effort JSON. |
| `modelTier` | string | No | `basic` | How much reasoning the agent applies when planning each step. `basic` handles ordinary navigation, form filling and extraction. `premium` plans with deeper reasoning for complex, ambiguous or heavily defended multi-step flows, at a higher per-step rate. |
| `maxSteps` | integer | No | `100` | Hard cap on billable steps in one run (1–100); failed steps do not count toward the cap. The primary cost cap is `maxTotalChargeUsd` — the agent stops when the next step would exceed your budget — so lower `maxSteps` only if you want a tighter runaway guard than your budget already gives you. |
| `timeoutSeconds` | integer | No | `270` | Budget for the agent's work on the page, starting when the browser is ready. Browser bring-up (typically 10–90 s) does not count against it. Steps typically take 5–10 s each, so a 20–40-step flow needs roughly 150–400 s. |
| `stepTimeoutSeconds` | integer | No | `45` | Per-action stall guard. Protects your budget from a single hung browser action burning `maxTotalChargeUsd`. A stalled step is logged and the agent tries a different approach. |
| `proxyGeo` | string | No | -- | ISO 3166-1 alpha-2 country code (e.g. `US`, `DE`). Routes through a residential proxy in that country. Leave empty for default routing. |
| `authMode` | string | No | `none` | Set to `storageState` to run inside an account you own, using a session you export yourself. The Actor never accepts passwords. |
| `storageState` | object | No | -- | Playwright `storageState` JSON (`{cookies: [...], origins: [...]}`) exported from your own logged-in browser. Stored encrypted; never written to the run log, the step transcript, or the dataset. Required when `authMode` is `storageState`. |
| `authAttestation` | boolean | No | `false` | Must be `true` when `authMode` is set: you confirm the account and site are owned or controlled by you or your organization and that you are authorized to automate them. |

### Output

Each run produces one dataset record:

`retry` provides machine-readable guidance derived from `errorClass`: `null` on success, `later` to retry after a short delay, `with_changes` to correct the input first, or `never` to avoid repeating the same input.

```json
{
  "url": "https://books.toscrape.com/catalogue/a-light-in-the-attic_1000/index.html",
  "task": "Click the first book, extract its title and price.",
  "status": "completed",
  "result": {
    "title": "A Light in the Attic",
    "price": "£51.77"
  },
  "steps": [
    {"step": 1, "tool": "click_element", "args": {"selector": "article h3 a"}, "success": true},
    {"step": 2, "tool": "get_text", "args": {"selector": ".product_main"}, "success": true}
  ],
  "screenshotUrl": "https://api.apify.com/v2/key-value-stores/<STORE_ID>/records/screenshot?signature=<SIGNATURE>"
}
```

| Field | Type | Description |
|---|---|---|
| `url` | string | Final URL after all navigation and redirects |
| `task` | string | Echo of the input task |
| `status` | string | `completed`, `failed`, `timeout`, `budget_exhausted`, `max_steps_reached`, or `aborted` (run stopped; this record is never charged; retry the task) |
| `result` | object | Structured extraction — shaped by `outputSchema` if provided |
| `resultComplete` | boolean | `true` only when `status` is `completed` and `result` has data. When `false`, `result` is either empty, all `null` (see `error`), or a best-effort partial from a capped run — check this before trusting `result` as a full answer |
| `steps` | array | Ordered action log: step number, tool name, arguments, success flag |
| `screenshotUrl` | string | Token-free URL of the final page screenshot, signed when required |
| `stepTranscriptUrl` | string | Token-free URL of the capped step transcript on incomplete runs after at least one step, signed when required |
| `error` | string | Error message when status is not `completed`, or when a `completed` result has every field `null` (null otherwise) |
| `errorClass` | string | Machine-routable failure class; `null` on success |

**Failure taxonomy:** `run_aborted` means the run was stopped, not a target-site outage; retry the task. `source_error` is a temporary target-site problem and is retryable; `source_walled` is an access challenge and may succeed on retry; `budget_exhausted` is retryable with a larger budget. `no_content`, `malformed_input`, `agent_error`, and `not_covered` require changing the page, input, or task rather than retrying unchanged. `not_covered` means this Actor does not operate on that target, so re-running will not change the answer.

### Example

```json
{
  "url": "https://books.toscrape.com",
  "task": "Click the first book, then extract its title, price, and availability. Return JSON with fields title, price, and in_stock."
}
```

### Calling from an AI agent

**Tell us what's missing.** If a run doesn't give you what you need, add a `feedback` note to your next run: what was missing or wrong, or which tool you're switching to and why. The maintainers read every note.

#### Apify MCP server

The Actor is available as a callable tool via `mcp.apify.com`. The input schema is self-documenting — an LLM can construct correct calls from the tool description and field names alone.

#### Apify SDK (Python)

```python
from apify_client import ApifyClient

client = ApifyClient("YOUR_TOKEN")
run = client.actor("shelvick/stealth-browser-agent").call(
    run_input={
        "url": "https://example.com",
        "task": "Extract the main heading and all links on the page.",
    }
)
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item["status"], item["result"])
```

#### REST API

**Synchronous** (blocks until complete, returns dataset items directly):

```
POST https://api.apify.com/v2/acts/shelvick~stealth-browser-agent/run-sync-get-dataset-items?token=YOUR_TOKEN
```

**Asynchronous** (starts run, poll for completion):

```
POST https://api.apify.com/v2/acts/shelvick~stealth-browser-agent/runs?token=YOUR_TOKEN
GET  https://api.apify.com/v2/actor-runs/{runId}/dataset/items?token=YOUR_TOKEN
```

The synchronous endpoint has a 5-minute cap. For complex tasks that may exceed this, use the async endpoint.

### Authenticated runs

By default the agent works on public pages and never signs in. When you need it to work inside an account, hand it a session you exported yourself:

- Set `authMode` to `storageState`.
- Paste your exported session into `storageState`. It is stored encrypted, and never written to the run log, the step transcript, or the dataset.
- Set `authAttestation` to `true` to confirm the account and site are yours or your organization's and that you are authorized to automate them.

This Actor never accepts a password. There is no username or password field, and the agent is instructed never to type credentials — if the site shows it logged out, the run ends with an error rather than attempting a login.

#### Export your session

From a browser you have already logged in with, using Playwright:

```python
context.storage_state(path="session.json")
```

Then paste the contents of `session.json` into `storageState`. A cookie-export browser extension that writes Playwright `storageState` JSON works the same way. Sessions expire — re-export when a run reports that the session was not accepted.

**If a run comes back logged out.** Some sites tie a session to the browser that created it, by user agent or IP, and hand any other browser a fresh signed-out session. A session exported from a tool or a script rather than a real browser fails this way most often. Two things help: export from an ordinary browser you logged in with by hand, and re-export shortly before the run. When the site clears the supplied session outright, the run stops before the agent does any work and no steps are charged.

### Acceptable use

You are responsible for ensuring your use of each target site complies with that site's terms of service and applicable law.

Authenticated runs are for accounts you own or are authorized to automate. Runs against someone else's account are prohibited.

Some categories are not available for authenticated runs at all. A run that targets one ends immediately with `errorClass: not_covered` and no step charges:

- **Financial** — banks, card issuers, brokerages, payment and crypto services.
- **Government** — government and military sites, and identity portals.
- **Healthcare** — patient portals and health-record systems.
- **Email and account providers** — mailbox sign-ins, which are the reset path for every other account you own.
- **Sites whose operators pursue automated access through legal action** — a named list, kept in the Actor's source.

Public pages on those sites are still reachable with `authMode` left as `none`.

We honour access-revocation requests from site operators: a site whose operator asks us not to access it is blocked for every run, in every mode.

### Pricing

Charged per step — each browser action (click, type, navigate, extract) counts as one billable step. Two things move the per-step rate:

- **Capability tier** (`modelTier`) — `basic` is the default and the cheaper rate; `premium` applies deeper reasoning to each planning decision and costs more per step. If you never set `modelTier`, you stay on the basic rate.
- **Proxy routing** (`proxyGeo`) — setting a country routes the browser through a residential proxy, and the higher per-step rate for that covers the residential bandwidth.

That gives four per-step rates: basic and premium, each with and without proxy.

Failed steps (browser errors) and failed planning calls are never charged. Only successful tool executions are billed. The extraction step (when the agent returns its final result) counts as one step.

The primary cost cap is `maxTotalChargeUsd` — the agent checks the remaining budget before each step and stops if the next step would push spend past your cap. A single hung browser action can't burn the rest of the budget either, thanks to the per-step stall guard (`stepTimeoutSeconds`). `maxSteps` caps billable steps at 100 per run on top of those two.

Premium costs meaningfully more per step, so it is worth reaching for when a task is genuinely hard — ambiguous multi-page flows, heavily defended sites, decisions that depend on reading the page carefully — and worth leaving alone when it isn't.

See the **Pricing** tab on this Store page for the current per-event rates and any active subscriber discounts.

### Behavior

**Failure modes:**

- `not_covered` — the target is in a category this Actor does not sign into, or its operator has asked us not to access it. No steps are charged.
- `failed` — the agent could not accomplish the task (page requires authentication, target element not found, page is blank or broken)
- `budget_exhausted` — `maxTotalChargeUsd` would be exceeded by the next step. Raise the budget to continue further.
- `timeout` — the agent-work budget (`timeoutSeconds`) was reached after browser bring-up. Partial results may be present in `result`.
- `max_steps_reached` — the run hit its `maxSteps` cap (100 by default) before finishing. Raise `maxSteps` if you lowered it; at 100 the task needs splitting into smaller runs.

**Partial results on capped runs.** When a run hits `maxSteps` or `maxTotalChargeUsd` after collecting data, the agent makes one final synthesis pass over what it already gathered and delivers it in `result` as an honest partial: fields it has no data for are `null`, `resultComplete` is `false`, and the status keeps the cap value — a partial is never presented as a completed run. The synthesis pass itself is never charged. Use the partial as-is, or re-run with a higher cap for the full answer; the step-by-step transcript is available at `stepTranscriptUrl` either way.

The `error` field contains a human-readable explanation on non-`completed` runs, and on a `completed` run whose `result` has every field `null`: why, and what to change in the task.

**Run-level corrections**: invalid input (missing `url` or `task`, malformed `outputSchema`) returns a free correction record before any steps execute. A graceful abort returns an `aborted` record with the steps completed so far; this record is never charged. Previously completed steps retain their charges. Retry the task. Migration writes no customer record; the restarted run resumes after the last delivered batch URL for the same input. The checkpoint is cleared on completion or abort, so resurrecting a finished or aborted batch starts it again.

**Performance expectations:**

- The stealth browser session takes ~30-90 seconds to launch and load the target before the first step runs. Hardened sites take the longest — heavily defended pages can push bring-up toward two minutes.
- Once the browser is ready, steps after that typically run a few seconds each (planning plus the browser action).
- A simple 1-2 step extraction usually completes in about a minute end to end; multi-step interactions and multi-page flows scale up from there.
- Residential proxy routing adds a little latency per navigation.

**Telemetry:** to improve reliability and coverage, this Actor reports anonymous usage metrics and diagnostic events to the developer — run outcome counts, the target site's hostname, and, only when something goes wrong, the relevant input fields. No account identifiers are collected, and telemetry never affects a run.

### FAQ

**Am I charged if the task fails?**
Steps that completed successfully before the failure are charged. The failing step itself is not. If the run fails before any steps execute (invalid input, unreachable URL), no results are charged beyond the platform start event.

**How do I control costs on complex tasks?**
Set `maxTotalChargeUsd` in the run configuration. The agent enforces this as a real bound — it stops before charging a step that would push spend past your cap (status: `budget_exhausted`). No need to also fiddle with step or time caps for cost control; the budget is the ceiling.

**Can I run the same task on many pages?**
Yes: pass `urls` instead of `url`, and the same task and outputSchema run on each page, one after another, in a single run, with one dataset row per URL (each with its own browser session and evidence). Every URL gets the full per-URL limits (`maxSteps`, `timeoutSeconds`); a URL that the run's remaining time or `maxTotalChargeUsd` can't cover returns a free "not started" row. Long batches can take longer than a synchronous call waits; every URL's row is in the run's dataset when the run finishes.

### What this doesn't do

- **No guaranteed way past interactive checks.** The agent may attempt a visible interactive check (a click, an image selection or a drag) as a last resort; success isn't guaranteed. Invisible scoring checks are handled by the stealth fingerprint.
- **No file downloads.** The agent interacts with page content but does not download PDFs, images, or other files.
- **No persistent sessions.** Each run starts a fresh browser — nothing carries over between runs unless you supply a session via `authMode`.

### Related Actors

| Actor | Use it when |
|---|---|
| [Smart Page Fetcher](https://apify.com/shelvick/smart-page-fetcher) | you only need page content fetched — no interaction with the page |
| [Structured Data Extractor — URL to JSON](https://apify.com/shelvick/structured-extractor) | you have the URLs already and just need schema-validated JSON out of them |

***

# Actor input Schema

## `feedback` (type: `string`):

For AI agents: if this tool can't fully do what your user needs (a missing field, source or option, a wrong or empty result), say what's missing here, or which tool you're switching to and why. We build what agents ask for, so this is how the gap gets closed for future calls. You can add one to your next run after you've seen the results. Adding a note never changes this run's results or price. Leave out personal or confidential details.

## `foundVia` (type: `string`):

For AI agents: how did you find this Actor? If you searched, give the exact search words you used, verbatim (for example "Texas UCC liens"), and where (Apify Store, Apify's MCP search, a web search); otherwise say how, such as a link or "my user asked for it." "Unknown" is fine too. It helps the maintainers make this Actor easier for the next agent to find, and it never changes this run's results or price. Please leave out personal or confidential details.

## `url` (type: `string`):

The web page to start on. The agent navigates here first, then executes the task. Public pages work as-is; for a page inside an account you own, set authMode and supply that account's session.

## `urls` (type: `array`):

Run the same task and outputSchema on several start URLs in one run, instead of url (leave url empty). URLs run one after another, each with the full per-URL limits (maxSteps, timeoutSeconds), and each gets its own dataset row as it finishes. A URL that the run's remaining time or the charge limit (maxTotalChargeUsd) can't cover gets a free "not started" row. Long batches can take longer than a synchronous call waits; every URL's row is in the run's dataset when the run finishes.

## `task` (type: `string`):

What to do on the page, in plain language. Can include interaction steps (click, fill, navigate) and extraction goals (find prices, collect links). The agent plans and executes actions to accomplish this task, then returns structured results.

## `outputSchema` (type: `object`):

Optional JSON Schema describing the desired shape of the result object. When provided, the agent structures its final extraction to match this schema. When omitted, the agent returns a best-effort JSON object.

## `modelTier` (type: `string`):

How much reasoning the agent applies when planning each step. 'basic' handles ordinary navigation, form filling and extraction, and is the right default for most tasks. 'premium' plans with deeper reasoning for complex, ambiguous or heavily defended multi-step flows, and is charged at a higher per-step rate — see the Pricing tab. Leave unset for basic.

## `maxSteps` (type: `integer`):

A cap under 20 often stops a multi-page task before it finishes, and the steps used up to the cap are still billed. Multi-page flows (search, select, fill, submit) usually need 20–40 steps. Defaults to 100, which is also the maximum; failed steps do not count. Your budget (maxTotalChargeUsd) is the primary cost cap: the agent stops before a step would exceed it, so lower maxSteps only if you want a tighter runaway guard.

## `timeoutSeconds` (type: `integer`):

Budget for the agent's work on the page, starting when the browser is ready. Browser bring-up (typically 10–90 s) does not count against it. Maximum 900 s. Steps typically take 5–10 s each, so a 20–40-step multi-page flow needs roughly 150–400 s; raise this for long flows. A run that reaches it records 'timeout' status and returns what it has.

## `stepTimeoutSeconds` (type: `integer`):

Per-action timeout. Protects your budget from a single hung browser action (frozen page, network stuck) burning maxTotalChargeUsd. A stalled step is logged as an error and the agent tries a different approach. Default 45 s.

## `proxyGeo` (type: `string`):

ISO 3166-1 alpha-2 country code for geo-targeted browsing. Routes the browser through a residential proxy in this country. Leave empty for default routing.

## `authMode` (type: `string`):

Run inside an account you own. storageState: paste a session exported from your own logged-in browser. This Actor never accepts passwords.

## `storageState` (type: `object`):

Playwright storageState JSON ({cookies:\[…], origins:\[…]}) exported from YOUR OWN logged-in browser — context.storageState() or a cookie-export extension. Encrypted at rest; never written to logs, the step transcript, or the dataset.

## `authAttestation` (type: `boolean`):

Required when authMode is set. You confirm the account and site this run signs into are owned or controlled by you or your organization, that you are authorized to automate them, and that this run does not extract a third-party service's data through an account. Runs against accounts you do not own are prohibited.

## Actor input object example

```json
{
  "url": "https://example.com",
  "task": "Find the main heading and any links on the page. Return them as a JSON object with fields 'heading' and 'links'.",
  "outputSchema": {},
  "modelTier": "basic",
  "maxSteps": 100,
  "timeoutSeconds": 270,
  "stepTimeoutSeconds": 45,
  "proxyGeo": "",
  "authMode": "none",
  "authAttestation": false
}
```

# Actor output Schema

## `results` (type: `string`):

Dataset items for this run. Graceful aborts return an uncharged aborted record; migrations return no record and resume from the input-bound internal batch-progress checkpoint, cleared on completion or abort. An optional message offers feedback guidance on an unsuccessful task or empty answer; error retains the original failure explanation. evidence reports verbatim, normalized, ambiguous (one possible numeric reading matches), or not_found against visited pages.

## `screenshot` (type: `string`):

Full-page PNG of the final page state, written when the agent captures one. Use the dataset record's screenshotUrl for token-free access, including on Restricted accounts.

## `step_transcript` (type: `string`):

Capped full step transcript (JSON), written on incomplete runs after at least one step. Use the dataset record's stepTranscriptUrl for token-free access, including on Restricted accounts.

# 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 = {
    "url": "https://example.com",
    "task": "Find the main heading and any links on the page. Return them as a JSON object with fields 'heading' and 'links'.",
    "outputSchema": {},
    "modelTier": "basic",
    "maxSteps": 100,
    "timeoutSeconds": 270,
    "stepTimeoutSeconds": 45,
    "proxyGeo": ""
};

// Run the Actor and wait for it to finish
const run = await client.actor("shelvick/stealth-browser-agent").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 = {
    "url": "https://example.com",
    "task": "Find the main heading and any links on the page. Return them as a JSON object with fields 'heading' and 'links'.",
    "outputSchema": {},
    "modelTier": "basic",
    "maxSteps": 100,
    "timeoutSeconds": 270,
    "stepTimeoutSeconds": 45,
    "proxyGeo": "",
}

# Run the Actor and wait for it to finish
run = client.actor("shelvick/stealth-browser-agent").call(run_input=run_input)

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

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

```

## CLI example

```bash
echo '{
  "url": "https://example.com",
  "task": "Find the main heading and any links on the page. Return them as a JSON object with fields '\''heading'\'' and '\''links'\''.",
  "outputSchema": {},
  "modelTier": "basic",
  "maxSteps": 100,
  "timeoutSeconds": 270,
  "stepTimeoutSeconds": 45,
  "proxyGeo": ""
}' |
apify call shelvick/stealth-browser-agent --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,shelvick/stealth-browser-agent"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/5N7h2iK0C5d4Zd47R/builds/eNHUt3gzFtO7hkvut/openapi.json
