VRAMfarm API
Starten Sie GPU-Server aus Skripten und CI: freie Maschine finden, mit einer fertigen App oder Ihrem eigenen Docker-Image starten, auf SSH warten, stoppen. Einfaches HTTPS und JSON, kein SDK nötig.
https://vramfarm.com/api- Antworten kommen als JSON in UTF-8. POST-Anfragen brauchen den Header
Content-Type: application/json. - Beträge sind in Euro.
priceist der Stundenpreis für den ganzen Server inklusive aller Karten, als Dezimalzahl mit Cent (zum Beispiel0.39). - Zeitangaben in Antworten sind ISO 8601 in UTC; das Feld
updatedim Katalog ist in Unix-Millisekunden. - Fehlermeldungen kommen in der Sprache Ihres Kontos oder in der Sprache aus dem Header
Accept-Language, ersatzweise auf Englisch.
Schnellstart
Das Skript startet einen Server mit RTX 4090, wartet, bis er bereit ist, gibt den SSH-Befehl aus und stoppt den Server wieder, auch wenn unterwegs etwas schiefgeht.
import os, time, requests
API = "https://vramfarm.com/api"
s = requests.Session()
s.headers["Authorization"] = f"Bearer {os.environ['GF_TOKEN']}"
r = s.post(f"{API}/instances", json={"gpu": "rtx-4090", "count": 1, "template": "pytorch"})
r.raise_for_status()
sid = r.json()["id"]
try:
while True:
srv = s.get(f"{API}/instances/{sid}").json()
if srv["status"] == "running":
break
if srv["status"] == "stopped":
raise RuntimeError(srv["error"])
print(srv["stage_note"] or srv["stage"])
time.sleep(10)
print(srv["access"]["ssh"]["command"])
# your work goes here: ssh, scp, requests to the app on the server
finally:
s.post(f"{API}/instances/stop", json={"id": sid})// Node.js 18+, .mjs file
const API = "https://vramfarm.com/api";
const headers = { Authorization: `Bearer ${process.env.GF_TOKEN}`, "Content-Type": "application/json" };
const call = async (path, body) => {
const r = await fetch(API + path, body ? { method: "POST", headers, body: JSON.stringify(body) } : { headers });
const data = await r.json();
if (!r.ok) throw new Error(`${r.status} ${data.code}: ${data.error}`);
return data;
};
const { id } = await call("/instances", { gpu: "rtx-4090", count: 1, template: "pytorch" });
try {
let srv;
while ((srv = await call(`/instances/${id}`)).status !== "running") {
if (srv.status === "stopped") throw new Error(srv.error);
console.log(srv.stage_note ?? srv.stage);
await new Promise((ok) => setTimeout(ok, 10_000));
}
console.log(srv.access.ssh.command);
} finally {
await call("/instances/stop", { id });
}#!/usr/bin/env bash
# needs curl and jq
set -euo pipefail
API=https://vramfarm.com/api; H="Authorization: Bearer $GF_TOKEN"; J="Content-Type: application/json"
ID=$(curl -sf "$API/instances" -H "$H" -H "$J" \
-d '{"gpu":"rtx-4090","count":1,"template":"pytorch"}' | jq -r .id)
trap 'curl -sf "$API/instances/stop" -H "$H" -H "$J" -d "{\"id\":$ID}" >/dev/null' EXIT
for i in $(seq 1 120); do
S=$(curl -sf "$API/instances/$ID" -H "$H")
ST=$(jq -r .status <<<"$S")
[ "$ST" = running ] && break
[ "$ST" = stopped ] && { echo "failed to start: $(jq -r .error <<<"$S")"; exit 1; }
echo " $(jq -r '.stage_note // .stage' <<<"$S")"; sleep 10
done
SSH=$(jq -r .access.ssh.command <<<"$S")
$SSH -o StrictHostKeyChecking=accept-new 'cd /workspace && python train.py'Authentifizierung
Erstellen Sie einen Token im Dashboard → Einstellungen → API-Tokens. Ein Token sieht aus wie gf_live_… und wird nur einmal angezeigt: Legen Sie ihn in den Secrets Ihrer CI oder in der Umgebungsvariable GF_TOKEN ab. Am selben Ort können Sie Tokens widerrufen. Bis zu 10 Tokens können gleichzeitig aktiv sein.
Senden Sie den Token im Header Authorization: Bearer gf_live_…. Mehr ist nicht nötig: Anfragen werden nicht signiert, es gibt keinen Zeitstempel und kein recvWindow, und bei Anfragen mit Token werden Cookies und Origin nicht geprüft.
import os, requests
H = {"Authorization": f"Bearer {os.environ['GF_TOKEN']}"}
print(requests.get("https://vramfarm.com/api/account", headers=H).json())const H = { Authorization: `Bearer ${process.env.GF_TOKEN}` };
console.log(await fetch("https://vramfarm.com/api/account", { headers: H }).then((r) => r.json()));export GF_TOKEN=gf_live_xxxxxxxxxxxxxxxxxxxxxxxx
curl -s https://vramfarm.com/api/account -H "Authorization: Bearer $GF_TOKEN"Ein Token gibt nur die Methoden auf dieser Seite frei. Passwort, SSH-Schlüssel und das Erstellen neuer Tokens gibt es nur im Dashboard; mit einem Token liefern diese Endpunkte 403. Wer an einen geleakten Token kommt, kann Server starten und stoppen, aber nicht Ihr Konto verwalten.
Der Katalog und /health funktionieren ohne Token. Senden Sie aber einen Authorization-Header mit falschem oder widerrufenem Token, liefert jeder Endpunkt 401.
Rufen Sie die API von Ihrem eigenen Server, Skript oder Ihrer CI auf. Anfragen aus dem Browser auf einer anderen Website gehen nicht durch, und dort könnte jeder Besucher den Token sehen.
Hinterlegen Sie vor dem ersten Start einen öffentlichen SSH-Schlüssel in den Dashboard-Einstellungen: Er wird auf jedem neuen Server installiert. Ob alles bereit ist, prüfen Sie mit GET /api/account.
Fehler und Limits
Bei einem Fehler erhalten Sie einen HTTP-Status und den Body {"error": "message", "code": "snake_case_code"}. Den Text in error können Sie Menschen unverändert anzeigen; verzweigen Sie im Code anhand von code, nie anhand des Texts. Manche Antworten enthalten zusätzlich einen Hinweis für Programme: unverified beim Start, saving beim Stoppen.
| Status | Wann |
|---|---|
400 | Ungültige Parameter: Image, Festplatte, zu wenig VRAM, Server-Limit |
401 | Falscher oder widerrufener Token, oder das Konto ist gesperrt |
402 | Nicht genug Guthaben; error nennt den nötigen Betrag |
403 | E-Mail-Adresse nicht bestätigt, oder die Methode ist mit einem Token nicht verfügbar |
404 | Server nicht gefunden oder er gehört zu einem anderen Konto; unbekannter Endpunkt |
409 | Keine Maschinen dieses Typs verfügbar; der Server-Ordner wird noch gespeichert |
415 | POST ohne Content-Type: application/json |
429 | Rate-Limit überschritten; warten Sie so viele Sekunden, wie der Header Retry-After angibt |
500 | Ein Fehler auf unserer Seite |
502 | Der Katalog ist vorübergehend nicht verfügbar |
503 | Der Maschinenanbieter ist überlastet; starten Sie in einer Minute erneut |
Limits
- 60 Anfragen pro Minute und Token, über alle Methoden. Das Limit eines Tokens wirkt sich nicht auf andere aus.
- 10 Starts pro Minute und Token. Fehlgeschlagene Starts zählen mit.
- Der Katalog ist zusätzlich auf 60 Anfragen pro Minute und IP-Adresse begrenzt.
- Bis zu 4 Server gleichzeitig; die genaue Zahl steht in
max_active_instances. Sie brauchen mehr? Schreiben Sie uns. - Request-Body bis 64 KB.
Der Server-Status ändert sich höchstens alle paar Sekunden. Fragen Sie ihn daher alle 5–10 Sekunden ab.
import time, requests
def call(method, url, **kw):
for attempt in range(5):
r = requests.request(method, url, **kw)
if r.status_code not in (429, 502, 503):
return r
time.sleep(int(r.headers.get("Retry-After", 30)))
return rasync function call(url, opts = {}) {
for (let attempt = 0; attempt < 5; attempt++) {
const r = await fetch(url, opts);
if (![429, 502, 503].includes(r.status)) return r;
await new Promise((ok) => setTimeout(ok, (+r.headers.get("Retry-After") || 30) * 1000));
}
throw new Error("API is not responding");
}curl -s --retry 5 --retry-delay 30 --retry-all-errors \
https://vramfarm.com/api/account -H "Authorization: Bearer $GF_TOKEN"Katalog
Verfügbare Maschinen
GET/api/offers
Eine Liste verfügbarer Maschinen mit Preis in Euro pro Stunde, die günstigsten zuerst. Der Katalog wird alle 10 Minuten aktualisiert.
Query-Parameter
| Parameter | Typ | Beschreibung |
|---|---|---|
gpuoptional | string | GPU-Modell, zum Beispiel h200. Ohne Angabe oder bei einem unbekannten Modell kommen alle Modelle zurück. Die Modellliste steht unter der Tabelle. |
countoptional | integer | Anzahl der Karten in der Maschine: 1, 2, 4 oder 8. Andere Werte werden ignoriert. |
alloptional | string | 1 zeigt auch nicht verifizierte Maschinen. Standardmäßig nur verifizierte (verified: true). |
Modelle: b200 h200 h100 a100 a100-40 l40s l40 l4 rtx-pro-6000 rtx-a6000 rtx-a5000 rtx-a4000 rtx-5090 rtx-5080 rtx-5070-ti rtx-4090 rtx-4080 rtx-4060-ti rtx-3090 rtx-3080 rtx-3060. a100 hat 80 GB, a100-40 hat 40 GB.
Felder einer Maschine
id | integer | Beim Start als offer_id übergeben |
gpu, gpu_name | string | Modell: Code für Anfragen und Anzeigename |
num_gpus | integer | Anzahl der Karten |
vram | integer | VRAM einer Karte, GB |
cpu, cpu_cores, cpu_ghz | string, integer, number | CPU, verfügbare Kerne, Taktfrequenz |
ram | integer | RAM, GB |
disk, disk_bw | integer | Wie viele GB Festplatte Sie belegen können; Festplattengeschwindigkeit, MB/s |
net_down, net_up | integer | Netzwerk, Mbit/s |
country | string | Land, ISO-Code |
reliability | number | Zuverlässigkeit der Maschine von 0 bis 1 |
cuda, driver | number, string | Höchste unterstützte CUDA- und Treiberversion |
dlperf, tflops, mem_bw, pcie | number | Leistung: DLPerf-Wert, TFLOPS, Speicher- und PCIe-Bandbreite |
verified | boolean | Die Maschine ist verifiziert |
price | number | EUR pro Stunde für den ganzen Server, inklusive der Mindestfestplatte des Templates |
disk_eur | number | EUR pro Stunde für 1 GB Festplatte über der enthaltenen Menge |
traffic_eur, traffic_up_eur | number | EUR pro 1 GB Traffic, Download wie Upload |
duration | integer | Wie viele Tage die Maschine noch gemietet werden kann |
static_ip, ports | boolean, integer | Statische IP, Anzahl offener Ports |
Ist live gleich false, ist der Katalog abgeschaltet und offers leer. updated ist der Zeitpunkt der Antwort in Unix-Millisekunden.
Fehler
401Falscher oder widerrufener Token429Mehr als 60 Anfragen pro Minute502Der Katalog ist vorübergehend nicht verfügbar; versuchen Sie es in einer Minute erneut
import os, requests
API = "https://vramfarm.com/api"
H = {"Authorization": f"Bearer {os.environ['GF_TOKEN']}"}
r = requests.get(f"{API}/offers", headers=H, params={"gpu": "h200", "count": 4})
r.raise_for_status()
for o in r.json()["offers"][:5]:
print(o["id"], o["gpu_name"], o["price"], "EUR/h", o["country"])const API = "https://vramfarm.com/api";
const H = { Authorization: `Bearer ${process.env.GF_TOKEN}` };
const r = await fetch(`${API}/offers?gpu=h200&count=4`, { headers: H });
const { offers } = await r.json();
console.log(offers.slice(0, 5).map((o) => [o.id, o.gpu_name, o.price, o.country]));curl -s "https://vramfarm.com/api/offers?gpu=h200&count=4" \
-H "Authorization: Bearer $GF_TOKEN"{
"live": true,
"updated": 1761040000000,
"offers": [
{
"id": 18273645,
"gpu": "h200",
"gpu_name": "H200",
"num_gpus": 4,
"vram": 141,
"cpu": "AMD EPYC 9354",
"cpu_cores": 64,
"ram": 512,
"disk": 2000,
"net_down": 8000,
"net_up": 8000,
"country": "DE",
"reliability": 0.998,
"cuda": 12.8,
"verified": true,
"price": 33.16,
"disk_eur": 0.00005,
"traffic_eur": 0.007,
"traffic_up_eur": 0.007
}
]
}Maschinentypen
GET/api/offer-types
Eine Zeile pro Paar „Modell × Anzahl Karten“: die günstigste Maschine mit einer Zuverlässigkeit ab 0,97 und wie viele solche Maschinen verfügbar sind. Praktisch, um gpu und count für einen Start zu wählen.
Keine Parameter.
Die Felder sind dieselben wie bei einer Maschine aus dem Katalog, dazu available, wie viele Maschinen dieses Typs frei sind, und max_dlperf, der beste Leistungswert darunter. Die Antwort wird eine Minute lang zwischengespeichert.
Fehler
401Falscher oder widerrufener Token429Mehr als 60 Anfragen pro Minute502Der Katalog ist vorübergehend nicht verfügbar
import os, requests
API = "https://vramfarm.com/api"
H = {"Authorization": f"Bearer {os.environ['GF_TOKEN']}"}
types = requests.get(f"{API}/offer-types", headers=H).json()["types"]
for t in types:
print(f'{t["num_gpus"]}x {t["gpu_name"]}: from {t["price"]} EUR/h, {t["available"]} available')const API = "https://vramfarm.com/api";
const H = { Authorization: `Bearer ${process.env.GF_TOKEN}` };
const { types } = await fetch(`${API}/offer-types`, { headers: H }).then((r) => r.json());
for (const t of types) console.log(`${t.num_gpus}x ${t.gpu_name}: from ${t.price} EUR/h`);curl -s https://vramfarm.com/api/offer-types -H "Authorization: Bearer $GF_TOKEN"{
"live": true,
"updated": 1761040000000,
"types": [
{
"id": 20311822,
"gpu": "rtx-3090",
"gpu_name": "RTX 3090",
"num_gpus": 1,
"vram": 24,
"price": 0.3,
"reliability": 0.991,
"available": 37,
"max_dlperf": 41
}
]
}Server
Server starten
POST/api/instances
Mietet eine Maschine und startet darauf die gewählte App oder Ihr Docker-Image. Die Antwort kommt sofort; bis der Server bereit ist, dauert es 2–5 Minuten, mit eigenem Image 8–15.
Request-Body, JSON
| Parameter | Typ | Beschreibung |
|---|---|---|
offer_idoptional | integer | id der Maschine aus dem Katalog. Ist sie schon vergeben, wählen wir eine ähnliche desselben Typs. Entweder offer_id oder gpu ist Pflicht. |
gpuoptional | string | Modell, wenn die genaue Maschine egal ist: Wir nehmen die günstigste zuverlässige, die passt. |
countoptional | integer | Anzahl der Karten bei Auswahl über gpu, 1 bis 8. Standard: 1. |
templateoptional | string | Was laufen soll, standardmäßig pytorch. Siehe Tabelle unten. Ein unbekannter Wert wird als pytorch behandelt. |
imageoptional | string | Pflicht bei custom: ein öffentliches Image, geschrieben wie bei docker pull. |
cmdoptional | string | Bei custom: ein Befehl, der beim Start ausgeführt wird, bis zu 4000 Zeichen. |
diskoptional | integer | Festplatte, GB. Weniger als das Minimum des Templates wird auf das Minimum angehoben. Höchstens 2000 und nicht mehr, als die Maschine hat. |
ws_restoreoptional | boolean | Standard true: Ihr gespeicherter Arbeitsordner kommt zurück auf den Server. false liefert einen leeren Server. |
Templates (template)
| Code | Was drin ist | Festplatte ab | Kartenspeicher ab |
|---|---|---|---|
pytorch | PyTorch, CUDA, Jupyter | 60 GB | |
jupyter | Jupyter | 40 GB | |
custom | Ihr Image aus image | 60 GB | |
comfyui | ComfyUI und Stable Diffusion 1.5 | 100 GB | |
comfy-flux | ComfyUI und Flux schnell | 100 GB | 16 GB |
comfy-sdxl | ComfyUI und SDXL | 100 GB | 12 GB |
comfy-wan | ComfyUI und Wan 2.2 5B, Video | 100 GB | 24 GB |
comfy-wan14 | ComfyUI und Wan 2.2 14B, Video | 160 GB | 48 GB |
comfy-qwen | ComfyUI und Qwen-Image | 100 GB | 24 GB |
comfy-qedit | ComfyUI und Qwen-Image-Edit | 100 GB | 24 GB |
comfy-hidream | ComfyUI und HiDream-O1 | 60 GB | 24 GB |
chat | Open WebUI und Ollama, Modell qwen3:8b | 80 GB | 12 GB |
chat-pro | Open WebUI und Ollama, Modell qwen3:32b | 100 GB | 24 GB |
chat-mimo | Open WebUI und Ollama, Modell MiMo 9B | 60 GB | 12 GB |
vllm | vLLM, Modell Qwen/Qwen2.5-7B-Instruct | 100 GB | |
tts-qwen3 | Qwen3-TTS, Text zu Sprache, Gradio | 40 GB | 12 GB |
tts-voxcpm | VoxCPM2, Text zu Sprache, Gradio | 40 GB | 12 GB |
tts-chatterbox | Chatterbox, Text zu Sprache, Gradio | 40 GB | 12 GB |
Abgerechnet wird minutengenau ab dem Moment, in dem der Server running ist, mindestens 15 Minuten pro Start. Ihr Guthaben muss mindestens eine Stunde aller Ihrer Server einschließlich des neuen abdecken. Ist das Guthaben aufgebraucht, werden die Server gestoppt und ihre Festplatten gelöscht.
Eine identische Anfrage innerhalb von 15 Sekunden liefert den bereits erstellten Server mit "duplicate": true zurück; eine zweite Maschine mieten wir nicht. Scheitert ein Start am Guthaben oder an einer unbestätigten E-Mail-Adresse, wird er bei Anfragen mit Token nicht vorgemerkt: Starten Sie ihn selbst erneut.
Fehler
400Keine GPU angegeben, ungültiges Image, Festplatte zu groß, zu wenig VRAM für das Template oder bereits 4 Server402Nicht genug Guthaben für eine Stunde; der Betrag steht inerror403E-Mail-Adresse nicht bestätigt; die Antwort enthält"unverified": true409Keine Maschinen dieses Typs verfügbar429Mehr als 10 Starts pro Minute und Token503Der Maschinenanbieter ist überlastet; versuchen Sie es in einer Minute erneut
import os, requests
API = "https://vramfarm.com/api"
H = {"Authorization": f"Bearer {os.environ['GF_TOKEN']}"}
r = requests.post(f"{API}/instances", headers=H, json={
"gpu": "h200", "count": 4, "template": "custom",
"image": "nvidia/cuda:12.4.1-devel-ubuntu22.04", "disk": 200,
"cmd": "pip install -q torch && python /workspace/train.py",
})
if not r.ok:
raise SystemExit(f"{r.status_code} {r.json()['code']}: {r.json()['error']}")
print("server", r.json()["id"])const API = "https://vramfarm.com/api";
const H = { Authorization: `Bearer ${process.env.GF_TOKEN}`, "Content-Type": "application/json" };
const r = await fetch(`${API}/instances`, {
method: "POST", headers: H,
body: JSON.stringify({
gpu: "h200", count: 4, template: "custom",
image: "nvidia/cuda:12.4.1-devel-ubuntu22.04", disk: 200,
cmd: "pip install -q torch && python /workspace/train.py",
}),
});
const data = await r.json();
if (!r.ok) throw new Error(`${r.status} ${data.code}: ${data.error}`);
console.log("server", data.id);curl -s https://vramfarm.com/api/instances \
-H "Authorization: Bearer $GF_TOKEN" \
-H "Content-Type: application/json" \
-d '{"gpu":"h200","count":4,"template":"custom",
"image":"nvidia/cuda:12.4.1-devel-ubuntu22.04","disk":200,
"cmd":"pip install -q torch && python /workspace/train.py"}'{
"ok": true,
"id": 1234,
"status": "provisioning"
}Server auflisten
GET/api/instances
Ihre Server, die neuesten zuerst, bis zu 100. Standardmäßig: laufende, startende und in den letzten 7 Tagen gestoppte.
Query-Parameter
| Parameter | Typ | Beschreibung |
|---|---|---|
statusoptional | string | provisioning, running oder stopped: nur Server in diesem Zustand. |
Jedes Element von instances hat dieselbe Form wie die Antwort von GET /api/instances/{id}.
Fehler
401Falscher oder widerrufener Token
import os, requests
API = "https://vramfarm.com/api"
H = {"Authorization": f"Bearer {os.environ['GF_TOKEN']}"}
r = requests.get(f"{API}/instances", headers=H, params={"status": "running"})
for s in r.json()["instances"]:
print(s["id"], s["gpu_name"], s["cost_eur"], "EUR")const API = "https://vramfarm.com/api";
const H = { Authorization: `Bearer ${process.env.GF_TOKEN}` };
const { instances } = await fetch(`${API}/instances?status=running`, { headers: H }).then((r) => r.json());
for (const s of instances) console.log(s.id, s.gpu_name, s.cost_eur);curl -s "https://vramfarm.com/api/instances?status=running" \
-H "Authorization: Bearer $GF_TOKEN"{
"instances": [
{ "id": 1234, "status": "running", "gpu": "h200", "gpu_count": 4, "cost_eur": 0.55, "...": "..." }
]
}Server und Zugang
GET/api/instances/{id}
Der Zustand eines Servers. Sobald status gleich running ist, enthält access SSH, Jupyter und die Adresse der App.
Pfadparameter
| Parameter | Typ | Beschreibung |
|---|---|---|
idPflicht | integer | id aus der Antwort beim Start |
Felder der Antwort
status | string | provisioning: wird vorbereitet; running: läuft und wird abgerechnet; stopped: gestoppt |
stage, stage_note | string | null | Nur während provisioning: die Phase (zum Beispiel pick, loading) und was gerade passiert, in der Sprache Ihres Kontos |
gpu, gpu_name, gpu_count | string, integer | Modell und Anzahl der Karten |
template, image | string | Template und Image (bei custom) |
disk_gb | integer | Festplatte, GB |
price_eur_hour | number | Stundenpreis für den ganzen Server, EUR |
cost_eur | number | Für diesen Start berechnet, EUR; bei einem laufenden Server bis zur aktuellen Minute |
traffic_eur | number | Für Traffic berechnet, EUR |
specs | object | null | Daten der Maschine |
access.ssh | object | null | host, port, user und ein fertiger command |
access.jupyter | string | null | Jupyter-Link mit Token |
access.app | string | null | Adresse der App bei ComfyUI und vLLM; bei custom, wenn das Image einen eigenen Port öffnet. Mehr unter Apps auf dem Server |
access.workdir | string | Arbeitsordner, /workspace |
error | string | null | Warum der Server von selbst gestoppt wurde: Das Image ließ sich nicht herunterladen, das Guthaben war aufgebraucht |
created_at, started_at, stopped_at | string | null | Zeit in ISO 8601, UTC |
access ist nur bei einem laufenden Server gefüllt. Der SSH-Schlüssel ist derjenige aus Ihren Dashboard-Einstellungen. Startet eine Maschine nicht, nehmen wir kostenlos eine andere; die id des Servers bleibt gleich.
ssh -p 31001 root@vramfarm.com nvidia-smi
scp -P 31001 root@vramfarm.com:/workspace/out.ckpt ./Fehler
404Kein Server mit dieser id, oder er gehört nicht Ihnen
import os, requests
API = "https://vramfarm.com/api"
H = {"Authorization": f"Bearer {os.environ['GF_TOKEN']}"}
s = requests.get(f"{API}/instances/1234", headers=H).json()
if s["status"] == "running":
print(s["access"]["ssh"]["command"])
else:
print(s["status"], s["stage_note"] or s["error"])const API = "https://vramfarm.com/api";
const H = { Authorization: `Bearer ${process.env.GF_TOKEN}` };
const s = await fetch(`${API}/instances/1234`, { headers: H }).then((r) => r.json());
console.log(s.status === "running" ? s.access.ssh.command : s.stage_note ?? s.error);curl -s https://vramfarm.com/api/instances/1234 -H "Authorization: Bearer $GF_TOKEN"{
"id": 1234,
"status": "running",
"stage": null,
"stage_note": null,
"gpu": "h200",
"gpu_name": "H200",
"gpu_count": 4,
"template": "custom",
"image": "nvidia/cuda:12.4.1-devel-ubuntu22.04",
"disk_gb": 200,
"price_eur_hour": 33.16,
"cost_eur": 0.55,
"traffic_eur": 0,
"specs": {
"vram_gb": 141, "cpu": "AMD EPYC 9354", "cpu_cores": 64, "ram_gb": 512,
"country": "DE", "cuda": 12.8, "net_down_mbps": 8000, "net_up_mbps": 8000
},
"access": {
"ssh": {
"host": "vramfarm.com",
"port": 31001,
"user": "root",
"command": "ssh -p 31001 root@vramfarm.com"
},
"jupyter": "https://u.vramfarm.com/api/j/…/lab?token=…",
"app": null,
"workdir": "/workspace"
},
"error": null,
"created_at": "2026-10-21T10:00:00.000Z",
"started_at": "2026-10-21T10:04:12.000Z",
"stopped_at": null
}Server stoppen
POST/api/instances/stop
Stoppt den Server; die Abrechnung endet sofort. Die Festplatte des Servers wird gelöscht; den Arbeitsordner können Sie speichern.
Request-Body, JSON
| Parameter | Typ | Beschreibung |
|---|---|---|
idPflicht | integer | id des Servers |
saveoptional | boolean | true: zuerst /workspace speichern, dann stoppen. Funktioniert nicht bei custom. |
forceoptional | boolean | true: sofort stoppen, auch wenn der Ordner noch gespeichert wird. Das Speichern wird abgebrochen. |
Einen bereits gestoppten Server zu stoppen liefert {"ok":true,"already":true}. Das ist kein Fehler, Sie können den Aufruf also in finally oder in einen trap beim Beenden des Skripts setzen.
Mit "save": true kommt die Antwort {"ok":true,"saving":true} sofort zurück, und der Server speichert den Ordner und stoppt von selbst, meist innerhalb weniger Minuten. Der Speicher kostet 0,03 € pro GB und Monat, täglich abgerechnet. Ihr nächster Server bekommt den Ordner automatisch, außer Sie übergeben "ws_restore": false.
Fehler
404Kein Server mit dieser id, oder er gehört nicht Ihnen409Der Ordner wird noch gespeichert; die Antwort enthält"saving": true. Warten Sie oder übergeben Sie"force": true
import os, requests
API = "https://vramfarm.com/api"
H = {"Authorization": f"Bearer {os.environ['GF_TOKEN']}"}
r = requests.post(f"{API}/instances/stop", headers=H, json={"id": 1234})
print(r.json())const API = "https://vramfarm.com/api";
const H = { Authorization: `Bearer ${process.env.GF_TOKEN}`, "Content-Type": "application/json" };
const r = await fetch(`${API}/instances/stop`, { method: "POST", headers: H, body: JSON.stringify({ id: 1234 }) });
console.log(await r.json());curl -s https://vramfarm.com/api/instances/stop \
-H "Authorization: Bearer $GF_TOKEN" \
-H "Content-Type: application/json" \
-d '{"id":1234}'{
"ok": true
}Konto
Konto
GET/api/account
Guthaben und Startbereitschaft. Praktisch, um vor dem Start zu prüfen, ob Ihre E-Mail-Adresse bestätigt und ein SSH-Schlüssel hinterlegt ist.
Keine Parameter.
email | string | E-Mail-Adresse des Kontos |
verified | boolean | E-Mail-Adresse bestätigt. Ohne Bestätigung liefert ein Start 403 |
balance_eur | number | Guthaben, EUR |
ssh_key_set | boolean | SSH-Schlüssel hinterlegt. Ohne Schlüssel startet der Server trotzdem, aber Sie kommen nur über Jupyter hinein |
active_instances | integer | Wie viele Server gerade starten oder laufen |
max_active_instances | integer | Wie viele Sie gleichzeitig betreiben können |
Fehler
401Falscher oder widerrufener Token, oder das Konto ist gesperrt
import os, requests
API = "https://vramfarm.com/api"
H = {"Authorization": f"Bearer {os.environ['GF_TOKEN']}"}
acc = requests.get(f"{API}/account", headers=H).json()
assert acc["verified"] and acc["ssh_key_set"], acc
print(acc["balance_eur"], "EUR")const API = "https://vramfarm.com/api";
const H = { Authorization: `Bearer ${process.env.GF_TOKEN}` };
const acc = await fetch(`${API}/account`, { headers: H }).then((r) => r.json());
console.log(acc.balance_eur, acc.verified, acc.ssh_key_set);curl -s https://vramfarm.com/api/account -H "Authorization: Bearer $GF_TOKEN"{
"email": "you@example.com",
"verified": true,
"balance_eur": 150.0,
"ssh_key_set": true,
"active_instances": 0,
"max_active_instances": 4
}Dienst
Dienststatus
GET/api/health
Prüft, ob die API erreichbar ist. Kein Token nötig.
Keine Parameter.
Bei einer Störung lautet die Antwort 503 mit einem kurzen Grund in why, zum Beispiel {"ok":false,"why":"…"}.
Fehler
503Der Dienst funktioniert nicht vollständig; der Grund steht inwhy
import requests
r = requests.get("https://vramfarm.com/api/health", timeout=10)
print(r.status_code, r.json())const r = await fetch("https://vramfarm.com/api/health");
console.log(r.status, await r.json());curl -s https://vramfarm.com/api/health{
"ok": true,
"status": "ok"
}Apps auf dem Server per API
ComfyUI, die Apps für Text zu Sprache, der Chat und vLLM haben jeweils eine eigene API. Damit erzeugen Sie Bilder, Audio und Text aus einem Skript, ohne die Oberfläche zu öffnen. Ihren VRAMfarm-Token brauchen diese Apps nicht, und er wird nie an sie gesendet.
So finden Sie die Adresse der App:
- ComfyUI und vLLM: das Feld
access.appin GET /api/instances/{id}, etwahttps://u.vramfarm.com/api/a/<key>/. Bei vLLM endet sie bereits aufv1. - Chat und Text zu Sprache: Eine Adresse wie
http://<host>:<port>steht auf der Server-Karte im Dashboard; inaccess.appist sie nicht enthalten. Aus einem Skript ist ein SSH-Tunnel zuverlässiger: Er funktioniert immer, wenn SSH funktioniert. - Eigenes Image (
custom):access.appist gefüllt, wenn der Container einen anderen Port als 22, 8080 und 8888 öffnet.
# SSH port from access.ssh.port; -N: tunnel only, no shell
ssh -p 31001 root@vramfarm.com -N -L 11434:127.0.0.1:11434Lassen Sie den Tunnel offen, solange Ihr Skript läuft: Anfragen an localhost:11434 gehen an den Server. Für Text zu Sprache nehmen Sie Port 7860 (-L 7860:127.0.0.1:7860), für ComfyUI 8188, für vLLM 8000. Die App startet 5–10 Minuten nach running; bis dahin liefert die Adresse 502 oder antwortet nicht.
ComfyUI
Templates comfyui und comfy-*. Bauen Sie einen Workflow in der Oberfläche und speichern Sie ihn über Workflow → Export (API) als workflow_api.json. Das Skript ändert dann den Prompt-Text im Workflow, sendet ihn an /prompt, wartet auf das Ergebnis in /history/{prompt_id} und lädt die Bilder über /view herunter.
import json, time, requests
APP = "https://u.vramfarm.com/api/a/<key>/" # access.app of the server
wf = json.load(open("workflow_api.json")) # Workflow → Export (API) in ComfyUI
wf["6"]["inputs"]["text"] = "a cat in a spacesuit, photo" # node number from your file
pid = requests.post(APP + "prompt", json={"prompt": wf}).json()["prompt_id"]
while pid not in (h := requests.get(APP + f"history/{pid}").json()):
time.sleep(2)
for node in h[pid]["outputs"].values():
for img in node.get("images", []):
data = requests.get(APP + "view", params=img).content # filename, subfolder, type
open(img["filename"], "wb").write(data)import { readFile, writeFile } from "node:fs/promises";
const APP = "https://u.vramfarm.com/api/a/<key>/"; // access.app of the server
const wf = JSON.parse(await readFile("workflow_api.json", "utf8"));
wf["6"].inputs.text = "a cat in a spacesuit, photo";
const { prompt_id } = await fetch(APP + "prompt", {
method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ prompt: wf }),
}).then((r) => r.json());
let h = {};
while (!h[prompt_id]) {
await new Promise((ok) => setTimeout(ok, 2000));
h = await fetch(APP + `history/${prompt_id}`).then((r) => r.json());
}
for (const node of Object.values(h[prompt_id].outputs))
for (const img of node.images ?? []) {
const r = await fetch(APP + "view?" + new URLSearchParams(img));
await writeFile(img.filename, Buffer.from(await r.arrayBuffer()));
}APP="https://u.vramfarm.com/api/a/<key>/"
curl -s "$APP"prompt -H "Content-Type: application/json" \
-d "{\"prompt\": $(cat workflow_api.json)}"
# {"prompt_id":"8f2c…","number":0}
curl -s "$APP"history/8f2c…
curl -s -o out.png "$APP""view?filename=ComfyUI_00001_.png&type=output"Text zu Sprache und andere Gradio-Apps
Die Templates tts-qwen3, tts-voxcpm und tts-chatterbox basieren auf Gradio. Unten auf der Seite der App gibt es den Link „Use via API“ mit den Methoden, ihren Parametern und fertigen Beispielen. Die Methodennamen unterscheiden sich je nach App. Ersetzen Sie daher /generate im Beispiel durch den Namen aus „Use via API“ oder aus view_api().
# pip install gradio_client
from gradio_client import Client
client = Client("http://localhost:7860/") # through an SSH tunnel or the address from the dashboard
client.view_api() # list of methods and their parameters
# method name and argument order: from view_api() or "Use via API"
audio_path = client.predict("Hello! This is a speech test.", api_name="/generate")
print(audio_path) # the result file on your computer// npm install @gradio/client
import { Client } from "@gradio/client";
const client = await Client.connect("http://localhost:7860/");
console.log(await client.view_api());
const res = await client.predict("/generate", ["Hello! This is a speech test."]);
console.log(res.data);# Gradio 5: first the request, then the result by event_id
curl -s -X POST http://localhost:7860/gradio_api/call/generate \
-H "Content-Type: application/json" \
-d '{"data": ["Hello! This is a speech test."]}'
# {"event_id":"3b1f…"}
curl -sN http://localhost:7860/gradio_api/call/generate/3b1f…Chat: Ollama und Open WebUI
Templates chat, chat-pro und chat-mimo: Open WebUI läuft auf Ollama. Aus einem Skript sprechen Sie Ollama einfacher direkt an. Es lauscht nur innerhalb des Servers auf 127.0.0.1:11434, Sie brauchen also einen SSH-Tunnel wie im Beispiel oben. Das Modell wird nach dem Start heruntergeladen; bis es geladen ist, meldet Ollama, dass das Modell fehlt.
import requests
r = requests.post("http://localhost:11434/api/chat", json={
"model": "qwen3:8b", # chat-pro uses qwen3:32b
"messages": [{"role": "user", "content": "In one sentence: what is CUDA?"}],
"stream": False,
})
print(r.json()["message"]["content"])const r = await fetch("http://localhost:11434/api/chat", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
model: "qwen3:8b",
messages: [{ role: "user", content: "In one sentence: what is CUDA?" }],
stream: false,
}),
});
console.log((await r.json()).message.content);curl -s http://localhost:11434/api/chat -d '{
"model": "qwen3:8b",
"messages": [{"role": "user", "content": "In one sentence: what is CUDA?"}],
"stream": false
}'Ollama versteht unter http://localhost:11434/v1/chat/completions auch das OpenAI-Format, OpenAI-Bibliotheken funktionieren also. Auch Open WebUI hat eine eigene API; den Schlüssel dafür erstellen Sie in den Einstellungen von Open WebUI.
vLLM
Das Template vllm startet einen OpenAI-kompatiblen Server mit Qwen/Qwen2.5-7B-Instruct. Die Adresse ist access.app, sie endet bereits auf /v1. Der Schlüssel wird nicht geprüft, die OpenAI-Bibliothek verlangt aber einen nicht leeren, übergeben Sie also einen beliebigen. Die Modellliste gibt es unter GET /v1/models.
# pip install openai
from openai import OpenAI
client = OpenAI(base_url="https://u.vramfarm.com/api/a/<key>/v1", # access.app
api_key="EMPTY") # no key needed
r = client.chat.completions.create(
model="Qwen/Qwen2.5-7B-Instruct",
messages=[{"role": "user", "content": "Write a haiku about a graphics card"}],
)
print(r.choices[0].message.content)const BASE = "https://u.vramfarm.com/api/a/<key>/v1"; // access.app
const r = await fetch(`${BASE}/chat/completions`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
model: "Qwen/Qwen2.5-7B-Instruct",
messages: [{ role: "user", content: "Write a haiku about a graphics card" }],
}),
});
console.log((await r.json()).choices[0].message.content);BASE="https://u.vramfarm.com/api/a/<key>/v1"
curl -s "$BASE/models"
curl -s "$BASE/chat/completions" -H "Content-Type: application/json" -d '{
"model": "Qwen/Qwen2.5-7B-Instruct",
"messages": [{"role": "user", "content": "Write a haiku about a graphics card"}]
}'Sie brauchen einen Token? Registrieren Sie sich, bestätigen Sie Ihre E-Mail-Adresse und erstellen Sie einen Token in den Dashboard-Einstellungen. Wenn etwas nicht zu dieser Seite passt oder Sie ein höheres Limit brauchen, schreiben Sie uns.