The DeployGPU platform

One API to launch GPUs across providers

One REST API and one key to find GPU offers, then launch, list and stop VMs on QuantaCloud, Massed Compute, Verda and Hyperstack. Examples and limits.

The DeployGPU API launches GPU virtual machines on QuantaCloud, Massed Compute, Verda and Hyperstack with one key and one balance: find an offer, create a deployment, poll until it is active, SSH in, and stop it when the job is done. Billing is the same as in the dashboard, so the prepaid balance caps what a runaway script can spend.

What the API can and cannot do

You canEndpoint
Search live offers, no key neededGET /api/v1/offers, GET /api/v1/offers/{id}
List GPU families and providers, no key neededGET /api/v1/gpu-families, GET /api/v1/providers
See a provider's OS images and volume types, no key neededGET /api/v1/providers/{provider}/images, .../volume-types
Launch, list, inspect and stop VMsPOST /api/v1/deployments, GET /api/v1/deployments, GET /api/v1/deployments/{id}, POST /api/v1/deployments/{id}/stop
Power a Verda or Hyperstack VM off and on without deleting it, with billing continuingPOST /api/v1/deployments/{id}/shutdown, .../start, GET .../power
Create, list and delete volumes on Verda and Hyperstack/api/v1/volumes
Manage SSH keys/api/v1/users/me/ssh-keys
Read your account and balanceGET /api/v1/account, GET /api/v1/account/balance

The API is deliberately narrower than the dashboard. It launches plain VMs only: no app templates, no private app links, and no Vast.ai containers, which return provider_not_supported. Volumes are attached at launch through volume_ids, never to a running VM. The power endpoints work on Verda and Hyperstack and return provider_not_supported elsewhere: they keep the VM and its disk, and full billing continues while the VM is off, so they are a recovery tool, not a pause. Credits and API keys are managed in the dashboard. There is no CLI, SDK or webhook today.

The base URL is https://api.deploygpu.ai. The full reference lives in the API documentation, and the OpenAPI spec is at https://api.deploygpu.ai/api-docs.

Keys

Create a key on the API keys page. Keys start with gpu_live_ and go in either header:

Code example
curl -s https://api.deploygpu.ai/api/v1/account/balance \
  -H "X-API-Key: $DEPLOYGPU_API_KEY"

curl -s https://api.deploygpu.ai/api/v1/account/balance \
  -H "Authorization: Bearer $DEPLOYGPU_API_KEY"

A key has no scopes and no expiry date: it can do everything your account can do, including launching GPUs. Give each system its own key, keep it in a secret manager rather than a repository, and delete keys you no longer use. A request without a valid key gets 401 with an empty body.

A full launch, step by step

  1. Find an offer. The offer search is public:
Code example
curl -s "https://api.deploygpu.ai/api/v1/offers?gpu=rtx-a6000&sort_by=priceHourly&sort_order=asc&size=20"

Keep offers whose deploymentType is virtual. The gpu filter takes variant slugs such as rtx-a6000, h100-sxm-80gb or h100-pcie-80gb, and GET /api/v1/gpu-families lists them all.

  1. Create the deployment with the offer's id and the provider's key:
Code example
curl -s -X POST https://api.deploygpu.ai/api/v1/deployments \
  -H "X-API-Key: $DEPLOYGPU_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"provider": "quantacloud", "offer_id": "OFFER_ID", "disk_gb": 100}'

The answer is 202 with {"id": "...", "status": "provisioning"}. The provider values the create call accepts are quantacloud, masscompute, verda and hyperstack. Offers name Massed Compute massed-compute, so map that one before you send it. disk_gb only matters on Verda: the other providers use the disk that comes with the offer. Leave out ssh_key_ids to install every key on your account, or list the ones you want.

  1. Poll until the status is active:
Code example
curl -s https://api.deploygpu.ai/api/v1/deployments/DEPLOYMENT_ID \
  -H "X-API-Key: $DEPLOYGPU_API_KEY"

For about a second after the 202, this can return 404, so treat an early 404 as "not yet". Most VMs are active within 1 to 3 minutes. The connection object then holds public_ip, ssh_port and ssh_user, and the user differs by provider: ubuntu, Ubuntu or root.

  1. SSH in. On some providers a VM reports active a little before SSH accepts connections, so retry for a minute before treating a refused connection as a failure.

  2. Stop it when the work is done. DELETE /api/v1/deployments/{id} does the same:

Code example
curl -s -X POST https://api.deploygpu.ai/api/v1/deployments/DEPLOYMENT_ID/stop \
  -H "X-API-Key: $DEPLOYGPU_API_KEY"

Stopping deletes the VM and its disk, and the unused seconds of the current hour go back to your balance.

The same flow in Python

This version tries the cheapest offers in order, never repeats a create call blindly, and always stops what it started:

Code example
import os
import time

import requests

