GPU Forge

Command-line tool

gpuforge is a small command-line tool for people and AI agents. It searches and rents machines, shows what they cost, opens SSH shells, manages your balance, and can call any API endpoint. It is a single Python file and needs only python3.

Install

Shell
curl -fsSL https://api.gpuforge.io/v1/cli/install.sh | sh

This downloads the tool to ~/.local/bin/gpuforge (set GPUFORGE_BIN_DIR to choose another folder) and tells you if that folder is not on your PATH. You can read the script first: curl -fsSL https://api.gpuforge.io/v1/cli/install.sh. The tool itself is served at https://api.gpuforge.io/v1/cli/gpuforge and contains no secrets.

Sign in

Shell
gpuforge login

The tool opens a page on the console with a short code. Check the code matches and press Approve while signed in. The tool then receives its own API key and saves it in ~/.config/gpuforge/config.json, readable only by you. Use --no-browser to print the link instead of opening it. A code expires after 10 minutes.

Shell
gpuforge whoami     # who is signed in
gpuforge logout     # revoke this computer's key and forget it

Configuration

SettingWhere
Config file~/.config/gpuforge/config.json (or the path in GPUFORGE_CONFIG)
API keySaved by gpuforge login, or the GPUFORGE_TOKEN environment variable (useful for agents and CI)
API addressSaved at login, or the GPUFORGE_URL environment variable

Commands

Add --json to any command for machine-readable output. Commands that cost money ask for confirmation first; add --yes (-y) to skip it once the user has agreed (an agent without a terminal must pass --yes, or the command is refused).

CommandWhat it doesCosts money
gpuforge login / logout / whoamiSign in, revoke this computer's key, show the accountNo
gpuforge search [options]Find machines that fitNo
gpuforge capsList your capabilitiesNo
gpuforge caps create spec.jsonCreate a capability from a JSON file {"name": ..., "spec": {...}} (your user_id is filled in)No
gpuforge rent --cap CAP [OFFER_ID]Rent a machine for a capability: the offer you name, or the cheapest that fitsYes
gpuforge status [POD]Your machines and what they cost right now, or one machine in detailNo
gpuforge ssh POD [COMMAND...]Open a shell on a machine, or run one commandNo
gpuforge stop PODDestroy a machine: billing stops and its disk is wipedNo
gpuforge billing [--usage HOURS]Balance, spend, cost now, card, top-up settings; with --usage, cost per machineNo
gpuforge topup AMOUNTAdd money from the card saved on the console (at least $5; within your daily cap)Yes
gpuforge api METHOD PATH [JSON or @file.json]Call any endpoint, e.g. gpuforge api GET /v1/catalogDepends

search options

OptionDescription
--gpus NGPUs on one machine, 1–8, exact (default 1)
--gpu NAMECard name contains, e.g. h200, b200, 4090
--familyOldest generation: ada_lovelace, hopper, blackwell
--vram GBMinimum memory per GPU
--ram GBMinimum system RAM
--disk GBDisk (default 50)
--max-price USDMost per hour for the whole machine
--providervast, runpod, nebius or jarvis
--cap CAPUse a capability's requirements instead
--allAlso show machines where something cannot be confirmed before boot
--limit NRows to show (default 20)

Offers expire 15 minutes after the search.

rent options

OptionDescription
--cap CAPThe capability to run (required)
OFFER_IDAn offer from search; omit it for the cheapest machine that fits
--nameA name for the machine
--max-price USDRefuse above this price per hour
--no-sshDo not add your public key. By default the first of ~/.ssh/id_ed25519.pub, id_ecdsa.pub, id_rsa.pub is added, so gpuforge ssh works

Before renting, the tool shows your balance and current spend; afterwards, how long the balance lasts at the new rate.

Examples

Shell
# Five H200s on one machine, cheapest first
gpuforge search --gpus 5 --gpu h200

# Save a capability and rent the cheapest machine for it
gpuforge caps create my-trainer.json
gpuforge rent --cap cap_ab12cd34

# Watch it, open a shell, stop it
gpuforge status
gpuforge ssh my-trainer-3f9a1c
gpuforge ssh my-trainer-3f9a1c nvidia-smi
gpuforge stop my-trainer-3f9a1c

# Money
gpuforge billing --usage 24
gpuforge topup 20

# Anything else
gpuforge api GET /v1/catalog
gpuforge api POST /v1/jobs @job.json

gpuforge ssh waits up to 3 minutes for SSH to be ready and pins the machine's host key, so the connection is verified.

Using the API from scripts and agents

Everything the CLI does is a plain HTTPS call; any language works.

  • Send Authorization: Bearer <key> on every call and Content-Type: application/json on every body.
  • Give each agent or project its own API key, so you can revoke it alone.
  • An agent can read a short guide written for it at GET https://api.gpuforge.io/v1/agent-guide (plain text, no key needed), and the full reference at GET https://api.gpuforge.io/v1/help.
  • Treat a machine as temporary: upload inputs after every start or wake, and download results at once.
  • Poll with care: GET /v1/jobs/{id}?wait=60 and GET /v1/fleet/{pod_id} every ~10 seconds are enough.
  • Read GET /v1/notifications?unread=true and pass the messages on to the person who owns the account.

A minimal Python job client

Python
import base64, json, os, urllib.request

BASE = os.environ["GPUFORGE_URL"]          # https://api.gpuforge.io
KEY = os.environ["GPUFORGE_TOKEN"]

def call(method, path, body=None):
    req = urllib.request.Request(
        BASE + path, method=method,
        data=json.dumps(body).encode() if body is not None else None,
        headers={"Authorization": f"Bearer {KEY}", "Content-Type": "application/json"})
    with urllib.request.urlopen(req, timeout=180) as r:
        return json.load(r)

job = call("POST", "/v1/jobs", {
    "program": os.environ["PROGRAM"],       # a program id from GET /v1/catalog
    "route": "/v1/generate",
    "input": {"prompt": "Full-color photograph of a red bicycle leaning on a brick wall, morning light."},
    "max_price_usd": 0.02,
})
while True:
    j = call("GET", f"/v1/jobs/{job['job_id']}?wait=60")
    if j["status"] in ("done", "failed"):
        break
if j["status"] == "done":
    open("bicycle.jpg", "wb").write(base64.b64decode(j["result"]["image"]))
    print("charged", j["price_usd"])
else:
    print("failed, not charged:", j["error"])

This page as Markdown, for tools and agents: /docs/cli.md