---
title: SDKs
description: TypeScript and Python over one API — installation, configuration, and where the two differ.
---

Two clients, one API. Each authenticates with an organization API key and speaks the same
vocabulary: machines, screens, keyboards and terminals. Neither depends on a model provider or
an agent framework, and neither can reach a route your own API key cannot.

Every page in this section shows both languages. Naming follows the host language —
`waitForDesktop` in TypeScript is `wait_for_desktop` in Python — and nothing else moves.

## Install

<CodeGroup>

```bash npm
npm install @raster/sdk
```

```bash uv
uv add raster
```

```bash pip
pip install raster
```

</CodeGroup>

Python is 3.11 or newer, and realtime is an optional extra: `pip install "raster[realtime]"`.
Most callers are agent loops that screenshot and click over ordinary HTTP and never open a
socket, so the websocket dependency is opt-in rather than paid for by everybody.

## Configure

| Variable          | Meaning                                                      |
| ----------------- | ------------------------------------------------------------ |
| `RASTER_API_KEY`  | an organization API key, created under Keys in the dashboard |
| `RASTER_BASE_URL` | where the API answers, **including** the `/v1` prefix        |

```bash
export RASTER_API_KEY=sk_...
export RASTER_BASE_URL=https://api.raster.sh/v1
```

There is deliberately no built-in default base URL. A wrong-but-plausible hostname baked into a
published SDK sends a customer's API key somewhere nobody chose, so the deployment names its own
API or [the constructor refuses](/docs/sdk/client).

## Hello, machine

<CodeGroup>

```ts TypeScript
import { Client } from "@raster/sdk";

const client = new Client();
const machine = await client.machines.create({ name: "research", size: "small" });
await machine.waitForDesktop();

await machine.browser.open("https://example.com");
const shot = await machine.screen.screenshot({ format: "png" });

await machine.close();
```

```python Python
from raster import Client

client = Client()

with client.machines.create(name="research", size="small") as machine:
    machine.wait_for_desktop()

    machine.browser.open("https://example.com")
    shot = machine.screen.screenshot(format="png")
```

</CodeGroup>

## The surface

<CardGroup cols={2}>
  <Card title="Client" href="/docs/sdk/client" icon="plug">
    Construction, options, resource groups, and the generated contract client.
  </Card>
  <Card title="Machines" href="/docs/sdk/machines" icon="server">
    Create, list, lifecycle, waiting, and the event log.
  </Card>
  <Card title="Screen" href="/docs/sdk/screen" icon="monitor">
    Screenshots, display geometry, and resizing.
  </Card>
  <Card title="Input" href="/docs/sdk/input" icon="mouse-pointer">
    Mouse, keyboard, clipboard, and the input lease.
  </Card>
  <Card title="Terminal" href="/docs/sdk/terminal" icon="terminal">
    One-shot commands as argv, and interactive ptys.
  </Card>
  <Card title="Files" href="/docs/sdk/files" icon="folder">
    Read, write, upload and download with an explicit encoding.
  </Card>
  <Card title="Browser" href="/docs/sdk/browser" icon="globe">
    Open a URL and enumerate tabs.
  </Card>
  <Card title="Ports" href="/docs/sdk/ports" icon="link">
    Publish a guest port at its own https hostname.
  </Card>
  <Card title="Secrets" href="/docs/sdk/secrets" icon="key">
    Store values that a captured disk never carries.
  </Card>
  <Card title="Snapshots" href="/docs/sdk/snapshots" icon="copy">
    Capture, restore, fork, and save as a template.
  </Card>
  <Card title="Streams" href="/docs/sdk/streams" icon="zap">
    Live frames and pty bytes over the gateway.
  </Card>
  <Card title="Tools" href="/docs/sdk/tools" icon="sparkles">
    The model-agnostic toolkit any agent loop can use.
  </Card>
  <Card title="Errors" href="/docs/sdk/errors" icon="alert-triangle">
    `RasterError`, retries, idempotency and pagination.
  </Card>
  <Card title="CLI" href="/docs/sdk/cli" icon="terminal">
    `raster` — the SDK with a terminal in front of it.
  </Card>
</CardGroup>

## Where the two differ

Both SDKs are the same design, but they are not identical. These are the gaps worth knowing
before you port code between them:

|                | TypeScript                           | Python                                        |
| -------------- | ------------------------------------ | --------------------------------------------- |
| Concurrency    | `async`/`await` throughout           | synchronous                                   |
| Responses      | typed objects and interfaces         | `TypedDict` — index with `[...]`              |
| Cleanup        | `await machine.close()`              | `with machine:` or `machine.close()`          |
| Lists          | `for await (… of …)` async iterables | ordinary iterators                            |
| `usage.series` | `client.usage.series(…)`             | not wrapped; use `client.api.usage.series(…)` |
| `error.quota`  | present on `quota_exceeded`          | not surfaced; read `error.message`            |
| Realtime       | included                             | `raster[realtime]` extra                      |
