# TinyFish (https://tenki.cloud/docs/sandbox/tinyfish)

> For the complete documentation index, see [llms.txt](https://tenki.cloud/llms.txt)

Start from Tenki's pre-built TinyFish image and drive TinyFish's web agent, remote browser, fetch, and search APIs.

Tenki's public `tenki/tinyfish:stable` image starts from the standard [`sandbox` base image](https://tenki.cloud/docs/sandbox/base-image.md) and adds the TinyFish CLI (the `tinyfish` command), the `@tiny-fish/sdk` TypeScript SDK, `playwright-core`, and the `tinyfish-smoke` command. It pins the CLI at 0.50.0 and the SDK at 0.8.0. It does not include a local browser or a TinyFish API key: TinyFish runs the browser on its side, and the CLI and SDK read `TINYFISH_API_KEY` from the session environment.

## TinyFish: web automation for agents

[TinyFish](https://docs.tinyfish.ai/) exposes four APIs, and the image ships a client for each:

* **Agent:** give a URL and a plain-English goal, and TinyFish's agent browses the page and returns JSON, optionally shaped by a JSON Schema. No selectors to write or maintain.
* **Browser:** a remote Chromium session with a CDP URL you drive from Playwright.
* **Fetch:** up to 10 URLs per call returned as markdown, HTML, or JSON, optionally with their links.
* **Search:** web search results, with optional location and language.

TinyFish also offers [Research](https://docs.tinyfish.ai/research-api), which turns a plain-language question into a cited report, and [Monitor](https://docs.tinyfish.ai/monitor), which re-checks a page or search topic on a schedule and reports what changed. The CLI manages monitors with `tinyfish monitor`; the image has no Research client, so call its REST API directly.

To give an AI assistant such as Claude Code or Cursor these tools without a sandbox, connect it to TinyFish's hosted [MCP server](https://docs.tinyfish.ai/mcp-integration) at `https://agent.tinyfish.ai/mcp`.

Your sandbox runs the code. TinyFish runs the browsers and agents. Credentials stay in the session environment and never land in the image or a template.

## Start a session

[Sign up for TinyFish](https://agent.tinyfish.ai/sign-up) if you don't have an account (TinyFish's [quick start](https://docs.tinyfish.ai/quick-start) walks through it), then create an [API key](https://agent.tinyfish.ai/api-keys). Keys start with `sk-tinyfish-`. Export the key locally, then pass it only to the session:

```bash
export TINYFISH_API_KEY=<your-tinyfish-api-key>

tenki sandbox create \
  --image tenki/tinyfish:stable \
  --env "TINYFISH_API_KEY=$TINYFISH_API_KEY" \
  --name tinyfish-demo
```

Pass the key only through `--env` when you create the session. Never put it in a template, image, or source file, where it would reach every session built from them. Snapshots taken from this session also contain the key. Session environment reaches `tenki sandbox exec` only, not SSH shells, so export the key yourself if you work over `tenki sandbox ssh`.

Run the built-in connectivity check:

```bash
tenki sandbox exec --session tinyfish-demo -- tinyfish-smoke https://example.com
```

The command uses all three clients against one remote browser session: the SDK creates it, Playwright loads the requested URL over CDP, and the CLI closes it. A successful result looks like:

```json
{ "session_id": "tf-...", "url": "https://example.com/", "title": "Example Domain" }
```

`tinyfish-smoke` exits `2` when `TINYFISH_API_KEY` is unset and `1` when any step fails, including an invalid key.

Terminate the Tenki session when you finish:

```bash
tenki sandbox terminate tinyfish-demo
```

## Use the CLI

The CLI reads `TINYFISH_API_KEY` from the environment, so no login step is needed. `tinyfish auth set` can store a key in `~/.tinyfish/config.json` instead; if a session has both, `TINYFISH_API_KEY` takes precedence over the stored key. Snapshots taken from this session also contain that key.

Every command prints JSON to stdout; add `--pretty` for a readable summary.

Run an agent with a URL and a goal:

```bash
tenki sandbox exec --session tinyfish-demo -- \
  tinyfish agent run "Return the title and points of the top 5 stories as JSON." \
  --url https://news.ycombinator.com --sync
```

With `--sync` the command waits for the run and prints one JSON object whose `result` field holds the agent's answer. Without it, the command streams progress events as JSON Lines, ending with a `COMPLETE` event. To fix the shape of the answer, pass a JSON Schema with `--output-schema-file`.

Fetch pages and search the web the same way:

```bash
tenki sandbox exec --session tinyfish-demo -- tinyfish fetch content get https://example.com --format markdown
tenki sandbox exec --session tinyfish-demo -- tinyfish search query "remote browser for AI agents"
```

## Use the SDK and Playwright in your code

`@tiny-fish/sdk` and `playwright-core` are installed under `/home/tenki/node_modules`, so JavaScript and TypeScript projects below `/home/tenki` can import them without installing another copy. The SDK is ESM-only: use an `.mjs` file or set `"type": "module"` in `package.json`.

Create a browser session with the SDK, connect to its CDP URL, and always close the remote session in a `finally` block. TinyFish bills a browser session until it is closed or reaches its inactivity timeout, and the SDK has no close method, so call the session endpoint directly:

```javascript

const session = await new TinyFish().browser.sessions.create({ timeout_seconds: 120 });
let browser;

try {
  browser = await chromium.connectOverCDP(session.cdp_url);
  const context = browser.contexts()[0] ?? (await browser.newContext());
  const page = context.pages()[0] ?? (await context.newPage());
  await page.goto("https://example.com");
  console.log(await page.title());
} finally {
  try {
    await browser?.close();
  } finally {
    await fetch(`https://api.browser.tinyfish.ai/${session.session_id}`, {
      method: "DELETE",
      headers: { "X-API-Key": process.env.TINYFISH_API_KEY },
    });
  }
}
```

`timeout_seconds` caps how long an idle session lives, so a session your code fails to close still ends quickly. The same client exposes `agent`, `fetch`, and `search`; [TinyFish's documentation](https://docs.tinyfish.ai/) covers each, along with browser profiles and proxy options.

If you pass `url` to `sessions.create`, the session returns before that first navigation finishes. Wait for the page to load before reading it.

The Python SDK is not preinstalled. Install it in the session with `pip install tinyfish` if you prefer Python.

## Telemetry

The `tinyfish` command sets `TINYFISH_NO_TELEMETRY=1` by default, which turns off the CLI's analytics and its update check. Set `TINYFISH_NO_TELEMETRY=0` in the session environment to turn them back on.

## Pin an exact build

`stable` moves to each newly published build. When a workload has to stay on the exact build it was tested against, resolve the tag once and launch the snapshot reference it returns:

```bash
tenki sandbox registry resolve tenki/tinyfish:stable
# image_id    : <image-id>
# snapshot_id : <snapshot-id>

tenki sandbox create --image tenki/tinyfish@<snapshot-id>
```

A snapshot reference never moves, so it keeps launching that build after `stable` advances.