TypeScript SDK
The @relaygrid/sdk client for calls, routed runs and the live log.
Not on npm yet
@relaygrid/sdk is not published yet. Until it is, call the REST API with fetch
(see the first call). This page documents the client as it
stands, so you know what is coming.
The SDK covers calls (one endpoint or a routed capability) and the live log. It runs anywhere with
fetch and web streams on the server: Node 20+, Bun, Deno and Workers. Never ship an API key to a
browser.
Create a client
import { createGrid } from "@relaygrid/sdk";
const grid = createGrid({ apiKey: process.env.GRID_API_KEY! });| Option | Default | |
|---|---|---|
apiKey | — | A key with call and run for calls, logs:read for the log |
baseUrl | https://api.gridrouter.io | |
fetch | global fetch | Inject your own (tests, proxies) |
userAgent | — | Sent as User-Agent |
app | — | Sent as X-Grid-App, so calls are attributed to your app in logs |
title | — | Sent as X-Title: your app's display name |
Run a capability
run lets GridRouter route: it tries the vendors for a capability in route order and stops at
the first hit. The body is the run request.
const result = await grid.run("people.email.find", {
input: { first_name: "Ada", last_name: "Lovelace", domain: "stripe.com" },
options: { cost: { max_cost_micro: 50_000 } },
});
console.log(result.provider, result.data);Call one endpoint
call pins a vendor endpoint. Async endpoints return a Job to poll instead of a result.
const res = await grid.call("hunter/email.find", {
input: { first_name: "Ada", last_name: "Lovelace", domain: "stripe.com" },
include_raw: true,
});Stream the live log
const tail = grid.logs.tail(
{
filter: { kinds: "call", capability: "people.email.find", status: "failed" },
onControl: (frame) => console.error(frame.kind),
onError: (err) => console.error(err),
},
(event) => {
if (event.kind === "call") console.log(event.row.call_id, event.row.outcome);
},
);
// later
tail.close();
await tail.done;
console.log(`dropped ${tail.dropped} events`);filter takes the same keys as the live tail.
tail streams over SSE, reconnects with backoff from 0.5 s to 10 s, asks for replay=100 on
reconnect and skips events it has already delivered. It stops on a 4xx other than 429 and reports
it to onError. Pass signal to stop with an AbortSignal, or reconnect: false to stop at the
first disconnect.
Read recent events
const { items } = await grid.logs.recent({ kinds: "call", limit: 20 });items is newest last, up to 1,000 (default 50).
Errors
Failed requests throw GridApiError, which carries the error envelope's code,
message and HTTP status.