$GPUFARM IS LIVE
GPUFarm
Developers · SDK

TypeScript SDK

@gpufarm/sdk is a typed client for the REST API with zero runtime dependencies (it uses fetch and WebCrypto: Node 18.17+, Bun, Deno, edge runtimes). It mirrors the API's models field for field, keeps money in integer micro-dollars and token base units, and funds escrow from your own wallet through viem.

Install#

Not on the npm registry yet
The SDK ships in the GPUFarm repository under sdk/ (ESM + CommonJS builds with types). Build it with npm run sdk:build and install it from that path; viem is an optional peer dependency needed only for jobs.fund().
shell
npm run sdk:build                 # in the GPUFarm repository
npm install ./gpufarm/sdk viem    # in your project (viem only if you fund from code)
client.ts
import { GPUFarm } from "@gpufarm/sdk";

export const gpufarm = new GPUFarm({
  apiKey: process.env.GPUFARM_API_KEY,   // gf_live_… on mainnet, gf_test_… elsewhere — keep it server-side
  baseUrl: "https://robingpu.farm",                    // or set GPUFARM_API_URL
});

Create a job#

Only image and gpu are required. create runs the workload policy, prices the job on live capacity and locks its escrow terms. Nothing is charged yet. An Idempotency-Key is sent automatically, so retries never create duplicates.

ts
const job = await gpufarm.jobs.create({
  image: "pytorch/pytorch:2.5.1-cuda12.4-cudnn9-runtime",
  gpu: { minimumVramGb: 24, count: 1 },
  command: ["python", "-c", "import torch; print(torch.cuda.get_device_name())"],
  maxRuntimeSeconds: 900,
  maxPricePerGpuHour: 0.65,   // USD cap; omit to cap at the priciest current offer that fits (see job.defaults)
  paymentToken: "stable",     // "eth" | "stable" (USDG)
});

job.status;           // "CREATED"
job.estimate;         // low / high / max cost in micro-USD, with the assumptions
job.funding?.calls;   // the exact escrow calls your wallet will send

Server defaults when omitted: workloadType: "custom", maxRuntimeSeconds: 3600, no outbound network, outputs collected from /output. To try the network end to end, run the deterministic canary — its result is re-computed by the server (RESULT VERIFIED):

ts
await gpufarm.jobs.create({ image: "gpufarm/selftest:1", command: ["sha256-chain", "1000"], workloadType: "batch", gpu: { count: 1 } });

Fund the escrow with a viem wallet#

Funding is a transaction from your wallet to ComputeEscrow on Robinhood Chain; GPUFarm never holds your keys or funds. The wallet must be the account that owns the API key (the escrow job key is derived from it) — the SDK checks this before sending anything and switches the wallet's chain when the client supports it.

ts
import { createPublicClient, createWalletClient, http } from "viem";
import { privateKeyToAccount } from "viem/accounts";

const chain = { id: 4663, name: "Robinhood Chain", nativeCurrency: { name: "Ether", symbol: "ETH", decimals: 18 }, rpcUrls: { default: { http: [process.env.RPC_URL!] } } } as const;
const walletClient = createWalletClient({ account: privateKeyToAccount(process.env.CUSTOMER_KEY as `0x${string}`), chain, transport: http() });
const publicClient = createPublicClient({ chain, transport: http() });

const result = await gpufarm.jobs.fund(job, walletClient, { publicClient });
// approve (stablecoin only; skipped when the allowance already covers it) → createJob → wait for receipts
// → POST /v1/jobs/:id/fund-confirm, where GPUFarm reads the JobFunded event from the chain itself
result.transactions;              // [{ title, kind, hash }]
result.confirmation?.job.status;  // "QUEUED"

Prefer another signer (ethers, a hardware wallet, a multisig)? Send the raw calls yourself:

ts
const funding = await gpufarm.jobs.funding(job.id);
for (const call of funding.calls) {
  // send { to: call.to, data: call.data, value: BigInt(call.value) } from funding.signer, in order
}
await gpufarm.jobs.confirmFunding(job.id, fundTxHash);
Keep private keys and API keys out of browsers, repositories and job specs. In a web app, fund with the user's injected wallet client and call the API from your server.

Wait, logs and results#

ts
const done = await gpufarm.jobs.wait(job.id, {
  onStatus: (j) => console.log(j.status, j.assignment?.farm?.name ?? ""),
});

if (done.status === "COMPLETED") {
  done.verificationLevel;    // RESULT_VERIFIED | EXECUTION_VERIFIED | HOST_REPORTED — never assumed
  done.result.hashesMatch;   // host-reported hash === server re-hash
  done.cost.totalMicroUsd;   // final charge
  const { data, sha256 } = await gpufarm.artifacts.download(done.result.outputArtifactId!);
  // the download is checked against the hash GPUFarm recorded; a mismatch throws
}

for await (const line of gpufarm.jobs.followLogs(job.id)) {
  console.log(`[${line.stream}] ${line.line}`);
}
  • wait() polls with backoff (2 s growing to 15 s) and throws GPUFarmTimeoutError after 24 h by default — the job keeps running. Pass { signal } to stop waiting, or until to resolve on other statuses.
  • The job stays in escrow's challenge window after completion; approve early or dispute from the job page. See the trust model.

Capacity, pricing, billing#

ts
const offers  = await gpufarm.capacity.list({ model: "rtx-4090", minVramGb: 24, onlyAvailable: true, sort: "price_asc" });
const pricing = await gpufarm.pricing.get();         // live per-class min / median / max, fee, billing units
const billing = await gpufarm.billing.list({ limit: 50 });

capacity.list() and pricing.get() work without an API key; they read the same live data as the marketplace.

Method reference#

MethodAPIScope
jobs.create(spec, { idempotencyKey? })POST /v1/jobsjobs:create
jobs.get(id)GET /v1/jobs/:idjobs:read
jobs.list({ status, limit, cursor }) · jobs.listAll()GET /v1/jobsjobs:read
jobs.wait(id, opts)polls GET /v1/jobs/:idjobs:read
jobs.logs(id, opts) · jobs.followLogs(id)GET /v1/jobs/:id/logsjobs:read
jobs.funding(id)GET /v1/jobs/:id/fundingjobs:create
jobs.fund(job, walletClient, opts)funding + your wallet + fund-confirmjobs:create
jobs.confirmFunding(id, txHash)POST /v1/jobs/:id/fund-confirmjobs:create
jobs.cancel(id, { reason })POST /v1/jobs/:id/canceljobs:create
artifacts.get(id) · artifacts.download(id)GET /v1/artifacts/:idartifacts:read
capacity.list(filters)GET /v1/capacity—
pricing.get()GET /v1/pricing—
billing.list({ limit, cursor })GET /v1/billingbilling:read

Errors and retries#

Every API error is a GPUFarmError with status, a stable code, message, details and the server's requestId. Client-side codes cover the wallet path: network_error, timeout, wallet_mismatch, wrong_chain, integrity_error.

ts
import { GPUFarmError } from "@gpufarm/sdk";

try {
  await gpufarm.jobs.create({ image: "someone/thing:latest", gpu: { count: 1 } });
} catch (e) {
  if (e instanceof GPUFarmError && e.code === "policy_rejected") console.log(e.details); // the policy findings
}

GETs and job creation are retried automatically on network errors, 429 (honoring Retry-After) and 5xx — maxRetries defaults to 2. The full error list is in the API reference.