> ## Documentation Index
> Fetch the complete documentation index at: https://docs.notte.cc/llms.txt
> Use this file to discover all available pages before exploring further.

# Anything API

> Build and run web automations from a single API call

<Tip>
  STOP. Do not read past this section until you have read and followed [/quickstart.md](/quickstart.md).

  Goal: produce a reliable SDK automation script. The intended authoring flow is generate then edit, not write browser automation from scratch.

  Do not hand-write SDK code with element IDs, selectors, or form fields from memory. Element IDs, selectors, and field mappings must come from a live `observe()` call, CLI `notte page observe` output, or generated workflow code. Guessed browser targets fail on real pages.

  Use the Notte skill and CLI to inspect the live site, validate actions/selectors, handle auth/session state, and export workflow code with `notte sessions workflow-code` before using SDK docs or SDK code.

  SDK reference pages are for understanding, running, or editing generated workflow code. They are not the starting point for manually authoring the initial browser automation. SDK-first code is guesswork on real-world pages with dynamic selectors, auth state, CAPTCHAs, and anti-bot behavior.
</Tip>

The Anything API lets you describe a task in plain English, and Notte will build, deploy, and run a web automation function for you -- all through a single HTTP request. The response is an SSE stream so you can follow the agent's progress in real time.

<CardGroup cols={2}>
  <Card title="Try it now" icon="arrow-up-right-from-square" href="https://anything.notte.cc">
    Open the Anything API web app.
  </Card>

  <Card title="Get your API key" icon="key" href="https://console.notte.cc">
    Create an account on the Console to get started.
  </Card>
</CardGroup>

## Quick start

```bash theme={null}
curl -N -X POST https://anything.notte.cc/api/anything/start \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $NOTTE_API_KEY" \
  -d '{"query": "fetch the top 3 hacker news posts"}'
```

The API streams back SSE events as the agent works. When it's done, the final `done` event contains the `function_id` of the created function, which you can re-run later via the [Notte CLI](https://github.com/nottelabs/notte-cli) or [SDK](/quickstart).

## Request

<ParamField path="query" type="string" required>
  A natural-language description of the task you want automated.
</ParamField>

```json POST /api/anything/start theme={null}
{
  "query": "fetch the top 3 hacker news posts"
}
```

**Headers**

| Header          | Required | Description              |
| --------------- | -------- | ------------------------ |
| `Authorization` | Yes      | `Bearer <NOTTE_API_KEY>` |
| `Content-Type`  | Yes      | `application/json`       |

## Response

The response is a `text/event-stream`. Each line follows the [SSE spec](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events):

```
data: {"type":"<event_type>", ...}
```

### Event types

| Type                 | Description                        | Key fields                                   |
| -------------------- | ---------------------------------- | -------------------------------------------- |
| `status`             | Agent lifecycle updates            | `status`                                     |
| `thinking_delta`     | Agent reasoning (streamed)         | `delta`                                      |
| `text_delta`         | Agent response text (streamed)     | `delta`                                      |
| `tool_start`         | Agent started using a tool         | `name`                                       |
| `tool_result`        | Tool execution result              | `content`                                    |
| `message_complete`   | An assistant message was persisted | `role`                                       |
| `notte_session_data` | Browser session info               | `viewer_url`                                 |
| `function_uploaded`  | A Notte Function was created       | `function_id`, `function_name`, `created_at` |
| `done`               | Agent finished                     | `total_cost_usd`, `function_id`, `metadata`  |
| `error`              | Something went wrong               | `error`, `detail`                            |
| `timeout`            | Agent timed out                    | `reason`, `detail`                           |
| `cancelled`          | Agent was cancelled                | `reason`                                     |

### Terminal events

The stream ends after one of these events: `done`, `error`, `timeout`, or `cancelled`.

### Example: done event

```json theme={null}
{
  "type": "done",
  "metadata": {
    "thread_id": "820a7cff-613b-4529-9ba4-52c7a6777713"
  },
  "total_cost_usd": 0.43,
  "function_id": "d3c31289-f28b-49bd-a340-95e071cfef7e"
}
```

## Re-running the created function

Once the agent finishes, it typically creates a reusable **Notte Function**. Use the `function_id` from the `done` event to run it again:

<CodeGroup>
  ```bash CLI theme={null}
  notte functions run \
    --function-id d3c31289-f28b-49bd-a340-95e071cfef7e \
    --vars '{"count": "3"}' \
    -o json
  ```

  {/* @sniptest testers/anything-api/run_function.py */}

  ```python run_function.py theme={null}
  from notte_sdk import NotteClient

  client = NotteClient()
  result = client.functions.run(
      function_id="d3c31289-f28b-49bd-a340-95e071cfef7e",
      variables={"count": "3"},
  )
  print(result)
  ```
</CodeGroup>

## Consuming the SSE stream

### Python

{/* @sniptest testers/anything-api/consume_sse.py */}

```python consume_sse.py theme={null}
import os

import requests

NOTTE_API_KEY = os.environ["NOTTE_API_KEY"]

response = requests.post(
    "https://anything.notte.cc/api/anything/start",
    headers={
        "Authorization": f"Bearer {NOTTE_API_KEY}",
        "Content-Type": "application/json",
    },
    json={"query": "fetch the top 3 hacker news posts"},
    stream=True,
    timeout=(10, 300),
)
response.raise_for_status()

for line in response.iter_lines():
    if line:
        decoded = line.decode("utf-8")
        if decoded.startswith("data: "):
            import json

            event = json.loads(decoded[6:])
            print(event["type"], event)
            if event["type"] == "done":
                print("Function ID:", event.get("function_id"))
                print("Cost:", event.get("total_cost_usd"))
```

### TypeScript

```typescript theme={null}
const response = await fetch("https://anything.notte.cc/api/anything/start", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${NOTTE_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ query: "fetch the top 3 hacker news posts" }),
});

if (!response.ok) {
  throw new Error(`HTTP ${response.status}`);
}
if (!response.body) {
  throw new Error("ReadableStream not available");
}

const reader = response.body.getReader();
const decoder = new TextDecoder();
const terminal = new Set(["done", "error", "timeout", "cancelled"]);
let buffer = "";

while (true) {
  const { done, value } = await reader.read();
  if (done) break;

  buffer += decoder.decode(value, { stream: true });
  const lines = buffer.split("\n");
  buffer = lines.pop()!;

  for (const line of lines) {
    if (line.startsWith("data: ")) {
      const event = JSON.parse(line.slice(6));
      console.log(event.type, event);

      if (event.type === "done") {
        console.log("Function ID:", event.function_id);
        console.log("Cost:", event.total_cost_usd);
      }

      if (terminal.has(event.type)) {
        reader.cancel();
        break;
      }
    }
  }
}
```

## Error handling

| Status | Meaning                                         |
| ------ | ----------------------------------------------- |
| `401`  | Missing or invalid API key                      |
| `400`  | Missing `query` field or invalid JSON           |
| `502`  | Agent failed to start or SSE stream unavailable |

If the stream connects successfully but the agent encounters an error, you will receive an `error` event in the SSE stream instead of an HTTP error status.

## Pricing

Each request is billed based on the LLM usage incurred by the agent. The `total_cost_usd` field in the `done` event reports the exact cost for that run.
