Skip to main content

GPU instances (Compute API)

Everything you can do with GPU instances in the console — launch, list, get the SSH command, extend, terminate — is available over the API with an API key that has Compute access. It is the same product: same endpoints the console uses, same prices, same credit holds and refunds, same limits on your account.

This page covers GPU instances. See also GPU clusters, Storage and SSH keys.

Before you start​

A key with Compute access (Read-only to view and quote, Full to launch) and credit — see the Compute API overview, which also has the shared rules: auth, region ids, billing, rate limits, errors.

https://api.ecohash.com
Authorization: Bearer eco_YOUR_KEY

The lifecycle in five calls​

KEY="eco_YOUR_KEY"
API="https://api.ecohash.com"

# 1. What can I launch, where, for how much?
curl -s $API/gpu-instances/availability -H "Authorization: Bearer $KEY"
curl -s $API/platform/gpu-prices

# 2. Launch — no end time, billed hour by hour until you terminate it.
# A platform image: it includes OpenSSH, which SSH access needs.
curl -s -X POST $API/gpu-instances \
-H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
-d '{
"name": "api-test",
"region_id": "<region id from availability>",
"gpu_type": "<gpu_type from availability>",
"gpu_count": 1,
"container_image": "public.ecr.aws/a2b7e2y7/ecolink/gpu-base:v0.1.0",
"estimated_duration_hours": 0,
"ssh_enabled": true,
"ssh_public_key": "ssh-ed25519 AAAA... you@laptop"
}'
# → 202 with the instance; note its "id"

# 3. Poll until "ssh_ready": true — "ssh_command" appears with it
curl -s $API/gpu-instances/<id> -H "Authorization: Bearer $KEY"

# 4. Connect
ssh -o HostKeyAlias=ecohash-instance-<id> -p <port> root@<host> # exactly the "ssh_command" string

# 5. Stop it — GPUs released, unused held credit refunded
curl -s -X POST $API/gpu-instances/<id>/terminate -H "Authorization: Bearer $KEY"

Endpoints​

MethodPathWhat it does
GET/gpu-instances/availabilityFree GPUs per region and what one GPU comes with
GET/platform/gpu-pricesPrice per GPU-hour by type (no auth needed)
GET/gpu-instancesYour instances: everything live, plus ended ones from the last 7 days (add ?ended=all for every ended one)
POST/gpu-instancesLaunch
GET/gpu-instances/{id}One instance, with the credit currently held for it
POST/gpu-instances/{id}/terminateStop it
POST/gpu-instances/{id}/extendAdd hours to a fixed-duration instance
PATCH/gpu-instances/{id}Change image / command / init script / service port and rebuild
DELETE/gpu-instances/{id}Hide a terminated or failed instance from the list (billing history is kept)
GET/billing/balanceAvailable credit

The web terminal, file upload and file browser are console-only; over the API you connect with SSH.

GET /gpu-instances/availability​

One row per region. available is live — it is how many GPUs a launch can get right now.

[
{
"id": "atl",
"name": "Atlanta",
"gpu_type": "NVIDIA-RTX-PRO-6000-Blackwell-Server-Edition",
"gpu_per_node": 8,
"total_nodes": 7,
"available": 44,
"node_count": 7,
"vcpu_per_gpu": 16,
"ram_gb_per_gpu": 64,
"scratch_disk_gib": 400
}
]

Use id as region_id and gpu_type exactly as returned when launching.

GET /platform/gpu-prices​

[
{
"gpu_type": "NVIDIA-RTX-PRO-6000-Blackwell-Server-Edition",
"display_name": "RTX Pro 6000",
"hourly_rate_usd": 1.89,
"vram_gb": 96
}
]

On-demand prices only. Spot, where it is offered to your account, is priced on the availability rows — see Spot instances.

POST /gpu-instances​

