Skip to content
Raster
Esc
navigateopen⌘Jpreview
On this page

Client

Construction, options, resource groups, and the generated contract client underneath.

import { Client } from "@raster/sdk";

const client = new Client();
from raster import Client

client = Client()

With no arguments the client reads RASTER_API_KEY and RASTER_BASE_URL from the environment. Every one is also a constructor argument.

Options

const client = new Client({
  baseUrl: "https://api.raster.sh/v1",
  apiKey: process.env.MY_KEY,
  timeoutMs: 30_000,
  maxRetries: 2,
  headers: { "x-team": "research" },
  fetch: myFetch,
});
client = Client(
    base_url="https://api.raster.sh/v1",
    api_key=os.environ["MY_KEY"],
    timeout=30.0,
    max_retries=2,
    headers={"x-team": "research"},
    http_client=my_httpx_client,
)
PropType
baseUrl?string

TypeScript `baseUrl` / Python `base_url`. Where the API answers, including the version prefix. Falls back to RASTER_BASE_URL. No built-in default: the constructor refuses rather than guessing a hostname to send your key to.

Typestring
apiKey?string

TypeScript `apiKey` / Python `api_key`. Falls back to RASTER_API_KEY.

Typestring
timeout?number

TypeScript `timeoutMs` (milliseconds) / Python `timeout` (seconds). Per attempt. A machine that is still booting is refused, not waited on — waiting is waitForDesktop's job.

Typenumber
Default30s
maxRetries?number

TypeScript `maxRetries` / Python `max_retries`. Only applies to requests that are safe to repeat.

Typenumber
Default2
headers?Record<string, string>

Sent on every request.

TypeRecord<string, string>
transport?fetch | httpx.Client

TypeScript `fetch` / Python `http_client`. Swap the transport, mostly for tests or a corporate proxy.

Typefetch | httpx.Client

No default base URL

Both constructors throw when they have neither an argument nor RASTER_BASE_URL:

invalid_request: no api url: pass `baseUrl` or set RASTER_BASE_URL

That is deliberate. A published SDK with a plausible hostname compiled into it will eventually send someone’s API key to a host nobody chose, and the failure is silent. Refusing is louder and cheaper.

Resource groups

Property What it holds
machines create, get, list
sessions list, close
templates list, get, create, delete
snapshots list, get, waitUntilReady, delete
plans list, get
billing get, current, checkout, portal
usage list, series, current
api the generated contract client
const plans = await client.plans.list();
const account = await client.billing.get();
const summary = await client.billing.current();

const url = await client.billing.checkout({ planId: "pro", successUrl });
const portal = await client.billing.portal();

for await (const record of client.usage.list({ periodStart, periodEnd })) {
  record.meter;
  record.quantity;
}
const series = await client.usage.series({ periodStart, periodEnd, meter: "egress_bytes" });
plans = client.plans.list()
account = client.billing.get()
summary = client.billing.current()

url = client.billing.checkout(plan_id="pro", success_url=success_url)
portal = client.billing.portal()

for record in client.usage.list(period_start=start, period_end=end):
    record["meter"], record["quantity"]

# not wrapped in python; the contract client is the same route.
series = client.api.usage.series(period_start=start, period_end=end, meter="egress_bytes")

See plans, usage and billing for what the numbers mean.

client.api

The generated contract client, built from the same oRPC contract the API implements and the OpenAPI document is generated from. Because it is generated, it cannot drift from the API.

It is public on purpose. A caller who needs a route the handwritten wrappers do not cover should reach it directly rather than fork the SDK:

const lease = await client.api.inputLeases.acquire({
  params: { machine_id: machine.id },
  body: { session_id, ttl_seconds: 60, force: true },
});
lease = client.api.input_leases.acquire(
    machine_id=machine.id,
    body={"session_id": session_id, "ttl_seconds": 60, "force": True},
)

Field names on client.api are the wire’s own snake_case and are passed through untouched. The wrapper layer above it is where they become the host language’s style.

Closing

The Python client owns an HTTP pool and is a context manager:

with Client() as client:
    ...

client.close() does the same by hand. The TypeScript client holds no pool of its own and needs no teardown — but a machine does, in both languages. See machines.

Was this page helpful?