Skip to main content
For AI agents: Copy the reference block below (~12k tokens) into your project as context for your coding agent (Claude, Cursor, Copilot, etc.), then prompt: “Help me integrate Yutori into my project.”
## Overview

[Yutori](https://yutori.com/) builds web agents that reliably and autonomously execute tasks on the web. There are four APIs:

| API | Description | Use case |
|-----|-------------|----------|
| **n1** | Pixels-to-actions LLM for browser control | Build your own browser agent with your own infra |
| **Browsing** | One-time cloud browser automation | Automate a browser task without managing infra |
| **Research** | Deep one-time web research | Research any topic across the web |
| **Scouting** | Continuous scheduled web monitoring | Monitor the web for changes on a schedule |

**Base URL:** `https://api.yutori.com`

**Authentication:** All requests require an API key via `X-API-Key` header or `Authorization: Bearer <key>`. Keys start with `yt-` and can be created at [platform.yutori.com/settings](https://platform.yutori.com/settings). Every new account gets **$5 in free credits**.

---

## REST API Reference

### n1 API — `POST /v1/chat/completions`

n1 is a pixels-to-actions LLM that processes screenshots and predicts browser actions. It follows OpenAI's Chat Completions interface.

**Models:** `n1-latest` (recommended), `n1-experimental`, `n1-20260203`

```python
from openai import OpenAI

client = OpenAI(
    base_url="https://api.yutori.com/v1",
    api_key="YOUR_API_KEY",
)

response = client.chat.completions.create(
    model="n1-latest",
    messages=[
        {
            "role": "user",
            "content": [
                {"type": "text", "text": "Search for Yutori on Google."},
                {"type": "image_url", "image_url": {"url": "data:image/webp;base64,..."}}
            ]
        }
    ]
)

# Response contains reasoning in content + actions in tool_calls
message = response.choices[0].message
print(message.content)      # "I can see the Google homepage. I'll click on the search bar."
print(message.tool_calls)   # [{"function": {"name": "left_click", "arguments": '{"coordinates": [640, 400]}'}}]
```

**n1 actions:** `left_click`, `double_click`, `triple_click`, `right_click`, `scroll`, `type`, `key_press`, `hover`, `drag`, `wait`, `goto_url`, `go_back`, `refresh`. Coordinates are in 1000x1000 space (must be converted to absolute). Custom tools can be added via the `tools` parameter.

**Screenshot requirements:** Browser content only (no OS chrome). Recommended resolution: 1280x800 (WXGA). Prefer WebP format.

**Pricing:** $0.75/M input tokens, $3/M output tokens.

---

### Browsing API

**Create a task:** `POST /v1/browsing/tasks`

```bash
curl -X POST https://api.yutori.com/v1/browsing/tasks \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "task": "Give me a list of all employees of Yutori.",
    "start_url": "https://yutori.com"
  }'
```

```python
import requests

response = requests.post(
    "https://api.yutori.com/v1/browsing/tasks",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={
        "task": "Give me a list of all employees of Yutori.",
        "start_url": "https://yutori.com",
        "max_steps": 75,                    # Optional (1-100, default 25)
        "output_schema": {...},             # Optional JSON schema for structured output
        "webhook_url": "https://...",       # Optional completion notification
        "webhook_format": "scout",          # Optional: "scout", "slack", "zapier"
    }
)
task_id = response.json()["task_id"]
```

**Get task status:** `GET /v1/browsing/tasks/{task_id}`

```python
result = requests.get(
    f"https://api.yutori.com/v1/browsing/tasks/{task_id}",
    headers={"X-API-Key": "YOUR_API_KEY"},
).json()
# result["status"]: "queued" | "running" | "succeeded" | "failed"
```

**Get task trajectory:** `GET /v1/browsing/tasks/{task_id}/trajectory` — returns screenshots and actions for each step.

**Agent options:** `navigator-n1-latest` (default, $0.015/step), `claude-sonnet-4-5-computer-use-2025-01-24` ($0.10/step). Set `require_auth: true` for login flows.

---

### Research API

**Create a task:** `POST /v1/research/tasks`

```python
response = requests.post(
    "https://api.yutori.com/v1/research/tasks",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={
        "query": "What are the latest developments in quantum computing?",
        "user_timezone": "America/Los_Angeles",  # Optional
        "user_location": "San Francisco, CA",    # Optional
        "output_schema": {...},                  # Optional JSON schema
        "webhook_url": "https://...",            # Optional
    }
)
task_id = response.json()["task_id"]
```

**Get task result:** `GET /v1/research/tasks/{task_id}`

```python
result = requests.get(
    f"https://api.yutori.com/v1/research/tasks/{task_id}",
    headers={"X-API-Key": "YOUR_API_KEY"},
).json()
# result["status"]: "queued" | "running" | "succeeded" | "failed"
```

**Pricing:** $0.35 per research task. Uses 100+ MCP tools for comprehensive research.

---

### Scouting API

**Create a scout:** `POST /v1/scouting/tasks`

```python
response = requests.post(
    "https://api.yutori.com/v1/scouting/tasks",
    headers={"X-API-Key": "YOUR_API_KEY"},
    json={
        "query": "Tell me about the latest news about Yutori",
        "output_interval": 86400,                # Seconds between runs (min 1800, default 86400)
        "user_timezone": "America/Los_Angeles",  # Optional
        "output_schema": {...},                  # Optional JSON schema
        "webhook_url": "https://...",            # Optional
        "webhook_format": "scout",               # "scout", "slack", "zapier"
        "skip_email": False,                     # Optional
    }
)
scout_id = response.json()["id"]
```

**Other endpoints:**

| Method | Endpoint | Description |
|--------|----------|-------------|
| `GET` | `/v1/scouting/tasks` | List scouts (params: `page_size`, `status`) |
| `GET` | `/v1/scouting/tasks/{id}` | Get scout details |
| `PATCH` | `/v1/scouting/tasks/{id}` | Update scout fields |
| `POST` | `/v1/scouting/tasks/{id}/pause` | Pause a scout |
| `POST` | `/v1/scouting/tasks/{id}/restart` | Resume a scout |
| `POST` | `/v1/scouting/tasks/{id}/complete` | Archive a scout |
| `DELETE` | `/v1/scouting/tasks/{id}` | Delete a scout |
| `GET` | `/v1/scouting/tasks/{id}/updates` | Get scout updates (params: `limit`, `cursor`) |

**Pricing:** $0.35 per scout-run.

---

## Python SDK

Install: `pip install yutori`

```python
from yutori import YutoriClient

client = YutoriClient(api_key="yt-...")  # Or set YUTORI_API_KEY env var, or run `yutori auth login`
```

**Auth resolution order:** explicit `api_key` param > `YUTORI_API_KEY` env var > `~/.yutori/config.json` (from CLI login).

### SDK Methods

**n1 (browser control):**
```python
response = client.chat.completions.create(
    model="n1-latest",
    messages=[{"role": "user", "content": [{"type": "text", "text": "..."}, {"type": "image_url", "image_url": {"url": "..."}}]}]
)
```

**Browsing:**
```python
task = client.browsing.create(task="...", start_url="https://...", max_steps=75, output_schema={...})
result = client.browsing.get(task["task_id"])
```

**Research:**
```python
task = client.research.create(query="...", output_schema={...})
result = client.research.get(task["task_id"])
```

**Scouting:**
```python
scout = client.scouts.create(query="...", output_interval=86400)
scouts = client.scouts.list(limit=20, status="active")
detail = client.scouts.get(scout_id)
updates = client.scouts.get_updates(scout_id, limit=10)
client.scouts.update(scout_id, status="paused")
client.scouts.delete(scout_id)
```

**Async client:** `AsyncYutoriClient` has the same interface — all methods are coroutines.

```python
from yutori import AsyncYutoriClient

async with AsyncYutoriClient() as client:
    task = await client.browsing.create(task="...", start_url="https://...")
```

**Error handling:**
```python
from yutori import APIError, AuthenticationError

try:
    client.get_usage()
except AuthenticationError as e:
    print(f"Invalid API key: {e}")
except APIError as e:
    print(f"API error (status {e.status_code}): {e.message}")
```

**CLI:**
```bash
yutori auth login                      # Authenticate via browser
yutori browse run "task" "url"         # Start browsing task
yutori browse get TASK_ID              # Get browsing result
yutori research run "query"            # Start research task
yutori research get TASK_ID            # Get research result
yutori scouts create -q "query"        # Create a scout
yutori scouts list                     # List scouts
yutori scouts get SCOUT_ID             # Get scout details
yutori scouts delete SCOUT_ID          # Delete a scout
```

Full SDK reference: [github.com/yutori-ai/yutori-sdk-python/blob/main/api.md](https://github.com/yutori-ai/yutori-sdk-python/blob/main/api.md)

---

## MCP Server

Install: `pip install yutori-mcp`

The [Yutori MCP server](https://github.com/yutori-ai/yutori-mcp) provides 10 tools for AI assistants (Claude, Cursor, VS Code, ChatGPT, Codex, etc.) to interact with the Yutori API.

### Available Tools

**Scout tools:** `list_scouts`, `get_scout_detail`, `create_scout`, `edit_scout`, `delete_scout`, `get_scout_updates`

**Research tools:** `run_research_task`, `get_research_task_result`

**Browsing tools:** `run_browsing_task`, `get_browsing_task_result`

All tools accept JSON parameters and return formatted text responses. Key parameters:

- `create_scout`: `query` (required), `output_interval`, `webhook_url`, `output_fields`, `user_timezone`, `skip_email`
- `run_research_task`: `query` (required), `user_timezone`, `output_fields`, `webhook_url`
- `run_browsing_task`: `task` (required), `start_url` (required), `max_steps`, `output_fields`, `webhook_url`

Full MCP tools reference: [github.com/yutori-ai/yutori-mcp/blob/main/TOOLS.md](https://github.com/yutori-ai/yutori-mcp/blob/main/TOOLS.md)