FieldTypeRequiredNotes
region_idstringyesFrom availability
gpu_typestringyesFrom availability, exactly
gpu_countintyes1–8, not more than the region's gpu_per_node
container_imagestringyesOne of the platform images (public.ecr.aws/a2b7e2y7/ecolink/gpu-base:v0.1.0 for general work — the full list is under Recommended base images) or any public image. A private image works too — register it first with its credentials, see Container images. SSH needs OpenSSH in the image; platform images have it, most third-party images don't
estimated_duration_hoursintno0 (or omitted) = no end time, billed hourly until you terminate it. 1–72 = fixed run, held up front, auto-stops when it elapses. See Duration.
namestringnoShown in lists
startup_commandstringnoOverrides the image's command. sleep infinity keeps an image alive that would otherwise exit
init_scriptstringnoShell run once at boot, before the startup command (apt/pip installs). See Init scripts
ssh_enabledboolnoSet true with ssh_public_key to get an SSH command back. Only works with an image that includes OpenSSH (platform images do)
ssh_public_keystringnoOne OpenSSH public key (ssh-ed25519 … / ssh-rsa …)
service_portintnoExpose this container port at https://api.ecohash.com/gpu-instances/{id}/service/ (authenticated with the same key)
interruptibleboolnotrue launches a spot instance: cheaper, but its GPU can be taken at any time — with a duration it pauses and resumes, with no end time it stops. Only where availability shows a spot_hourly_rate_usd
template_idintnoThe template this launch came from. It records where the launch came from and does not fill in any other field: send the template's spec fields with it
user_model_idintnoMount a registered model's weights. Your own model: its shared filesystem, writable. A model another account published: that model's directory only, read-only, at the path its startup command uses. The instance must be in the model's region; any other is refused with a message naming it
cloud_drives, new_cloud_drives, shared_filesystems, new_shared_filesystemsarraysnoAttach or create storage at launch. See Storage. A mount path that overlaps the model weights' path is refused

Returns 202 Accepted with the instance (status pending). The credit hold is taken in the same request — if it cannot be, nothing is created and you get 402.

Spot instances​

A spot instance costs less per GPU-hour than on-demand, in exchange for one rule: its GPU can be taken at any time when the capacity is needed, with about 5 seconds' notice. What happens then depends on its duration.

  • Spot is offered only where your account's GET /gpu-instances/availability rows show spot_hourly_rate_usd. Launch with "interruptible": true.
  • spot_available is how many GPUs a spot launch can take now, and spot_max_gpus_per_instance is the largest spot instance: 1, 2 or 4 GPUs. Your account can run a limited number of spot instances and GPUs at once; a launch over a limit is refused with 409 and code spot_limit.
  • With a fixed duration (estimated_duration_hours 1–72), it pauses and comes back on its own:
    • while paused its status is preempted, paused_at says since when, and it is not billed;
    • it resumes automatically when a GPU frees up, restarted from its image, with its drives and filesystems attached;
    • it runs until its running time reaches the duration you bought, however many times it is paused. Time spent paused is added to its end time (paused_seconds is the total so far);
    • you get a notification each time it pauses and resumes;
    • there is no limit on how long it waits. Terminate it at any time: you are billed only for the time it ran, and the rest of the hold is refunded.
  • With no end time, it does not come back. Its status becomes terminated with termination_reason spot_reclaimed, you get a notification, and you are billed only until it stopped. The rest of the hold is refunded.
  • Either way, memory, running processes and the container's own disk are lost. Use spot for work you can restart: checkpoint to a cloud drive or shared filesystem.
  • Spot isn't available for clusters, model instances or reserved nodes.

The instance object​

Returned by list, get, launch and terminate.

