API VRAMfarm
Lancez des serveurs GPU depuis vos scripts et votre CI : trouvez une machine disponible, démarrez-la avec une application prête à l’emploi ou votre propre image Docker, attendez l’accès SSH, arrêtez-la. Du HTTPS et du JSON, sans SDK.
https://vramfarm.com/api- Les réponses sont en JSON, UTF-8. Les requêtes POST exigent l’en-tête
Content-Type: application/json. - Les montants sont en euros.
priceest le prix horaire du serveur entier, toutes cartes comprises, sous forme décimale avec les centimes (par exemple0.39). - Les dates des réponses sont au format ISO 8601 en UTC ; le champ
updateddu catalogue est en millisecondes Unix. - Les messages d’erreur sont dans la langue de votre compte, ou dans celle de l’en-tête
Accept-Language, et en anglais par défaut.
Démarrage rapide
Ce script lance un serveur RTX 4090, attend qu’il soit prêt, affiche la commande SSH et arrête le serveur, même si quelque chose échoue en cours de route.
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'Authentification
Créez un token dans le tableau de bord → Paramètres → Tokens API. Un token ressemble à gf_live_… et ne s’affiche qu’une fois : conservez-le dans les secrets de votre CI ou dans la variable d’environnement GF_TOKEN. Vous pouvez révoquer vos tokens au même endroit. Jusqu’à 10 tokens peuvent être actifs.
Envoyez le token dans l’en-tête Authorization: Bearer gf_live_…. Rien d’autre n’est nécessaire : les requêtes ne sont pas signées, il n’y a ni horodatage ni recvWindow, et les cookies et l’en-tête Origin ne sont pas vérifiés pour les requêtes avec token.
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"Un token donne accès uniquement aux méthodes de cette page. Votre mot de passe, votre clé SSH et la création de nouveaux tokens ne sont accessibles que depuis le tableau de bord ; avec un token, ces endpoints renvoient 403. Un token divulgué permet de lancer et d’arrêter des serveurs, mais pas de gérer votre compte.
Le catalogue et /health fonctionnent sans token. Mais si vous envoyez un en-tête Authorization avec un token erroné ou révoqué, tous les endpoints renvoient 401.
Appelez l’API depuis votre propre serveur, script ou CI. Les requêtes envoyées depuis un navigateur sur un autre site ne passeront pas, et n’importe quel visiteur pourrait y voir le token.
Avant votre premier lancement, ajoutez une clé SSH publique dans les paramètres du tableau de bord : elle est installée sur chaque nouveau serveur. Vous pouvez vérifier que tout est prêt avec GET /api/account.
Erreurs et limites
En cas d’erreur, vous recevez un statut HTTP et le corps {"error": "message", "code": "snake_case_code"}. Le texte de error peut être montré tel quel à une personne ; faites dépendre votre code de code, jamais du texte. Certaines réponses ajoutent une indication pour les programmes : unverified au lancement, saving à l’arrêt.
| Statut | Quand |
|---|---|
400 | Paramètres invalides : image, disque, VRAM insuffisante, limite de serveurs |
401 | Token erroné ou révoqué, ou compte bloqué |
402 | Solde insuffisant ; error indique le montant nécessaire |
403 | E-mail non confirmé, ou méthode non disponible avec un token |
404 | Serveur introuvable ou appartenant à un autre compte ; endpoint inconnu |
409 | Aucune machine de ce type disponible ; le dossier du serveur est encore en cours d’enregistrement |
415 | POST sans Content-Type: application/json |
429 | Limite de requêtes dépassée ; attendez le nombre de secondes indiqué par l’en-tête Retry-After |
500 | Une erreur de notre côté |
502 | Le catalogue est temporairement indisponible |
503 | Le fournisseur de machines est surchargé ; relancez dans une minute |
Limites
- 60 requêtes par minute et par token, toutes méthodes confondues. La limite d’un token n’affecte pas les autres.
- 10 lancements par minute et par token. Les lancements échoués comptent aussi.
- Le catalogue est en plus limité à 60 requêtes par minute et par adresse IP.
- Jusqu’à 4 serveurs simultanés ; le nombre exact figure dans
max_active_instances. Besoin de plus ? Écrivez-nous. - Corps de requête jusqu’à 64 Ko.
Le statut d’un serveur change au plus toutes les quelques secondes : interrogez-le toutes les 5 à 10 secondes.
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"Catalogue
Machines disponibles
GET/api/offers
La liste des machines disponibles avec leur prix en euros par heure, de la moins chère à la plus chère. Le catalogue est actualisé toutes les 10 minutes.
Paramètres de requête
| Paramètre | Type | Description |
|---|---|---|
gpufacultatif | string | Modèle de GPU, par exemple h200. Sans ce paramètre, ou avec un modèle inconnu, tous les modèles sont renvoyés. La liste des modèles se trouve sous le tableau. |
countfacultatif | integer | Nombre de cartes dans la machine : 1, 2, 4 ou 8. Les autres valeurs sont ignorées. |
allfacultatif | string | 1 affiche aussi les machines non vérifiées. Par défaut, seules les machines vérifiées (verified: true) sont renvoyées. |
Modèles : 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 correspond à 80 Go, a100-40 à 40 Go.
Champs d’une machine
id | integer | À passer comme offer_id au lancement |
gpu, gpu_name | string | Modèle : code pour les requêtes et nom affiché |
num_gpus | integer | Nombre de cartes |
vram | integer | VRAM d’une carte, Go |
cpu, cpu_cores, cpu_ghz | string, integer, number | CPU, cœurs disponibles, fréquence |
ram | integer | RAM, Go |
disk, disk_bw | integer | Espace disque disponible en Go ; vitesse du disque, Mo/s |
net_down, net_up | integer | Réseau, Mbit/s |
country | string | Pays, code ISO |
reliability | number | Fiabilité de la machine, de 0 à 1 |
cuda, driver | number, string | Version maximale de CUDA et du pilote prise en charge |
dlperf, tflops, mem_bw, pcie | number | Performances : score DLPerf, TFLOPS, bande passante mémoire et PCIe |
verified | boolean | La machine est vérifiée |
price | number | EUR par heure pour le serveur entier, disque minimal du template inclus |
disk_eur | number | EUR par heure pour 1 Go de disque au-delà du volume inclus |
traffic_eur, traffic_up_eur | number | EUR par Go de trafic téléchargé et envoyé |
duration | integer | Nombre de jours pendant lesquels la machine peut encore être louée |
static_ip, ports | boolean, integer | IP statique, nombre de ports ouverts |
Si live vaut false, le catalogue est désactivé et offers est vide. updated est l’heure de la réponse en millisecondes Unix.
Erreurs
401Token erroné ou révoqué429Plus de 60 requêtes par minute502Le catalogue est temporairement indisponible ; réessayez dans une minute
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
}
]
}Types de machines
GET/api/offer-types
Une ligne par couple « modèle × nombre de cartes » : la machine la moins chère avec une fiabilité d’au moins 0,97, et le nombre de machines de ce type disponibles. Pratique pour choisir gpu et count avant un lancement.
Aucun paramètre.
Les champs sont les mêmes que pour une machine du catalogue, avec en plus available, le nombre de machines libres de ce type, et max_dlperf, le meilleur score de performance parmi elles. La réponse est mise en cache pendant une minute.
Erreurs
401Token erroné ou révoqué429Plus de 60 requêtes par minute502Le catalogue est temporairement indisponible
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
}
]
}Serveurs
Lancer un serveur
POST/api/instances
Loue une machine et y démarre l’application choisie ou votre image Docker. La réponse arrive immédiatement ; le serveur met 2 à 5 minutes à être prêt, ou 8 à 15 avec votre propre image.
Corps de la requête, JSON
| Paramètre | Type | Description |
|---|---|---|
offer_idfacultatif | integer | id d’une machine du catalogue. Si elle a déjà été prise, nous en choisissons une similaire du même type. offer_id ou gpu est obligatoire. |
gpufacultatif | string | Modèle, si la machine exacte vous importe peu : nous prenons la moins chère parmi les machines fiables qui conviennent. |
countfacultatif | integer | Nombre de cartes lors d’un choix par gpu, de 1 à 8. 1 par défaut. |
templatefacultatif | string | Ce qu’il faut lancer, pytorch par défaut. Voir le tableau ci-dessous. Une valeur inconnue est traitée comme pytorch. |
imagefacultatif | string | Obligatoire pour custom : une image publique, écrite comme pour docker pull. |
cmdfacultatif | string | Pour custom : une commande à exécuter au démarrage, jusqu’à 4000 caractères. |
diskfacultatif | integer | Disque, en Go. Une valeur inférieure au minimum du template est relevée à ce minimum. Pas plus de 2000, ni plus que ce dont dispose la machine. |
ws_restorefacultatif | boolean | true par défaut : votre dossier de travail enregistré revient sur le serveur. false donne un serveur vierge. |
Templates (template)
| Code | Contenu | Disque min. | Mémoire de carte min. |
|---|---|---|---|
pytorch | PyTorch, CUDA, Jupyter | 60 GB | |
jupyter | Jupyter | 40 GB | |
custom | Votre image indiquée dans image | 60 GB | |
comfyui | ComfyUI et Stable Diffusion 1.5 | 100 GB | |
comfy-flux | ComfyUI et Flux schnell | 100 GB | 16 GB |
comfy-sdxl | ComfyUI et SDXL | 100 GB | 12 GB |
comfy-wan | ComfyUI et Wan 2.2 5B, vidéo | 100 GB | 24 GB |
comfy-wan14 | ComfyUI et Wan 2.2 14B, vidéo | 160 GB | 48 GB |
comfy-qwen | ComfyUI et Qwen-Image | 100 GB | 24 GB |
comfy-qedit | ComfyUI et Qwen-Image-Edit | 100 GB | 24 GB |
comfy-hidream | ComfyUI et HiDream-O1 | 60 GB | 24 GB |
chat | Open WebUI et Ollama, modèle qwen3:8b | 80 GB | 12 GB |
chat-pro | Open WebUI et Ollama, modèle qwen3:32b | 100 GB | 24 GB |
chat-mimo | Open WebUI et Ollama, modèle MiMo 9B | 60 GB | 12 GB |
vllm | vLLM, modèle Qwen/Qwen2.5-7B-Instruct | 100 GB | |
tts-qwen3 | Synthèse vocale Qwen3-TTS, Gradio | 40 GB | 12 GB |
tts-voxcpm | Synthèse vocale VoxCPM2, Gradio | 40 GB | 12 GB |
tts-chatterbox | Synthèse vocale Chatterbox, Gradio | 40 GB | 12 GB |
La facturation se fait à la minute à partir du moment où le serveur passe en running, avec un minimum de 15 minutes par lancement. Votre solde doit couvrir au moins une heure de tous vos serveurs, nouveau serveur compris. Quand le solde est épuisé, les serveurs s’arrêtent et leurs disques sont effacés.
Une requête identique dans les 15 secondes renvoie le serveur déjà créé, avec "duplicate": true ; nous ne louons pas de seconde machine. Si un lancement échoue à cause du solde ou d’un e-mail non confirmé, il n’est pas mis en file d’attente pour les requêtes avec token : relancez-le vous-même.
Erreurs
400Aucun GPU indiqué, image invalide, disque trop grand, VRAM insuffisante pour le template, ou déjà 4 serveurs402Solde insuffisant pour une heure ; le montant figure danserror403E-mail non confirmé ; la réponse contient"unverified": true409Aucune machine de ce type disponible429Plus de 10 lancements par minute et par token503Le fournisseur de machines est surchargé ; réessayez dans une minute
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"
}Liste des serveurs
GET/api/instances
Vos serveurs, du plus récent au plus ancien, jusqu’à 100. Par défaut : en cours, en démarrage et arrêtés au cours des 7 derniers jours.
Paramètres de requête
| Paramètre | Type | Description |
|---|---|---|
statusfacultatif | string | provisioning, running ou stopped : uniquement les serveurs dans cet état. |
Chaque élément de instances a la même forme que la réponse de GET /api/instances/{id}.
Erreurs
401Token erroné ou révoqué
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, "...": "..." }
]
}Serveur et accès
GET/api/instances/{id}
L’état d’un serveur. Dès que status vaut running, access contient SSH, Jupyter et l’adresse de l’application.
Paramètres de chemin
| Paramètre | Type | Description |
|---|---|---|
idobligatoire | integer | id issu de la réponse au lancement |
Champs de la réponse
status | string | provisioning : en préparation ; running : en cours et facturé ; stopped : arrêté |
stage, stage_note | string | null | Uniquement pendant provisioning : l’étape (par exemple pick, loading) et ce qui se passe, dans la langue de votre compte |
gpu, gpu_name, gpu_count | string, integer | Modèle et nombre de cartes |
template, image | string | Template et image (pour custom) |
disk_gb | integer | Disque, Go |
price_eur_hour | number | Prix horaire du serveur entier, EUR |
cost_eur | number | Montant facturé pour ce lancement, EUR ; pour un serveur en cours, jusqu’à la minute actuelle |
traffic_eur | number | Montant facturé pour le trafic, EUR |
specs | object | null | Caractéristiques de la machine |
access.ssh | object | null | host, port, user et une command prête à l’emploi |
access.jupyter | string | null | Lien Jupyter avec un token |
access.app | string | null | Adresse de l’application pour ComfyUI et vLLM ; pour custom, si l’image ouvre son propre port. Plus de détails dans Applications du serveur |
access.workdir | string | Dossier de travail, /workspace |
error | string | null | Pourquoi le serveur s’est arrêté de lui-même : l’image n’a pas pu être téléchargée, le solde est épuisé |
created_at, started_at, stopped_at | string | null | Heure au format ISO 8601, UTC |
access n’est rempli que pour un serveur en cours. La clé SSH est celle ajoutée dans les paramètres du tableau de bord. Si une machine ne démarre pas, nous en prenons une autre gratuitement ; l’id du serveur reste le même.
ssh -p 31001 root@vramfarm.com nvidia-smi
scp -P 31001 root@vramfarm.com:/workspace/out.ckpt ./Erreurs
404Aucun serveur avec cet id, ou il ne vous appartient pas
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
}Arrêter un serveur
POST/api/instances/stop
Arrête le serveur ; la facturation s’arrête immédiatement. Le disque du serveur est effacé ; vous pouvez enregistrer le dossier de travail.
Corps de la requête, JSON
| Paramètre | Type | Description |
|---|---|---|
idobligatoire | integer | id du serveur |
savefacultatif | boolean | true : enregistrer d’abord /workspace, puis arrêter. Ne fonctionne pas pour custom. |
forcefacultatif | boolean | true : arrêter tout de suite, même si le dossier est en cours d’enregistrement. L’enregistrement est interrompu. |
Arrêter un serveur déjà arrêté renvoie {"ok":true,"already":true}. Ce n’est pas une erreur : vous pouvez placer l’appel dans un finally ou un trap à la sortie du script.
Avec "save": true, la réponse {"ok":true,"saving":true} arrive immédiatement, puis le serveur enregistre le dossier et s’arrête de lui-même, généralement en quelques minutes. Le stockage coûte 0,03 € par Go et par mois, facturé chaque jour. Votre prochain serveur récupère le dossier automatiquement, sauf si vous passez "ws_restore": false.
Erreurs
404Aucun serveur avec cet id, ou il ne vous appartient pas409Le dossier est encore en cours d’enregistrement ; la réponse contient"saving": true. Patientez, ou passez"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
}Compte
Compte
GET/api/account
Solde et capacité à lancer. Pratique pour vérifier avant un lancement que votre e-mail est confirmé et qu’une clé SSH est ajoutée.
Aucun paramètre.
email | string | E-mail du compte |
verified | boolean | E-mail confirmé. Sinon, un lancement renvoie 403 |
balance_eur | number | Solde, EUR |
ssh_key_set | boolean | Clé SSH ajoutée. Sans clé, le serveur démarre quand même, mais vous ne pouvez y accéder que via Jupyter |
active_instances | integer | Nombre de serveurs en démarrage ou en cours |
max_active_instances | integer | Nombre de serveurs que vous pouvez faire tourner en même temps |
Erreurs
401Token erroné ou révoqué, ou compte bloqué
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
}Service
État du service
GET/api/health
Vérifie que l’API répond. Aucun token nécessaire.
Aucun paramètre.
En cas de panne, la réponse est 503 avec une brève raison dans why, par exemple {"ok":false,"why":"…"}.
Erreurs
503Le service ne fonctionne pas entièrement ; la raison figure danswhy
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"
}Applications du serveur via l’API
ComfyUI, les applications de synthèse vocale, le chat et vLLM ont chacun leur propre API. Utilisez-la pour générer des images, de l’audio et du texte depuis un script, sans ouvrir l’interface. Ces applications n’ont pas besoin de votre token VRAMfarm, et il ne leur est jamais envoyé.
Où trouver l’adresse de l’application :
- ComfyUI et vLLM : le champ
access.appde GET /api/instances/{id}, du typehttps://u.vramfarm.com/api/a/<key>/. Pour vLLM, elle se termine déjà parv1. - Chat et synthèse vocale : une adresse du type
http://<host>:<port>s’affiche sur la carte du serveur dans le tableau de bord ; elle ne figure pas dansaccess.app. Depuis un script, un tunnel SSH est plus fiable : il fonctionne dès que SSH fonctionne. - Votre propre image (
custom) :access.appest rempli si le conteneur ouvre un port autre que 22, 8080 et 8888.
# SSH port from access.ssh.port; -N: tunnel only, no shell
ssh -p 31001 root@vramfarm.com -N -L 11434:127.0.0.1:11434Gardez le tunnel ouvert pendant l’exécution de votre script : les requêtes vers localhost:11434 sont transmises au serveur. Pour la synthèse vocale, utilisez le port 7860 (-L 7860:127.0.0.1:7860), pour ComfyUI le 8188, pour vLLM le 8000. L’application démarre 5 à 10 minutes après running ; d’ici là, l’adresse renvoie 502 ou ne répond pas.
ComfyUI
Templates comfyui et comfy-*. Construisez un workflow dans l’interface et enregistrez-le via Workflow → Export (API) sous le nom workflow_api.json. Le script modifie ensuite le texte du prompt dans le workflow, l’envoie à /prompt, attend le résultat dans /history/{prompt_id} et télécharge les images via /view.
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"Synthèse vocale et autres applications Gradio
Les templates tts-qwen3, tts-voxcpm et tts-chatterbox reposent sur Gradio. En bas de la page de l’application, un lien « Use via API » liste les méthodes, leurs paramètres et des exemples prêts à l’emploi. Les noms de méthode diffèrent d’une application à l’autre : remplacez /generate dans l’exemple par le nom indiqué dans « Use via API » ou par 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 et Open WebUI
Templates chat, chat-pro et chat-mimo : Open WebUI tourne au-dessus d’Ollama. Depuis un script, le plus simple est de parler directement à Ollama. Il n’écoute qu’à l’intérieur du serveur, sur 127.0.0.1:11434 : il vous faut donc un tunnel SSH comme dans l’exemple ci-dessus. Le modèle se télécharge après le lancement ; tant qu’il n’est pas chargé, Ollama répond que le modèle est introuvable.
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 parle aussi le format OpenAI sur http://localhost:11434/v1/chat/completions : les bibliothèques OpenAI fonctionnent donc. Open WebUI a aussi sa propre API ; vous générez une clé pour elle dans les paramètres d’Open WebUI.
vLLM
Le template vllm démarre un serveur compatible OpenAI avec Qwen/Qwen2.5-7B-Instruct. L’adresse est access.app, qui se termine déjà par /v1. La clé n’est pas vérifiée, mais la bibliothèque OpenAI en exige une non vide : passez n’importe quelle valeur. La liste des modèles se trouve sur 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"}]
}'Besoin d’un token ? Inscrivez-vous, confirmez votre e-mail et créez un token dans les paramètres du tableau de bord. Si quelque chose ne correspond pas à cette page, ou si vous avez besoin d’une limite plus élevée, écrivez-nous.