Documents
Home>Documents>AI>Agent>Xgen

Building a VastAI GPU Instance Management API

7 min readMay 10, 2025Feb 22, 2026

VastAI GPU Instance Management API: A Development Story

AI model training and serving require GPUs. Rather than maintaining our own GPU servers — which is cost-prohibitive — we decided to use VastAI, a GPU cloud marketplace. This post documents how we built an API to manage VastAI instances directly from the XGen platform.

What is VastAI?

VastAI is a marketplace for renting and leasing idle GPUs. It offers a wide variety of GPUs (A100, H100, RTX 4090, etc.) at prices significantly lower than AWS or GCP.

VastAI API Client

import httpx
from typing import Optional, List

class VastAIClient:
    BASE_URL = "https://console.vast.ai/api/v0"

    def __init__(self, api_key: str):
        self.api_key = api_key
        self.headers = {"Authorization": f"Bearer {api_key}"}

    async def search_offers(
        self,
        gpu_name: str = "RTX_4090",
        num_gpus: int = 1,
        min_ram: float = 16.0,
    ) -> List[dict]:
        async with httpx.AsyncClient() as client:
            params = {
                "gpu_name": gpu_name,
                "num_gpus": num_gpus,
                "gpu_ram": f">={min_ram}",
                "order": "dph_total",  # sort by hourly price
                "type": "on-demand",
            }
            resp = await client.get(
                f"{self.BASE_URL}/bundles",
                headers=self.headers,
                params=params,
            )
            resp.raise_for_status()
            return resp.json().get("offers", [])

    async def create_instance(
        self, offer_id: int, image: str, disk_gb: float = 50
    ) -> dict:
        async with httpx.AsyncClient() as client:
            payload = {
                "client_id": "me",
                "image": image,
                "disk": disk_gb,
                "onstart": "bash /opt/setup.sh",
            }
            resp = await client.put(
                f"{self.BASE_URL}/asks/{offer_id}/",
                headers=self.headers,
                json=payload,
            )
            resp.raise_for_status()
            return resp.json()

    async def destroy_instance(self, instance_id: int) -> bool:
        async with httpx.AsyncClient() as client:
            resp = await client.delete(
                f"{self.BASE_URL}/instances/{instance_id}/",
                headers=self.headers,
            )
            return resp.status_code == 200

Instance Health Monitoring

class InstanceMonitor:
    def __init__(self, vast_client: VastAIClient):
        self.client = vast_client

    async def health_check(self, instance_id: int) -> dict:
        instance = await self.client.get_instance(instance_id)
        status = instance.get("actual_status", "unknown")

        return {
            "instance_id": instance_id,
            "status": status,
            "gpu_util": instance.get("gpu_utilization", 0),
            "gpu_temp": instance.get("gpu_temp", 0),
            "uptime": instance.get("duration", 0),
            "cost_so_far": instance.get("total_cost", 0),
        }

    async def auto_destroy_idle(
        self, instance_id: int, idle_minutes: int = 30
    ):
        health = await self.health_check(instance_id)
        if health["gpu_util"] < 5 and health["uptime"] > idle_minutes * 60:
            await self.client.destroy_instance(instance_id)
            return True
        return False

FastAPI Endpoints

router = APIRouter(prefix="/api/gpu", tags=["gpu"])

@router.get("/offers")
async def search_gpu_offers(gpu_name: str = "RTX_4090", num_gpus: int = 1):
    offers = await vast_client.search_offers(gpu_name, num_gpus)
    return {"offers": offers[:10]}

@router.post("/instances")
async def create_gpu_instance(offer_id: int, image: str):
    result = await vast_client.create_instance(offer_id, image)
    return {"instance": result}

@router.delete("/instances/{instance_id}")
async def destroy_gpu_instance(instance_id: int):
    success = await vast_client.destroy_instance(instance_id)
    return {"success": success}

SSH Tunneling

We also implemented SSH tunneling to provide terminal access to instances, allowing team members to open a terminal directly from the XGen platform UI. With VastAI integrated into XGen, the team can now spin up and tear down GPU instances with just a few clicks in the web UI — and we've cut costs by roughly 60% compared to AWS.

Tags
VastAIGPUCloudComputingAPIInfrastructure