API = "https://api.deploygpu.ai/api/v1"
HEADERS = {"X-API-Key": os.environ["DEPLOYGPU_API_KEY"]}
# Offers call Massed Compute "massed-compute"; the create call wants "masscompute".
PROVIDER_KEY = {"massed-compute": "masscompute"}
VM_PROVIDERS = {"quantacloud", "masscompute", "verda", "hyperstack"}


def offers(gpu_slug):
    params = {"gpu": gpu_slug, "sort_by": "priceHourly", "sort_order": "asc", "size": 20}
    resp = requests.get(f"{API}/offers", params=params, timeout=30)
    resp.raise_for_status()
    for offer in resp.json()["offers"]:
        provider = PROVIDER_KEY.get(offer["provider"]["slug"], offer["provider"]["slug"])
        if offer["deploymentType"] == "virtual" and provider in VM_PROVIDERS:
            yield provider, offer


def create(provider, offer):
    body = {"provider": provider, "offer_id": offer["id"], "disk_gb": 100}
    resp = requests.post(f"{API}/deployments", json=body, headers=HEADERS, timeout=60)
    if resp.status_code in (400, 503):
        code = resp.json()["error"]["code"]
        if code in ("offer_unavailable", "offer_cooling_down"):
            return None  # this offer is gone or cooling down: try the next one
    resp.raise_for_status()
    return resp.json()["id"]


def wait_until_active(deployment_id, timeout_s=900):
    deadline = time.time() + timeout_s
    while time.time() < deadline:
        resp = requests.get(f"{API}/deployments/{deployment_id}", headers=HEADERS, timeout=30)
        if resp.status_code != 404:  # a 404 right after create means "not visible yet"
            resp.raise_for_status()
            deployment = resp.json()
            if deployment["status"] == "active":
                return deployment
            if deployment["status"] in ("failed", "terminated", "interrupted"):
                raise RuntimeError(f"{deployment['failure_code']}: {deployment['suggested_action']}")
        time.sleep(10)
    raise TimeoutError(deployment_id)


deployment_id = None
for provider, offer in offers("rtx-a6000"):
    deployment_id = create(provider, offer)
    if deployment_id:
        break
if deployment_id is None:
    raise SystemExit("No RTX A6000 offer could be launched right now")

try:
    conn = wait_until_active(deployment_id)["connection"]
    print(f"ssh {conn['ssh_user']}@{conn['public_ip']} -p {conn['ssh_port']}")
    # ... run your job over SSH here ...
finally:
    requests.post(f"{API}/deployments/{deployment_id}/stop", headers=HEADERS, timeout=60)

The one rule in that script that matters most: never retry POST /api/v1/deployments automatically after a timeout or a dropped connection. The API has no idempotency key, so a retried create can start, and bill, a second GPU. If a create call times out, list your deployments with GET /api/v1/deployments before you try again. The provisioning failures guide goes through every failure path in more detail.

Errors worth handling

Errors come back as {"error": {"code": "...", "message": "..."}}.

StatusCodeWhat to do
400offer_unavailableThe offer sold out between your search and the create. Try the next offer
400validation_error, invalid_ssh_key, image_incompatible, image_not_offeredFix the request. Retrying the same body will fail again
400provider_not_supportedThe offer is a container (Vast.ai). Pick a virtual offer
400offer_provider_mismatchThe provider value does not match the offer. Check the Massed Compute mapping above
402insufficient_balanceYour balance must cover one hour of the offer. Add credits
429rate_limitedSlow down
503offer_cooling_downThat offer just failed for capacity and is paused for about a minute. Wait for Retry-After or try another offer

A deployment that fails after the 202 carries failure_code, retryable and suggested_action, and the reserved first hour is refunded automatically.

Limits and billing

The limit is 3,000 requests per 15 minutes per IP, and every response carries the remaining count in the X-Rate-Limit-Remaining header. The offer search refreshes from the providers every minute, so polling it more often than that gains nothing.

A deployment launched through the API is billed exactly like one launched in the dashboard: the first hour is reserved at launch, each later hour is charged as it starts, and unused seconds come back when you stop. There is no idle timer, so a script that crashes before its stop call leaves a GPU running. The finally block above is the cheapest insurance, and the stop guide has a guardrail script that stops anything running longer than a limit you set. The pricing page covers top-ups and fees.

Frequently asked questions

Which providers can I launch on through the DeployGPU API?

QuantaCloud, Massed Compute, Verda and Hyperstack, as virtual machines. Vast.ai containers are available only in the dashboard.

Can I launch Jupyter, ComfyUI or Open WebUI through the API?

No. App templates and their private links are dashboard-only. Through the API you launch a plain VM and install what you need over SSH.

Is it safe to retry a create request?

Not blindly. There is no idempotency key, so a retried create can launch and bill a second GPU. List your deployments first and retry only if nothing was created.

How do I know when a deployment is ready?

Poll the deployment until its status is active, then read the SSH user, IP address and port from the connection field. Most VMs get there in 1 to 3 minutes.

Can I limit what an API key is allowed to do?

Not today. Keys have no scopes or expiry, so use one key per system, store it as a secret and delete it when it is no longer needed.