FieldMeaning
id, name
statuspending → running → terminating_requested → terminated; also terminating_low_balance, failed, and preempted while a spot instance is paused
region_id, gpu_type, gpu_count, container_image, startup_command, init_script, service_portWhat you launched
user_model_idThe model whose weights are mounted; absent when none
hourly_rate_usdRate for the whole instance (per GPU × count)
interruptibletrue for a spot instance
estimated_duration_hoursnull = no end time (billed hourly); a number = fixed run
paused_seconds, paused_atSpot with a fixed duration: total time spent paused so far (added to the end time, not billed), and since when it is paused now (null while running)
ssh_enabled, ssh_tunnel_host, ssh_tunnel_port, ssh_ready, ssh_commandssh_ready is a live check: true once the instance actually accepts SSH connections. ssh_command (ssh -o HostKeyAlias=ecohash-instance-<id> -p <port> root@<host>; connect as root with the key you passed) is present only when ssh_ready is true — poll until it appears, usually within a minute of running. If it never appears, the image has no OpenSSH (see SSH access)
ssh_host_fingerprint, ssh_known_hosts_lineThe instance's own SSH host key: its SHA256:… fingerprint, to check on the first connection, and the line to add to ~/.ssh/known_hosts so a script connects without a prompt. Kept for the instance's whole life. Absent until the instance has been started with its own key
jupyter_urlFor the Jupyter platform image
created_at, started_at, terminated_atBilling runs from started_at (the pod is up), not from creation
termination_reasonuser, duration_expired, credit_depleted, spot_reclaimed (a spot instance stopped because its capacity was needed), failed, admin
created_by_user_idWhich member of the account launched it

GET /gpu-instances/{id} wraps it: { "instance": {…}, "held_amount": 1.89, "cloud_drives": […], "shared_filesystems": […] }.

These are the fields we commit to. Anything else that appears in a response is not part of the contract and may change; fields are only ever added, never renamed or removed.

POST /gpu-instances/{id}/terminate​

No body. Returns 202 with the instance in terminating_requested. The pod stops within seconds; the unused part of the hold is refunded once it has. 409 if the instance is not running.

POST /gpu-instances/{id}/extend​

{ "hours": 2 }

Fixed-duration instances only. Holds hours × hourly_rate_usd now and moves the auto-stop time. Returns 400 for an instance with no end time — it has nothing to extend.

PATCH /gpu-instances/{id}​

Any of container_image, startup_command, init_script, service_port. The instance is rebuilt with the new values; GPUs, region and billing are unchanged. See Redeploy.

Billing, rate limits, errors​

As on the overview. In short: no end time = first hour held, then settled and re-held hourly, stops at $0; fixed duration = whole run held, auto-stop, early-terminate refund. 60 requests/min per key, 10 launches/min per account.

Python example​

import time, requests

API, KEY = "https://api.ecohash.com", "eco_YOUR_KEY"
H = {"Authorization": f"Bearer {KEY}"}

regions = requests.get(f"{API}/gpu-instances/availability", headers=H).json()
r = next(x for x in regions if x["available"] > 0)

inst = requests.post(f"{API}/gpu-instances", headers=H, json={
"name": "from-python",
"region_id": r["id"],
"gpu_type": r["gpu_type"],
"gpu_count": 1,
"container_image": "public.ecr.aws/a2b7e2y7/ecolink/gpu-base:v0.1.0", # has OpenSSH
"estimated_duration_hours": 0, # no end time
"ssh_enabled": True,
"ssh_public_key": open("~/.ssh/id_ed25519.pub").read().strip(),
}).json()

while True:
d = requests.get(f"{API}/gpu-instances/{inst['id']}", headers=H).json()["instance"]
if d["status"] == "running" and d.get("ssh_command"):
print(d["ssh_command"]); break
if d["status"] in ("failed", "terminated"):
raise SystemExit(d.get("termination_reason"))
time.sleep(5)

# ... work over SSH ...

requests.post(f"{API}/gpu-instances/{inst['id']}/terminate", headers=H)

Other compute resources​

GPU clusters · Storage — cloud drives and shared filesystems · SSH keys · Templates: list, quote, and launch from one.

Working from Claude Code, Codex or Claude Desktop? The same actions are available as tools — see GPU MCP server.

Your privacy choices

Essential cookies are always on. You can change these choices at any time.