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** | Computer-use model 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
### Browser-Use Model API — `POST /v1/chat/completions`
Computer-use models that process screenshots and predict browser actions. Follows OpenAI's Chat Completions interface.
**Models:** `n1.5-latest` (recommended), `n1-latest`, and [dated versions](/reference/browser-use).
```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.5-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]}'}}]
```
**Screenshot requirements:** Browser content only (no OS chrome). Recommended resolution: 1280x800 (WXGA). Prefer WebP format. Coordinates are in 1000x1000 space (must be converted to absolute).
**Pricing (n1):** $0.75/M input tokens, $3/M output tokens. n1.5 pricing TBD — see [pricing](/pricing).
---
### 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)
"require_auth": False, # Optional: true for login flows
"browser": "cloud", # Optional: "cloud" (default) or "local" for Yutori Local
"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. Set `browser: "local"` to use Yutori Local with the user's desktop sessions.
---
### 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
"browser": "cloud", # Optional: "cloud" (default) or "local"
"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.
---
### Usage API — `GET /v1/usage`
Get usage statistics over different time periods, active scouts, and rate limits.
```python
result = requests.get(
"https://api.yutori.com/v1/usage",
headers={"X-API-Key": "YOUR_API_KEY"},
params={"period": "7d"}, # Optional: "24h" (default), "7d", "30d", "90d"
).json()
# Returns: num_active_scouts, active_scout_ids, rate_limits, navigator_rate_limits, activity
# (n1_rate_limits and activity.n1_calls are deprecated aliases that will be removed in a future release)
```
---
## Python SDK
Install: `pip install yutori` (or `uv add yutori`). Requires Python 3.9+.
```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
**Browser-use model (`client.chat`)** — defaults to `n1.5-latest`. Pass `model="n1-latest"` for the older model.
```python
response = client.chat.completions.create(
messages=[{"role": "user", "content": [{"type": "text", "text": "..."}, {"type": "image_url", "image_url": {"url": "..."}}]}],
# n1.5-only options (all optional):
tool_set="browser_tools_expanded-20260403", # or "browser_tools_core-20260403" (default)
disable_tools=["hold_key", "drag"],
json_schema={...}, # Returned as response.parsed_json
)
```
Model/tool-set constants are importable from `yutori.navigator` (`N1_MODEL`, `N1_5_MODEL`, `TOOL_SET_CORE`, `TOOL_SET_EXPANDED`). For building your own agent loop, `yutori.navigator` also ships screenshot, coordinate, trimming, and key-mapping helpers.
**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") # or status="active" / "done"
client.scouts.delete(scout_id)
```
`output_schema` on `browsing.create` / `research.create` / `scouts.create` accepts a JSON Schema dict, a Pydantic v2 `BaseModel` class/instance (via `model_json_schema()`), or a Pydantic v1 class/instance (via `schema()`). Pydantic is not a hard dependency.
**Usage:**
```python
usage = client.get_usage(period="7d") # "24h" (default), "7d", "30d", "90d"
```
**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}") # 401/403 or no API key resolvable
except APIError as e:
print(f"API error (status {e.status_code}): {e.message}")
```
`client.chat` surfaces `openai.OpenAIError` subclasses (it wraps the OpenAI SDK), not `yutori` exceptions.
**CLI:**
```bash
yutori auth login # Authenticate via browser
yutori auth status # Show current auth status
yutori auth logout # Remove saved credentials
yutori browse run "task" "url" # Start browsing task (flags: --max-steps, --agent, --require-auth, --browser)
yutori browse get TASK_ID # Get browsing result
yutori research run "query" # Start research task (flags: --timezone/-tz, --location, --browser)
yutori research get TASK_ID # Get research result
yutori scouts create -q "query" # Create a scout (flags: --interval/-i hourly|daily|weekly, --timezone/-tz)
yutori scouts list # List scouts (flags: --limit, --status)
yutori scouts get SCOUT_ID # Get scout details
yutori scouts delete SCOUT_ID # Delete a scout (-f to skip confirmation)
yutori usage --period 7d # Show API usage statistics (24h | 7d | 30d | 90d)
```
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
Quick start:
1. `uvx yutori-mcp login` — opens Yutori Platform in your browser; saves your API key locally.
2. `npx add-mcp "uvx yutori-mcp"` — install the MCP server in your client (requires Node.js).
3. (Optional) `npx skills add yutori-ai/yutori-mcp` — install workflow skills like `/yutori-scout`.
4. Restart your MCP client.
The [Yutori MCP server](https://github.com/yutori-ai/yutori-mcp) provides 11 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`
**Usage tools:** `list_api_usage`
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`, `browser`, `output_fields`, `webhook_url`
- `run_browsing_task`: `task` (required), `start_url` (required), `max_steps`, `require_auth`, `browser`, `output_fields`, `webhook_url`
- `list_api_usage`: `period` (optional, `"24h"`, `"7d"`, `"30d"`, `"90d"`)
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)