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,
)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.
stringapiKey?string
TypeScript `apiKey` / Python `api_key`. Falls back to RASTER_API_KEY.
stringtimeout?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.
number30smaxRetries?number
TypeScript `maxRetries` / Python `max_retries`. Only applies to requests that are safe to repeat.
number2headers?Record<string, string>
Sent on every request.
Record<string, string>transport?fetch | httpx.Client
TypeScript `fetch` / Python `http_client`. Swap the transport, mostly for tests or a corporate proxy.
fetch | httpx.ClientNo 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.