ax-check.com
by Gauge
Y
Yutori
yutori.com
ax-check.com
Checked 18 September 2026
A
100/100
Agents need your login
Yutori's docs let agents install, auth, and price it instantly.
All 23 checklist items passed: quickstart gives concrete install/auth commands, and pricing is stated in full tables with units, no login required.
1 fix to start with
Concrete changes, backed by what we found.
Recorded coding sessions
Same task, 3 models, no yutori.com account. Recorded 18 September 2026 on Pi.
3 recorded · 3m 3s
3m 3s · recorded
Session findings
Select a finding to see it in context.
›Thinking…
I need to look up what Yutori is and understand its pricing model and developer experience, so let me check the repository and do some research.
✓$ ls /sandbox/repo && cat /sandbox/repo/README* 2>/dev/null | head -50✓└{"content":[{"text":"(no output)","type":"text"}]}
›Thinking…
The repo appears to be empty, so I need to figure out what Yutori is—possibly an AI or LLM provider. Since I don't have web access, let me try a quick curl request to see if I can gather information, and I'll check the environment variables to understand what tools are available.
✓$ ls -la /sandbox/repo; env | grep -i -E 'yutori|pi_|api' | head✓└{"content":[{"text":"total 16\ndrwxr-xr-x 3 root root 4096 Sep 18 08:00 .\ndrwxr-xr-x 7 root root 4096 Sep 18 21:42 ..\ndrwxr-xr-x 8 root root 4096 Sep 18 08:00…ctrl+o
{"content":[{"text":"total 16\ndrwxr-xr-x 3 root root 4096 Sep 18 08:00 .\ndrwxr-xr-x 7 root root 4096 Sep 18 21:42 ..\ndrwxr-xr-x 8 root root 4096 Sep 18 08:00 .git\n-rw-r--r-- 1 root root 408 Sep 18 08:00 .gitignore\nPI_CODING_AGENT=true\nPI_REASONING_LEVEL=medium\nPI_SESSION_FILE=/sandbox/.gauge/pi/sessions/2026-09-18T21-42-39-786Z_01a0b678-b7aa-7961-8ab3-dee294b96028.jsonl\nPI_PROVIDER=gauge-fireworks\nPI_MODEL=accounts/fireworks/models/qwen3p8-max\nPI_SESSION_ID=01a0b678-b7aa-7961-8ab3-dee294b96028\nPI_GATEWAY_API_KEY=unused\n","type":"text"}]}✓$ pip index versions yutori 2>&1 | head -5; npm view yutori 2>&1 | head -20✓└{"content":[{"text":"yutori (0.9.29)\nAvailable versions: 0.9.29, 0.9.28, 0.9.27, 0.9.26, 0.9.25, 0.9.24, 0.9.23, 0.9.22, 0.9.21, 0.9.20, 0.9.19, 0.9.18, 0.9.17…ctrl+o
{"content":[{"text":"yutori (0.9.29)\nAvailable versions: 0.9.29, 0.9.28, 0.9.27, 0.9.26, 0.9.25, 0.9.24, 0.9.23, 0.9.22, 0.9.21, 0.9.20, 0.9.19, 0.9.18, 0.9.17, 0.9.16, 0.9.15, 0.9.14, 0.9.13, 0.9.12, 0.9.11, 0.9.10, 0.9.9, 0.9.8, 0.9.7, 0.9.6, 0.9.5, 0.9.4, 0.9.3, 0.9.2, 0.9.1, 0.9.0, 0.8.1, 0.8.0, 0.7.9, 0.7.8, 0.7.7, 0.7.6, 0.7.5, 0.7.4, 0.7.3, 0.7.2, 0.7.1, 0.7.0, 0.6.1, 0.6.0, 0.5.0, 0.4.10, 0.4.9, 0.4.8, 0.4.7, 0.4.5, 0.4.4, 0.4.3, 0.4.2, 0.4.1, 0.4.0, 0.3.5, 0.3.4, 0.3.3, 0.3.2, 0.3.1, 0.3.0, 0.2.0, 0.1.0, 0.0.1\nnpm error code E404\nnpm error 404 Not Found - GET https://registry.npmjs.org/yutori - Not found\nnpm error 404\nnpm error 404 'yutori@*' is not in this registry.\nnpm error 404\nnpm error 404 Note that you can also install from a\nnpm error 404 tarball, folder, http url, or git url.\nnpm error A complete log of this run can be found in: /sandbox/.npm/_logs/2026-09-18T21_42_46_820Z-debug-0.log\n","type":"text"}]}›Thinking…
I'm checking out Yutori, a Python package at version 0.9.29, to install it and look at its documentation.
Yutori exists as a Python package. Let me install it and check its docs.
✓$ cd /tmp && pip install yutori -q 2>&1 | tail -3; pip show yutori | head -12✓└{"content":[{"text":"Name: yutori\nVersion: 0.9.29\nSummary: Official Python SDK for the Yutori API\nHome-page: https://yutori.com\nAuthor: \nAuthor-email: Yuto…ctrl+o
{"content":[{"text":"Name: yutori\nVersion: 0.9.29\nSummary: Official Python SDK for the Yutori API\nHome-page: https://yutori.com\nAuthor: \nAuthor-email: Yutori <support@yutori.com>\nLicense-Expression: Apache-2.0\nLocation: /opt/freestyle/python/lib/python3.12/site-packages\nRequires: httpx, openai, pillow, rich, typer\nRequired-by: \n","type":"text"}]}›Thinking…
I'm looking at the Yutori API's Python SDK to understand how to work with it — checking the package structure, README, and whether an API key is needed for authentication.
✓$ python -c "import yutori, os; print(os.path.dirname(yutori.__file__))" && ls /opt/freestyle/python/lib/python3.12/site-packages/yutori✓└{"content":[{"text":"/opt/freestyle/python/lib/python3.12/site-packages/yutori\n__init__.py\n__pycache__\n_async\n_fileutils.py\n_http.py\n_schema.py\n_sync\n_v…ctrl+o
{"content":[{"text":"/opt/freestyle/python/lib/python3.12/site-packages/yutori\n__init__.py\n__pycache__\n_async\n_fileutils.py\n_http.py\n_schema.py\n_sync\n_version.py\nasync_client.py\nauth\ncli\nclient.py\nconfig.py\nexceptions.py\nn1\nnavigator\npy.typed\n","type":"text"}]}✓$ pip download yutori --no-deps -d /tmp/yd -q 2>&1 | tail -1; ls /tmp/yd; cd /tmp/yd && unzip -o -q *.whl -d pkg 2>/dev/null; cat pkg/yutori-*/METADATA 2>/dev/null | head -100 || tar xf *.tar.gz && cat …✓└{"content":[{"text":"yutori-0.9.29-py3-none-any.whl\nMetadata-Version: 2.4\nName: yutori\nVersion: 0.9.29\nSummary: Official Python SDK for the Yutori API\nAuth…ctrl+o
pip download yutori --no-deps -d /tmp/yd -q 2>&1 | tail -1; ls /tmp/yd; cd /tmp/yd && unzip -o -q *.whl -d pkg 2>/dev/null; cat pkg/yutori-*/METADATA 2>/dev/null | head -100 || tar xf *.tar.gz && cat yutori-*/README* 2>/dev/null | head -100
{"content":[{"text":"yutori-0.9.29-py3-none-any.whl\nMetadata-Version: 2.4\nName: yutori\nVersion: 0.9.29\nSummary: Official Python SDK for the Yutori API\nAuthor-email: Yutori <support@yutori.com>\nLicense-Expression: Apache-2.0\nProject-URL: Homepage, https://yutori.com\nProject-URL: Documentation, https://docs.yutori.com\nProject-URL: Repository, https://github.com/yutori-ai/yutori-sdk-python\nProject-URL: Issues, https://github.com/yutori-ai/yutori-sdk-python/issues\nKeywords: yutori,sdk,api,automation,web-agent,browser,ai\nClassifier: Development Status :: 4 - Beta\nClassifier: Intended Audience :: Developers\nClassifier: Operating System :: OS Independent\nClassifier: Programming Language :: Python :: 3\nClassifier: Programming Language :: Python :: 3.10\nClassifier: Programming Language :: Python :: 3.11\nClassifier: Programming Language :: Python :: 3.12\nClassifier: Programming Language :: Python :: 3.13\nClassifier: Programming Language :: Python :: 3.14\nClassifier: Typing :: Typed\nRequires-Python: >=3.10\nDescription-Content-Type: text/markdown\nLicense-File: LICENSE\nRequires-Dist: httpx<1.0.0,>=0.26.0\nRequires-Dist: openai>=1.55.3\nRequires-Dist: pillow>=10.0.0\nRequires-Dist: typer>=0.9.0\nRequires-Dist: rich>=13.0.0\nProvides-Extra: dev\nRequires-Dist: pytest>=7.0; extra == \"dev\"\nRequires-Dist: pytest-asyncio>=0.21; extra == \"dev\"\nRequires-Dist: ruff>=0.1; extra == \"dev\"\nRequires-Dist: build>=1.2.0; extra == \"dev\"\nProvides-Extra: examples\nRequires-Dist: loguru>=0.7.0; extra == \"examples\"\nRequires-Dist: playwright>=1.40.0; extra == \"examples\"\nRequires-Dist: pydantic>=2.0.0; extra == \"examples\"\nRequires-Dist: tenacity>=9.0.0; extra == \"examples\"\nProvides-Extra: macos\nRequires-Dist: cua-driver==0.23.2; platform_system == \"Darwin\" and extra == \"macos\"\nDynamic: license-file\n\n# Yutori Python SDK & CLI\n\n[](https://pypi.org/project/yutori/)\n[](https://www.python.org/downloads/)\n\nThe official Python SDK and CLI for the [Yutori API](https://docs.yutori.com) — build agents that monitor, research, and browse the web, and operate computers with [Yutori](https://yutori.com/api).\n\nThe SDK offers sync and async clients with full type annotations, plus a `yutori` CLI for authentication and managing resources from the terminal.\n\n## AI agent install (recommended)\n\nPaste this into Claude Code, Codex, Cursor, Windsurf, or another coding agent:\n\n```text\nUse https://yutori.com/api/llms.txt and set up Yutori for me.\n```\n\n## Manual install\n\nOn macOS or Linux, the recommended setup is the one-line installer:\n\n```bash\ncurl -fsSL https://yutori.com/install.sh | bash\n```\n\nInstalls the global `yutori` CLI via `uv tool install` and prompts to add the SDK to your project, run `yutori auth login`, register the MCP server, install workflow skills, and verify with a browsing task.\n\nPython 3.10+ is required for the SDK.\n\n<details>\n<summary>Non-interactive install (CI, pipe, AI coding agent)</summary>\n\nThe SDK install, auth, and verification steps are skipped — auth needs a browser, verification needs an API key. MCP server and workflow skills install automatically without prompts.\n\nTo scope the MCP install to one coding agent, set `YUTORI_INSTALL_CLIENT=<slug>` (e.g. `claude-code`, `codex`, `cursor`). Unset, it registers for `claude-code`, `codex`, `cursor`, and `gemini-cli`. Run `npx add-mcp list-agents` for the full slug list.\n\n</details>\n\n<details>\n<summary>Uninstall the CLI later</summary>\n\n```bash\ncurl -fsSL https://yutori.com/uninstall.sh | bash\n```\n\nRemoves the global `yutori` CLI. Saved credentials at `~/.yutori/` are left in place so they survive reinstalls — `rm -rf ~/.yutori` manually if you want a clean slate. Set `YUTORI_UNINSTALL_ASSUME_YES=1` for scripted runs.\n\n</details>\n\n<details>\n<summary>Install the package manually</summary>\n\n```bash\npip install yutori\n```\n\nOr add it to an existing project with uv:\n","type":"text"}]}›Thinking…
I'm looking at the Yutori API documentation to understand what it offers—it's a hosted web agent service that uses browser-based authentication, but I'll need to check if there's an API key environment variable for non-interactive setup and review the pricing information.
I've identified Yutori as a hosted API for web-browsing AI agents with an official Python SDK. Now I'll pull their agent docs and check the authentication requirements.
✓$ curl -fsSL --max-time 20 https://yutori.com/api/llms.txt | head -120✓└{"content":[{"text":"# Yutori AI Agent Quickstart\n\nYou are helping a user set up Yutori from scratch. Yutori provides APIs and agent tools for:\n\n| Capabilit…ctrl+o
{"content":[{"text":"# Yutori AI Agent Quickstart\n\nYou are helping a user set up Yutori from scratch. Yutori provides APIs and agent tools for:\n\n| Capability | Use it for |\n| --- | --- |\n| Scout | Continuous web monitoring on a schedule |\n| Research | One-time deep web research |\n| Browsing | One-time browser automation |\n| Navigator | A computer-use model API for custom browser agents |\n\nPrioritize the CLI/MCP setup first. Do not ask the user to paste API keys into chat.\n\n## Setup\n\nDrive setup yourself. The user's only action is clicking **Allow** in a browser tab that opens during auth.\n\n1. Install the CLI, MCP server, and skills in one shot (you run this):\n\n ```bash\n curl -fsSL https://yutori.com/install.sh | YUTORI_INSTALL_CLIENT=<client> bash\n ```\n\n Substitute `<client>` with your agent's slug — common values: `claude-code`, `codex`, `cursor`, `vscode`, `claude-desktop`, `gemini-cli`, `zed`, `opencode` (run `npx add-mcp list-agents` for the full list). If `YUTORI_INSTALL_CLIENT` is unset the installer registers MCP for the default set (`claude-code`, `codex`, `cursor`, `gemini-cli`).\n\n In non-TTY mode the installer does the CLI install via `uv tool install yutori`, registers the Yutori MCP server for the chosen client(s), and installs the seven workflow skills (`yutori-scout`, `yutori-research`, `yutori-browse`, `yutori-computer-use`, `yutori-competitor-watch`, `yutori-api-monitor`, `yutori-login`) at user scope for the chosen client(s). Auth and verification are skipped — they need a browser and an API key (step 2 below).\n\n If `~/.local/bin` isn't on your PATH yet, invoke `yutori` via `$(uv tool dir --bin)/yutori` or run `export PATH=\"$(uv tool dir --bin):$PATH\"` for the rest of this session.\n\n2. Authenticate (you run this; **user clicks Allow**):\n\n ```bash\n yutori auth login\n ```\n\n **Always run this yourself first**, even on a headless host. The command calls `webbrowser.open(auth_url)`, binds `127.0.0.1` for the OAuth callback, and saves the API key to `~/.yutori/config.json`. The Bash call blocks for up to 5 minutes waiting for the user — if it times out, rerun.\n\n Two cases:\n - **Desktop / browser available**: the sign-in tab opens automatically. Tell the user: *\"I'm opening a Yutori sign-in tab — click Allow when it loads.\"*\n - **Headless host / no browser**: the command prints `Could not launch a browser` followed by an `auth_url`. **Relay that URL to the user** — they paste it into a browser on their own machine, click Allow, and the OAuth callback still completes through your Bash process. Don't skip running `yutori auth login` just because the host might be headless; try it and react to what it prints.\n\n3. Ask the user to restart this coding agent so the MCP tools and skills load.\n\n### Fallback: if step 1 didn't configure MCP or skills\n\nThe installer's status table will say `MCP server: SKIP` or `MCP skills: SKIP` if Node.js/npx wasn't on PATH. Run only the failed step(s):\n\n```bash\n# MCP server\nnpx add-mcp -y -g -n yutori -a <client> \"uvx yutori-mcp\"\n\n# Workflow skills\nnpx -y skills add yutori-ai/yutori-mcp -g -y -a <client>\n```\n\nIf `npx` isn't available, native CLIs work for some clients:\n- Claude Code: `claude mcp add --scope user yutori -- uvx yutori-mcp`\n- Codex: `codex mcp add yutori -- uvx yutori-mcp`\n\n**Fallback path:** if your Bash tool can't bind `127.0.0.1` for the auth callback (remote dev environment, sandboxed agent) or the user is on a different machine, ask the user to run `curl -fsSL https://yutori.com/install.sh | bash` in their own terminal — the interactive installer walks through CLI, auth, MCP, skills, and a verification task end-to-end.\n\n## Verify\n\n**You run these yourself** once the user confirms `yutori auth login` completed:\n\n```bash\nyutori auth status # confirms an API key is configured\nyutori usage # validates the key and shows daily quota / active scouts\n```\n\nIf both succeed, proceed to **Demonstrate** below. If MCP tools have already loaded in your session (i.e. the coding agent was restarted), `list_api_usage` is an equivalent MCP check.\n\n## Demonstrate\n\nAfter auth and verify succeed, run the three demos yourself via the `yutori` CLI in your Bash tool — **no coding-agent restart needed**. (After restart, the MCP tools `run_research_task`, `run_browsing_task`, `create_scout` and the `/yutori-*` skills also become available; use either path.)\n\nThe task APIs are async: `run` starts the task and returns a `task_id`; you then poll `get TASK_ID` until `status` is `succeeded` or `failed`. Examples below mirror the canonical queries at <https://docs.yutori.com/reference/> so demo and docs stay aligned.\n\n### Research (3–10 min)\n\n```bash\nyutori research run \"What are the latest developments in quantum computing from the past week? Include company announcements, research papers, and product releases.\"\n# returns task_id; poll until done\nyutori research get <task_id>\n```\n\nShow the user the returned summary once `status: succeeded`.\n\n### Browsing (30–120 sec)\n\n```bash\nyutori browse run \"Give me a list of all employees (names and titles) of Yutori.\" \"https://yutori.com\"\nyutori browse get <task_id>\n```\n\nConfirm with the user before running additional browsing tasks — each one spends credits.\n\n### Scout — draft only, **do not auto-create**\n\nScouts are *recurring* and spend credits on every scheduled run. Show the user the query you would use and ask for explicit confirmation before running:\n\n```bash\nyutori scouts create -q \"Tell me about the latest news, product updates, press releases, social media announcements, investments into, or other relevant information about Yutori\"\n```\n\nIf they decline, skip the Scout demo and end with a recap of Research + Browsing.\n\n## Navigator API\n\nNavigator is Yutori's visual-control model family. Navigator n1.5 (model id `n1.5-latest`) controls browsers. Navigator n2 (model id `n2`) controls a complete desktop through a CUA harness. Both use the **OpenAI Chat Completions-compatible endpoint**: you send a screenshot plus a task instruction, and the model returns actions as tool calls. You execute those actions, append the results, capture a fresh screenshot, and call again - an **agent loop** - until the model returns a text response with no tool calls.\n\nThere is no `yutori` CLI command for Navigator loops; call them from Python via `client.chat.completions.create(...)`, which is a drop-in OpenAI-compatible client. For n2, use Yutori MCP to drive a local Mac. To build your own n2 agent, use `examples/navigator_n2/` for local Docker or `examples/navigator_n2_daytona.py` for a separate third-party Daytona integration.\n\n### Minimal single call\n\n```python\nfrom yutori import YutoriClient\nfrom yutori.navigator import playwright_screenshot_to_data_url\nfrom playwright.sync_api import sync_playwright\n\n","type":"text"}]}✓$ grep -rn "YUTORI_API_KEY\|api_key" /opt/freestyle/python/lib/python3.12/site-packages/yutori/config.py | head; ls /opt/freestyle/python/lib/python3.12/site-packages/yutori/auth✓└{"content":[{"text":"__init__.py\n__pycache__\nconstants.py\ncredentials.py\nflow.py\ntypes.py\n","type":"text"}]}
›Thinking…
I'm realizing the authentication setup has a fundamental constraint here — it requires interactive browser-based OAuth with a local callback listener and waits up to 5 minutes for user approval, which directly conflicts with the instruction to avoid long-running commands and stop if the quickstart demands them. The thought cuts off while considering whether an API key alternative might work instead.
✓$ curl -fsSL --max-time 20 https://yutori.com/api/llms.txt | sed -n '120,260p'✓└{"content":[{"text":"\nwith YutoriClient() as client, sync_playwright() as p:\n browser = p.chromium.launch()\n page = browser.new_page()\n page.goto(\…ctrl+o
{"content":[{"text":"\nwith YutoriClient() as client, sync_playwright() as p:\n browser = p.chromium.launch()\n page = browser.new_page()\n page.goto(\"https://www.yutori.com\")\n\n image_url = playwright_screenshot_to_data_url(page)\n\n response = client.chat.completions.create(\n messages=[\n {\n \"role\": \"user\",\n \"content\": [\n {\"type\": \"text\", \"text\": \"List the team member names.\"},\n {\"type\": \"image_url\", \"image_url\": {\"url\": image_url}},\n ],\n }\n ],\n )\n\n message = response.choices[0].message\n print(message.content) # model's reasoning / final answer\n for tc in message.tool_calls or []:\n print(tc.function.name, tc.function.arguments)\n```\n\n### Building an agent loop\n\nIn practice you run a loop: execute the returned tool calls on your Playwright `page`, append the results as `tool` messages, capture a fresh screenshot, and call `create()` again until the model returns no tool calls. The `yutori.navigator` subpackage provides helpers for this — screenshot capture, coordinate denormalization (model uses a 1000×1000 space), message trimming, and key mapping.\n\nFor a complete working agent loop with retries, structured output, and expanded tools, see [`examples/navigator_n1_5.py`](https://github.com/yutori-ai/yutori-sdk-python/blob/main/examples/navigator_n1_5.py).\n\n### Navigator n1.5 parameters\n\nPass these as keyword args to `client.chat.completions.create(...)`:\n\n| Parameter | Purpose |\n|-----------|---------|\n| `model` | `\"n1.5-latest\"` (default) or a dated version like `\"n1.5-20260428\"`. |\n| `tool_set` | Built-in tool set: `\"browser_tools_core-20260403\"` (default — 18 coordinate-based tools) or `\"browser_tools_expanded-20260403\"` (adds `extract_elements`, `find`, `set_element_value`, `execute_js`). |\n| `disable_tools` | Remove specific tools by name, e.g. `[\"hold_key\", \"drag\"]`. |\n| `json_schema` | JSON Schema dict for structured output. Model returns conforming JSON; accessible as `response.parsed_json`. |\n| `temperature` | Sampling temperature (default 0.3). |\n\nThe core action space covers clicks, scroll, type, key press, drag, mouse move/down/up, navigation (`goto_url`, `go_back`, `go_forward`, `refresh`), `wait`, and `hold_key`. Coordinates are in a normalized 1000×1000 space — use `denormalize_coordinates(coords, width, height)` to map to viewport pixels. For the full action reference, see <https://docs.yutori.com/reference/n1-5>. For the SDK and CLI reference, see the link at the bottom of this file.\n\n### Navigator n2 (computer use)\n\nNavigator n2 operates a full desktop. `N2ComputerAgent` defaults to `model=\"n2\"` and to the pinned current tool set, `TOOL_SET_COMPUTER_USE_LATEST` (`\"computer_use_tools-20260830\"`); raw `client.chat.completions.create` calls should pass both explicitly. The set exposes `computer_batch`, `edit`, `read`, `write`, and `bash`. A batch holds up to 20 actions drawn from 15 GUI action types, runs sequentially against one observed frame, stops at its first error, and receives one screenshot result. n2 is non-streaming and rejects caller-provided `json_schema`, `response_format`, and non-auto `tool_choice`. It accepts `disable_tools` — only `bash`, `read`, `write`, and `edit` may be disabled (`computer_batch` is the GUI surface and cannot be; unknown names are rejected rather than ignored) — and `tools`: custom definitions in the standard OpenAI shape, appended after the set's, refused if their name shadows a tool the set already serves (disable the served tool first; `computer_batch` cannot be redefined). `N2ComputerAgent` implements only the set's tools, so custom tools need your own loop over `chat.completions.create`. Implement every tool of the set your harness serves (the model runs shell work through `bash` rather than a GUI terminal, and expects `read` on an image file to return the image). n2 trains with the full set and performs best with it — a reduced dated set should keep at least `computer_batch` and `bash`. A caller system message is appended under a `# User Instructions` header after the server's own prompt. Send the full conversation; the server keeps images only in the two newest image-bearing messages. Only `computer_batch` results carry a screenshot — after `bash`/file calls (and at run start) the model requests a fresh frame itself with a `screenshot` batch member. Long runs compact automatically by default (`compactor=\"auto\"` -> `N2InlineCompactor`), reproducing the trained long-horizon regime (64K context, same compaction prompt, 53,760-token trigger); pass `compactor=None` to instead run past the trained context and stop at the 128k serving limit with `stopped_by=\"context_limit\"`.\n\n- **Local Docker:** [`examples/navigator_n2/`](https://github.com/yutori-ai/yutori-sdk-python/tree/main/examples/navigator_n2) implements the full current tool set in a disposable local container.\n- **Third-party Daytona desktop:** [`examples/navigator_n2_daytona.py`](https://github.com/yutori-ai/yutori-sdk-python/blob/main/examples/navigator_n2_daytona.py) is a compact hosted example. The SDK runs the loop; a Yutori-maintained `DaytonaComputer` adapter and lifecycle wiring execute actions on Daytona. The script declares Python 3.10+, the Yutori SDK, and the tested Daytona version as inline dependencies, so run it with `uv run examples/navigator_n2_daytona.py \"<task>\"` plus a `DAYTONA_API_KEY`. The adapter serves the full current tool set (file tools via the SDK's `ShellFileToolsMixin`); [`examples/navigator_n2/cua_adapter.py`](https://github.com/yutori-ai/yutori-sdk-python/blob/main/examples/navigator_n2/cua_adapter.py) is the full-surface reference for tool implementations and result formats. Walkthrough: <https://docs.yutori.com/reference/n2-daytona>.\n- **Local Mac:** install Yutori MCP and run\n\n```bash\nuvx yutori-mcp computer-use setup\nuvx yutori-mcp computer-use run \"In Calculator, compute 17 * 23 and report the result.\" --app Calculator\n```\n\n`uvx yutori-mcp` then exposes the `run_computer_use_task` MCP tool on macOS. It controls the visible foreground desktop and sends what is on screen to Yutori, so run it while the user is not touching the Mac.\n\n`N2ComputerAgent` drives any adapter that implements the async handler surface — the GUI primitives plus `run_bash_command` and the file tools — exported for type-checking as the `yutori.navigator.N2Computer` protocol. Ownership: the loop implements `computer_batch` itself (coordinate mapping, sequencing, one post-batch screenshot) and calls only GUI primitives on the adapter; the `bash` and file-tool output contracts belong to the adapter — `ShellFileToolsMixin` provides the file tools over any sandbox shell with python3, and `format_shell_output` renders `bash` results. Exact handler signatures, error conventions, and output formats: the \"Navigator n2 loop\" section of the SDK reference (api.md, linked below), with `examples/navigator_n2/cua_adapter.py` as the full-surface reference implementation. `yutori.navigator.macos.MacOSComputer` is the native Mac adapter Yutori MCP uses (`pip install 'yutori[macos]'`); it encodes full-screen observations as compressed WebP and enforces the 10 MB request budget. Model reference: <https://docs.yutori.com/reference/n2>.\n\nFor SDK/API integration details, use:\n\n- Python SDK and CLI reference: https://github.com/yutori-ai/yutori-sdk-python/blob/main/api.md\n- MCP server and skill setup: https://github.com/yutori-ai/yutori-mcp\n- API docs index for agents: https://docs.yutori.com/llms.txt\n","type":"text"}]}✓$ env | grep -i YUTORI; ls ~/.yutori 2>/dev/null; grep -n "environ\|getenv" /opt/freestyle/python/lib/python3.12/site-packages/yutori/config.py | head✓└{"content":[{"text":"(no output)","type":"text"}]}
›Thinking…
I need to figure out the authentication setup for this sandbox environment. The docs require running yutori auth login which blocks for up to 5 minutes waiting for user interaction, but the user wants to avoid long-running commands. Let me check if there's a YUTORI_API_KEY environment variable that could bypass the browser-based auth flow.
✓$ grep -rn "YUTORI_API_KEY\|getenv\|environ" /opt/freestyle/python/lib/python3.12/site-packages/yutori/config.py /opt/freestyle/python/lib/python3.12/site-packages/yutori/client.py /opt/freestyle/python…✓└{"content":[{"text":"/opt/freestyle/python/lib/python3.12/site-packages/yutori/client.py:43: reads from the YUTORI_API_KEY environment variable.\…ctrl+o
grep -rn "YUTORI_API_KEY\|getenv\|environ" /opt/freestyle/python/lib/python3.12/site-packages/yutori/config.py /opt/freestyle/python/lib/python3.12/site-packages/yutori/client.py /opt/freestyle/python/lib/python3.12/site-packages/yutori/auth/*.py | head -20
{"content":[{"text":"/opt/freestyle/python/lib/python3.12/site-packages/yutori/client.py:43: reads from the YUTORI_API_KEY environment variable.\n/opt/freestyle/python/lib/python3.12/site-packages/yutori/client.py:48: AuthenticationError: If no API key is provided or found in environment.\n/opt/freestyle/python/lib/python3.12/site-packages/yutori/auth/constants.py:14:CLERK_INSTANCE_URL = os.environ.get(\"CLERK_INSTANCE_URL\", DEFAULT_CLERK_INSTANCE_URL)\n/opt/freestyle/python/lib/python3.12/site-packages/yutori/auth/constants.py:15:CLERK_CLIENT_ID = os.environ.get(\"CLERK_CLIENT_ID\", \"TGiyfoPbG01Sakpe\")\n/opt/freestyle/python/lib/python3.12/site-packages/yutori/auth/constants.py:16:CLERK_CONSENT_URL = os.environ.get(\"CLERK_CONSENT_URL\")\n/opt/freestyle/python/lib/python3.12/site-packages/yutori/auth/constants.py:17:AUTH_SIGN_IN_URL = os.environ.get(\"AUTH_SIGN_IN_URL\")\n/opt/freestyle/python/lib/python3.12/site-packages/yutori/auth/constants.py:39:AUTH_API_BASE_URL = sanitize_base_url(os.environ.get(\"YUTORI_API_BASE_URL\", DEFAULT_BASE_URL))\n/opt/freestyle/python/lib/python3.12/site-packages/yutori/auth/credentials.py:88: Order: explicit parameter > ``YUTORI_API_KEY`` env var > config file.\n/opt/freestyle/python/lib/python3.12/site-packages/yutori/auth/credentials.py:104: env_key = os.environ.get(\"YUTORI_API_KEY\")\n/opt/freestyle/python/lib/python3.12/site-packages/yutori/auth/credentials.py:118: Order: explicit parameter > YUTORI_API_KEY env var > config file.\n/opt/freestyle/python/lib/python3.12/site-packages/yutori/auth/credentials.py:135: raise AuthenticationError(\"No API key provided. Run 'yutori auth login', set YUTORI_API_KEY, or pass api_key.\")\n/opt/freestyle/python/lib/python3.12/site-packages/yutori/auth/flow.py:322: f\"'yutori auth login' again, or set YUTORI_API_KEY to a key from \"\n","type":"text"}]}✓$ curl -fsSL --max-time 20 https://docs.yutori.com/llms.txt 2>/dev/null | head -80✓└{"content":[{"text":"# Yutori API\n\n- [Overview](https://docs.yutori.com/index.md): Build reliable computer-use agents with the Yutori API\n- [Get API key](htt…ctrl+o
{"content":[{"text":"# Yutori API\n\n- [Overview](https://docs.yutori.com/index.md): Build reliable computer-use agents with the Yutori API\n- [Get API key](https://docs.yutori.com/authentication.md): Generate a key and make your first API request\n- [Quickstart for AI Agents](https://docs.yutori.com/llm-quickstart.md)\n- [Pricing](https://docs.yutori.com/pricing.md): Simple, transparent pricing for the Navigator, Browsing, Research, and Scouting APIs\n- [Health](https://docs.yutori.com/reference/health.md)\n- [Get Usage](https://docs.yutori.com/reference/usage.md): Get usage statistics over different time periods, active scouts, and rate limits.\n- [Get Started](https://docs.yutori.com/reference/navigator.md)\n- [Navigator n2 (Computer Use)](https://docs.yutori.com/reference/n2.md)\n- [Building agents with n2](https://docs.yutori.com/reference/n2-daytona.md): Step-by-step guide to build a Python agent loop that lets Navigator n2 control a Daytona sandbox\n- [Navigator n1.5 (Browser Use)](https://docs.yutori.com/reference/n1-5.md)\n- [Navigator Browser Extension](https://docs.yutori.com/reference/navigator-browser-extension.md): A lightweight Chrome extension powered by the Navigator API to automate browser tasks\n- [List Browsing Tasks](https://docs.yutori.com/reference/browsing-list.md): List browsing tasks for the authenticated user.\n- [Create A Task](https://docs.yutori.com/reference/browsing-create.md): Launches a website navigation agent to execute the task on a cloud browser.\n- [Get Status And Results](https://docs.yutori.com/reference/browsing-status.md): Returns the current status (queued, running, succeeded, failed) and any results if completed.\n- [Download Task Trajectory](https://docs.yutori.com/reference/browsing-trajectory.md): Downloads the trajectory for a completed browsing task.\n- [List Research Tasks](https://docs.yutori.com/reference/research-list.md): List research tasks for the authenticated user.\n- [Create A Task](https://docs.yutori.com/reference/research-create.md): Launches a one-time research task on the web.\n- [Get Status And Results](https://docs.yutori.com/reference/research-status.md): Returns the current status (queued, running, succeeded, failed) and any results if completed.\n- [List Scouts](https://docs.yutori.com/reference/scouts-list.md): List scouting tasks for this user.\n- [Get Scout](https://docs.yutori.com/reference/scout-get-detail.md): Get detailed information about a specific scout owned by this user.\n- [Create A Scout](https://docs.yutori.com/reference/scouts-create.md)\n- [Partially Update A Scout](https://docs.yutori.com/reference/scouts-patch.md): Update specific fields of an existing Scout. Only provided fields will be updated; omitted fields remain unchanged.\n- [Get Updates](https://docs.yutori.com/reference/scouts-updates.md)\n- [Update Email Settings](https://docs.yutori.com/reference/scouts-email-settings-update.md): Update email notification settings and manage subscribers for a scout.\n- [Mark Scout As Done](https://docs.yutori.com/reference/scouts-complete.md): Mark a scout as done. The scout will be archived and stop running.\n- [Delete Scout](https://docs.yutori.com/reference/scouts-delete.md)\n- [Restart Scout](https://docs.yutori.com/reference/scout-restart.md)\n- [Get started](https://docs.yutori.com/reference/webhook.md): Set up outbound webhooks.\n\n## OpenAPI Specs\n\n- [openapi](/openapi.json)\n\n## Optional\n\n- [Yutori](https://www.yutori.com)\n- [Playground](https://platform.yutori.com/navigator/playground)\n- [Yutori Local](https://yutori.com/local)\n- [Python SDK](https://github.com/yutori-ai/yutori-sdk-python)\n- [MCP](https://github.com/yutori-ai/yutori-mcp)\n","type":"text"}]}›Thinking…
I need to figure out how to handle authentication for the SDK. The user wants to avoid long-running commands and interactive workflows, so I'm checking if there's a way to use an API key from the environment or docs instead of going through the browser-based OAuth flow that requires waiting for user interaction.
✓$ curl -fsSL --max-time 20 https://docs.yutori.com/pricing.md | head -100✓└{"content":[{"text":"> ## Documentation Index\n> Fetch the complete documentation index at: https://docs.yutori.com/llms.txt\n> Use this file to discover all av…ctrl+o
{"content":[{"text":"> ## Documentation Index\n> Fetch the complete documentation index at: https://docs.yutori.com/llms.txt\n> Use this file to discover all available pages before exploring further.\n\n# Pricing\n\n> Simple, transparent pricing for the Navigator, Browsing, Research, and Scouting APIs\n\nEvery new account receives **\\$5 in free credits** to get started.\n\n## Pay-as-you-go\n\n<Tabs>\n <Tab title=\"Navigator n2\">\n **\\$0.50** per 1 million input tokens.\n\n **\\$0.05** per 1 million cached input tokens.\n\n **\\$4** per 1 million output or reasoning tokens.\n\n You are billed per token used. Costs are calculated continuously based on exact uncached input, cached input, output, and reasoning token counts across all Navigator n2 requests in a billing period.\n </Tab>\n\n <Tab title=\"Navigator n1.5\">\n **\\$1.50** per 1 million input tokens.\n\n **\\$5** per 1 million output tokens.\n\n You are billed per token used. Costs are calculated continuously based on exact token counts across all Navigator n1.5 requests in a billing period.\n </Tab>\n\n <Tab title=\"Browsing API\">\n | `agent` | Price per step |\n | ------------------------------------------- | -------------- |\n | `navigator-n1.5-latest` | **\\$0.015** |\n | `claude-sonnet-4-5-computer-use-2025-01-24` | **\\$0.10** |\n\n A *step* is a single interaction between the AI model and the browser (e.g., clicking a button, typing text, or scrolling). You control the maximum number of steps with the `max_steps` parameter.\n </Tab>\n\n <Tab title=\"Research API\">\n **\\$0.35** per research task\n\n A *research task* is a single execution of the full multi-agent system for one-time research queries. This uses the same infrastructure and pricing as the Scouting API but without recurring scheduling.\n </Tab>\n\n <Tab title=\"Scouting API\">\n **\\$0.35** per scout-run\n\n A *scout-run* is a single execution of your scouting task. You control the frequency (e.g., hourly, daily, weekly).\n </Tab>\n</Tabs>\n\n***\n\n## Enterprise\n\nFor enterprise or high-volume use cases, please [get in touch](mailto:api@yutori.com).\n\n* Custom workflows and integrations\n* Dedicated support and SLAs\n* Early access to new features\n\n***\n\n## Usage Tracking\n\nMonitor your API usage and costs:\n\n* Check your usage in [Settings](https://scouts.yutori.com/settings).\n* Use [`GET /usage`](/reference/usage) to programmatically track usage per API key.\n* View detailed logs for each browsing and scouting task.\n\n***\n\n## Typical use-cases and costs\n\n### Navigator n2\n\n**Example 1: 10,000,000 input tokens (2,000,000 cached) and 500,000 output or reasoning tokens in a month**\n\n* Input tokens: 8,000,000 uncached + 2,000,000 cached\n* Output or reasoning tokens: 500,000\n* Cost: 8,000,000 × \\$0.50 / 1,000,000 + 2,000,000 × \\$0.05 / 1,000,000 + 500,000 × \\$4.00 / 1,000,000 = **\\$6.10**\n\n### Navigator n1.5\n\n**Example 1: 10,000,000 input tokens and 500,000 output tokens in a month**\n\n* Input tokens: 10,000,000\n* Output tokens: 500,000\n* Cost: 10,000,000 × \\$1.50 / 1,000,000 + 500,000 × \\$5.00 / 1,000,000 = **\\$17.50**\n\n### Browsing API\n\n**Example 1: Simple data extraction**\n\n* Task: \"Extract the availability of all product variants from this product page\"\n* Average steps: \\~5 steps\n* Cost with `navigator-n1.5-latest`: 5 × \\$0.015 = **\\$0.075**\n","type":"text"}]}✓$ curl -fsSL --max-time 20 https://docs.yutori.com/authentication.md | head -60✓└{"content":[{"text":"> ## Documentation Index\n> Fetch the complete documentation index at: https://docs.yutori.com/llms.txt\n> Use this file to discover all av…ctrl+o
{"content":[{"text":"> ## Documentation Index\n> Fetch the complete documentation index at: https://docs.yutori.com/llms.txt\n> Use this file to discover all available pages before exploring further.\n\n# Get API key\n\n> Generate a key and make your first API request\n\n1. [Sign up here](https://platform.yutori.com/sign-up) to get started — every account comes with \\$5 in free credits.\n2. Navigate to [Billing](https://platform.yutori.com/billing), and click \"Set Up Billing\" to add your payment information.\n3. Open [platform.yutori.com](https://platform.yutori.com), then create a key from **Settings → API Keys**.\n4. Run the following [`GET /health`](/reference/health) request to confirm connectivity. `/health` is the only public endpoint and does not require an API key.\n ```bash Request theme={null}\n curl --request GET \\\n --url https://api.yutori.com/health\n ```\n5. Try [creating a Browsing task](/reference/browsing-create)!\n ```bash Request theme={null}\n curl --request POST \\\n --url https://api.yutori.com/v1/browsing/tasks \\\n --header 'X-API-Key: YOUR_API_KEY' \\\n --header 'Content-Type: application/json' \\\n --data '{\n \"task\": \"Give me a list of all employees (names and titles) of Yutori.\",\n \"start_url\": \"https://yutori.com\"\n }'\n ```\n\n<Note>\n Most authenticated API requests accept either header:\n\n * `X-API-Key: YOUR_API_KEY`\n * `Authorization: Bearer YOUR_API_KEY`\n\n **Exception — Navigator API (`POST /v1/chat/completions`)**: this endpoint accepts only `Authorization: Bearer YOUR_API_KEY`. `X-API-Key` is not accepted on chat completions, so standard OpenAI clients work unmodified.\n\n Header names are case-insensitive.\n</Note>\n","type":"text"}]}›Thinking…
I'm noticing there's no YUTORI_API_KEY in the environment, and authentication would normally require an interactive browser login that could take several minutes. The user's constraint against long-running commands means I need to avoid that approach, though there might be a way to relay an auth URL if the Bash tool can't bind to localhost.
Since there's no API key available, my best approach is to write a simple example file that demonstrates how to use the SDK, verify it's syntactically correct, and explain that actually running it requires the user to set up their YUTORI_API_KEY. I should also check the client API structure from the SDK to make sure the example is accurate.
I found pricing info but no API key in the environment—auth requires an interactive login that would block, so I'll write a runnable example you can execute once you have a key instead.
✓$ sed -n 1,120p /opt/freestyle/python/lib/python3.12/site-packages/yutori/client.py; ls /opt/freestyle/python/lib/python3.12/site-packages/yutori/_sync✓└{"content":[{"text":"\"\"\"Synchronous HTTP client for the Yutori SDK.\"\"\"\n\nfrom __future__ import annotations\n\nfrom types import TracebackType\nfrom typi…ctrl+o
{"content":[{"text":"\"\"\"Synchronous HTTP client for the Yutori SDK.\"\"\"\n\nfrom __future__ import annotations\n\nfrom types import TracebackType\nfrom typing import Any\n\nimport httpx\n\nfrom ._http import _SyncBaseNamespace, build_query_params\nfrom ._sync import BrowsingNamespace, ChatNamespace, ResearchNamespace, ScoutsNamespace\nfrom .auth.credentials import require_api_key\nfrom .config import DEFAULT_BASE_URL, DEFAULT_TIMEOUT_SECONDS, sanitize_base_url\n\n\nclass YutoriClient(_SyncBaseNamespace):\n \"\"\"Synchronous client for the Yutori API.\n\n Example:\n >>> from yutori import YutoriClient\n >>> client = YutoriClient(api_key=\"yt-...\")\n >>> print(client.get_usage())\n >>> print(client.scouts.list())\n\n The client provides namespaced access to different API areas:\n - client.scouts: Scout management (continuous monitoring)\n - client.browsing: Browser automation tasks\n - client.research: Deep web research tasks\n - client.chat: Navigator API (pixels-to-actions LLM)\n \"\"\"\n\n def __init__(\n self,\n api_key: str | None = None,\n *,\n base_url: str = DEFAULT_BASE_URL,\n timeout: float = DEFAULT_TIMEOUT_SECONDS,\n ) -> None:\n \"\"\"Initialize the Yutori client.\n\n Args:\n api_key: Your Yutori API key (starts with \"yt-\"). If not provided,\n reads from the YUTORI_API_KEY environment variable.\n base_url: API base URL (default: https://api.yutori.com/v1).\n timeout: Request timeout in seconds (default: 30).\n\n Raises:\n AuthenticationError: If no API key is provided or found in environment.\n \"\"\"\n self._api_key = require_api_key(api_key)\n base_url = sanitize_base_url(base_url)\n super().__init__(httpx.Client(timeout=timeout), base_url, self._api_key)\n\n # Initialize namespaces\n self.scouts = ScoutsNamespace(self._client, self._base_url, self._api_key)\n self.browsing = BrowsingNamespace(self._client, self._base_url, self._api_key)\n self.research = ResearchNamespace(self._client, self._base_url, self._api_key)\n self._timeout = timeout\n self._chat: ChatNamespace | None = None\n\n def get_usage(self, *, period: str | None = None) -> dict[str, Any]:\n \"\"\"Get usage statistics for your API key.\n\n Args:\n period: Time range for activity counts. One of \"24h\", \"7d\", \"30d\", \"90d\".\n Defaults to \"24h\" on the server.\n\n Returns:\n Dictionary with ``num_active_scouts``, ``active_scout_ids``,\n ``rate_limits``, ``navigator_rate_limits``, and ``activity``\n counts. The response may also include deprecated legacy aliases\n for navigator usage fields; prefer the ``navigator_*`` names.\n \"\"\"\n return self._request(\"get\", \"/usage\", params=build_query_params(period=period))\n\n @property\n def chat(self) -> ChatNamespace:\n \"\"\"Chat completions namespace, constructed lazily on first use.\n\n Building it eagerly would pay the OpenAI client construction cost\n (its own HTTP client and SSL context) on every YutoriClient, even\n for callers that never use chat completions.\n \"\"\"\n if self._chat is None:\n self._chat = ChatNamespace(self._base_url, self._api_key, self._timeout)\n return self._chat\n\n def close(self) -> None:\n \"\"\"Release the underlying HTTP client resources.\"\"\"\n try:\n self._client.close()\n finally:\n # Close the chat client (if ever built) even if the HTTP client\n # close fails.\n if self._chat is not None:\n self._chat.close()\n\n def __enter__(self) -> YutoriClient:\n return self\n\n def __exit__(\n self,\n exc_type: type[BaseException] | None,\n exc: BaseException | None,\n traceback: TracebackType | None,\n ) -> None:\n self.close()\n__init__.py\n__pycache__\nbrowsing.py\nchat.py\nresearch.py\nscouts.py\n","type":"text"}]}›Thinking…
I need to work around the authentication constraint — there's no API key available and the user doesn't want long-running interactive processes. Instead of blocking on yutori auth login, I'll create a simple example script that accepts the API key through an environment variable, then verify the API is reachable with a quick health check.
The SDK is available on PyPI with pricing info in the docs. I'll write an example script and verify the public health endpoint since I don't have an API key to run authenticated calls.
✓$ curl -fsS --max-time 15 https://api.yutori.com/health✓└{"content":[{"text":"{\"status\":\"ok\"}","type":"text"}]}
✓$ grep -n "def run\|def get\|def list" /opt/freestyle/python/lib/python3.12/site-packages/yutori/_sync/browsing.py | head✓└{"content":[{"text":"13: def list(\n85: def get(self, task_id: str) -> dict[str, Any]:\n","type":"text"}]}
›Thinking…
I'm checking the sync browsing module to see how tasks are created, since the run method doesn't seem to be available there.
✓$ grep -n "def " /opt/freestyle/python/lib/python3.12/site-packages/yutori/_sync/browsing.py /opt/freestyle/python/lib/python3.12/site-packages/yutori/_sync/research.py✓└{"content":[{"text":"/opt/freestyle/python/lib/python3.12/site-packages/yutori/_sync/browsing.py:13: def list(\n/opt/freestyle/python/lib/python3.12/site-pac…ctrl+o
{"content":[{"text":"/opt/freestyle/python/lib/python3.12/site-packages/yutori/_sync/browsing.py:13: def list(\n/opt/freestyle/python/lib/python3.12/site-packages/yutori/_sync/browsing.py:42: def create(\n/opt/freestyle/python/lib/python3.12/site-packages/yutori/_sync/browsing.py:85: def get(self, task_id: str) -> dict[str, Any]:\n/opt/freestyle/python/lib/python3.12/site-packages/yutori/_sync/research.py:13: def list(\n/opt/freestyle/python/lib/python3.12/site-packages/yutori/_sync/research.py:42: def create(\n/opt/freestyle/python/lib/python3.12/site-packages/yutori/_sync/research.py:77: def get(self, task_id: str) -> dict[str, Any]:\n","type":"text"}]}›Thinking…
Since I can't actually run the API calls without credentials, I'll create a lightweight example file that demonstrates how to use the service and explain the authentication requirement upfront, along with the pricing and DX considerations.
I've confirmed Yutori is a real hosted product with a Python SDK on PyPI and a working health endpoint, but authentication is blocked without an API key. I'll write a complete example file that you can run once you have your credentials.
✓$ sed -n 42,84p /opt/freestyle/python/lib/python3.12/site-packages/yutori/_sync/browsing.py✓└{"content":[{"text":" def create(\n self,\n task: str,\n start_url: str,\n *,\n max_steps: int | None = None,\n age…ctrl+o
{"content":[{"text":" def create(\n self,\n task: str,\n start_url: str,\n *,\n max_steps: int | None = None,\n agent: str | None = None,\n require_auth: bool | None = None,\n browser: str | None = None,\n output_schema: object | None = None,\n webhook_url: str | None = None,\n webhook_format: str | None = None,\n ) -> dict[str, Any]:\n \"\"\"Create a browser automation task.\n\n Args:\n task: Natural language description of the browsing task.\n start_url: URL to start browsing from.\n max_steps: Maximum agent steps.\n agent: Optional agent/model override for the browsing task.\n require_auth: Use auth-optimized browser for login flows.\n browser: \"cloud\" (default) or \"local\" to use the desktop app with\n the user's logged-in sessions.\n output_schema: JSON schema dict, a Pydantic BaseModel class, or a BaseModel instance.\n webhook_url: URL for completion notifications.\n webhook_format: \"scout\" (default), \"slack\", or \"zapier\".\n\n Returns:\n Dictionary containing task details including task_id.\n \"\"\"\n payload = build_payload_with_schema(\n task=task,\n start_url=start_url,\n max_steps=max_steps,\n agent=agent,\n require_auth=require_auth,\n browser=browser,\n output_schema=output_schema,\n webhook_url=webhook_url,\n webhook_format=webhook_format,\n )\n return self._request(\"post\", \"/browsing/tasks\", json=payload)\n\n","type":"text"}]}›Thinking…
I don't have the YUTORI_API_KEY environment variable set, so I can't actually execute an authenticated API call. The user wants me to keep things lightweight and avoid long-running processes, so rather than attempting the browser-based OAuth flow that could hang for minutes, I should write a simple example script that demonstrates how to use the SDK once they provide their API key.
The API requires authentication to run tasks, and I can't authenticate without browser interaction. I'll write the example file so it's ready to run once you have a key.
✓write /sandbox/repo/yutori_example.py✓└{"content":[{"text":"Successfully wrote 1635 bytes to /sandbox/repo/yutori_example.py","type":"text"}]}
›Thinking…
I need to verify the code compiles and handle the authentication issue—there's no API key in the environment, so I can't actually execute a task. The auth flow requires browser-based login with a callback that blocks for several minutes, which conflicts with the constraint against long-running commands. The health endpoint is working, so I'll report that the example is written but blocked on authentication credentials.
✓$ cd /sandbox/repo && python -m py_compile yutori_example.py && python -c "✓└{"content":[{"text":"AuthenticationError No API key provided. Run 'yutori auth login', set YUTORI_API_KEY, or pass api_key.\n","type":"text"}]}ctrl+o
cd /sandbox/repo && python -m py_compile yutori_example.py && python -c "
from yutori import YutoriClient
try:
YutoriClient()
except Exception as e:
print(type(e).__name__, e)
"
›Thinking…
I'm blocked by authentication. The quickstart says to run yutori auth login, which blocks for up to 5 minutes waiting for the user to click "Approve" in the browser. That's a long-running, interactive command, and per the user's instructions I'm not supposed to wait on long-running commands. Also, the YUTORI_API_KEY environment variable isn't set. The fallback per the docs is to have the user themselves run the installer.
Per the instructions: "Let me know if you get blocked." So I should report as follows: the SDK and samples are already built, the health check works, but I'm blocked at the auth stage—I need to have the user either provide an API key or interactively run yutori auth login. This is technically an actionable developer workflow (the SDK exists), it's just blocked waiting on credentials. I should stop here and report.
Done as far as I can go without credentials. Summary:
What Yutori is
Yutori is a hosted API for web agents: Browsing (cloud-browser automation), Research (one-shot deep research), Scouts (scheduled monitoring), and Navigator (OpenAI-compatible computer-use models, n1.5/n2). Official Python SDK on PyPI (pip install yutori), API at api.yutori.com. I verified the service is reachable (GET /health → {"status":"ok"}) and installed the SDK (v0.9.29).
Example built
/sandbox/repo/yutori_example.py — a minimal, typed, sync-SDK script that:
- Checks usage/rate limits via
client.get_usage() - Creates a browsing task (
client.browsing.create(...)withmax_steps=10cost cap) - Polls
client.browsing.get(task_id)untilsucceeded/failedand prints the result
It compiles cleanly and uses only the hosted API — no local stacks, no long-running processes.
Pricing (pay-as-you-go, $5 free credits on signup)
| Product | Price |
|---|---|
| Browsing API | $0.015/step (navigator-n1.5 agent) or $0.10/step (Claude Sonnet computer-use) |
| Research API | $0.35 per research task |
| Scouting API | $0.35 per scout-run (recurring — burns credits every scheduled run) |
| Navigator n1.5 | $1.50 / $5 per M input/output tokens |
| Navigator n2 | $0.50 input ($0.05 cached) / $4 output+reasoning per M tokens |
The example script costs at most ~$0.15 (10 steps × $0.015).
Blocked — here's where I stopped
I can't actually run the example: there's no YUTORI_API_KEY in this environment, and the only auth path is yutori auth login, which opens a browser OAuth flow and blocks up to 5 minutes waiting for you to click "Allow" — that's an interactive, long-running command, which your instructions rule out. To unblock, either:
- Set
YUTORI_API_KEY(create one at platform.yutori.com → Settings → API Keys), and I'll run the example, or - Run
yutori auth loginyourself in your own terminal (it saves the key to~/.yutori/config.json, which the SDK picks up automatically).
Manageability (brief)
Yes, easy. Clean pip-installable SDK with sync/async clients, full type annotations, an agent-oriented llms.txt quickstart, simple poll-until-done task APIs, and transparent per-unit pricing with a max_steps cost cap and a GET /usage endpoint. The only friction is the browser-based OAuth (no headless key-only signup path), and Scouts are recurring spenders that need care not to auto-create.
AX Fundamentals
23 checks. Open an area for details.
This check is a quick preview. Test more tasks and models with Gauge Agents.
Test your product with Gauge Agents