CloudGPU for AI agents: integration guide
This guide is for agents and the people who build them. It covers a public GPU catalog, a quote API, connect links that give an agent its own key with a budget, the Jobs API, and the GPU instances API. The MCP server uses the same endpoints. The machine-readable description of every endpoint is at openapi.json.
Base URL: https://cloudgpu.app. Errors always look like {"error": {"type", "code", "message"}}.
1. Discover: the public GPU catalog
GET /v1/public/gpu-catalog. No key needed.
curl -s https://cloudgpu.app/v1/public/gpu-catalog
| Field | Meaning |
|---|---|
gpus[] |
model, vram_gb, price_per_gpu_hour_usd, min_gpu_count, availability (available, limited, unavailable), tier |
billing |
increment_seconds, minimum_seconds, preauthorized_seconds, description |
disk_options[] |
disk_gb and addon_per_hour_usd for each system disk size you can request; 0 is the standard disk |
templates[] |
Ready templates with id, recommended_gpu and min_vram_gb |
generated_at, cache_ttl_seconds |
When the data was read, and how long you may cache it |
Prices and availability change during the day. Read the catalog, or request a quote, right before you start an instance.
2. Quote: estimate a task
POST /v1/quotes. No key needed. Send a key to also learn whether your balance can start each candidate.
curl -s https://cloudgpu.app/v1/quotes \
-H 'Content-Type: application/json' \
-d '{
"requirements": {"min_vram_gb": 24, "gpu_count": 1},
"estimated_duration_seconds": 1800,
"max_budget_usd": "1.00",
"sort_by": "estimated_total_cost"
}'
requirements can also carry gpu_model, template and disk_gb. Each entry in candidates[] has:
| Field | Meaning |
|---|---|
estimated_total_cost |
USD the task is expected to use: billed minutes × the instance’s hourly price |
required_starting_balance |
USD your balance needs to start the instance. One hour is pre-authorised at start, so this is often higher than the estimate for short tasks |
within_budget |
Whether estimated_total_cost is within max_budget_usd. It does not mean you can start now |
can_start_with_balance |
Only set when you send an API key: whether that account’s balance covers required_starting_balance. null without a key |
instance_price_per_hour_usd, billed_minutes, availability |
The inputs to the estimate |
supports_jobs |
Whether this GPU can run a job with POST /v1/jobs right now. Some GPUs can only be rented as instances |
A quote has an id and an expires_at 5 minutes later. It is an estimate (price_guarantee: "estimate"), not a reservation.
3. Connect: get a key with a budget
An agent without a key asks the account owner for one. Nothing is charged.
curl -s https://cloudgpu.app/v1/agent/connect \
-H 'Content-Type: application/json' \
-d '{"agent_name":"Build agent on ci-runner-3","spend_limit_usd":20,"max_concurrent":1,"expires_in_days":30}'
The response has a device_code (keep it secret, in the agent), a user_code such as ABCD-EFGH and a verification_uri_complete link. Show the link to the account owner. They sign in, check the agent’s name and limits, may lower them, and approve or decline.
Meanwhile call POST /v1/agent/connect/token with {"device_code": "..."} every interval seconds. It returns 400 authorization_pending until the owner approves, 403 access_denied if they decline, and 400 expired_token after 10 minutes. On approval it returns api_key once, with spend_limit_usd, max_concurrent and expires_at.
That key is refused, on CloudGPU’s servers, when:
- a job or instance would take its total spend over
spend_limit_usd(403 key_budget_exceeded). Unfinished jobs and instances count at theirmax_spend_usd, finished ones at what they cost, so every request with this key must setmax_spend_usd; - it already runs
max_concurrentjobs or instances (429 key_concurrency_exceeded); - it has expired or the owner revoked it on the API page (
401). A revoked key cannot start anything, and a job it queued that has not started a GPU yet is stopped.
4. Run a job
POST /v1/jobs runs one bash command on a GPU from start to finish: CloudGPU starts the GPU, runs the command, and destroys the GPU when the command exits, on its servers. Needs a key and an Idempotency-Key header.
curl -s https://cloudgpu.app/v1/jobs \
-H "Authorization: Bearer $CLOUDGPU_API_KEY" -H 'Content-Type: application/json' \
-H 'Idempotency-Key: 6f1c0a2e-6e0b-4b3a-9a59-2d9c3f1c7e11' \
-d '{
"gpu_model": "RTX 4090",
"command": "python train.py && rclone copy out/ mystorage:run1",
"max_minutes": 60,
"max_spend_usd": 2.00
}'
| Field | Meaning |
|---|---|
command |
Bash, run once. The GPU’s disk is erased when the job ends: upload results from the command itself, or print them |
gpu_model, template, gpu_count, disk_gb, quote_id |
As for instances below. quote_id binds the job to a quote |
max_minutes, max_spend_usd |
Required. Limits for the whole job, enforced by CloudGPU’s servers |
Idempotency. Use a new Idempotency-Key (a UUID) for each new job, and the same one when you retry. The same key with the same request returns the existing job (200, header Idempotent-Replayed: true) and never starts a second GPU, so it is safe to retry after a timeout. The same key with different parameters returns 409 idempotency_key_reused.
The call returns 202 with the job. Read it with GET /v1/jobs/{id}:
status |
Meaning |
|---|---|
queued |
Accepted, no GPU yet |
provisioning |
Starting the GPU |
running |
The command is running |
stopping |
The command finished, or a limit or a cancel was hit; the GPU is being destroyed |
succeeded |
The command exited with code 0 and the GPU is destroyed |
failed |
See error.code: command_failed (non-zero exit_code), time_limit_reached, spend_limit_reached, insufficient_balance, provisioning_failed, credential_revoked and others |
cancelled |
Cancelled with POST /v1/jobs/{id}/cancel |
needs_attention |
CloudGPU stopped the job because it could not confirm the state of its GPU after an error. It is not retried automatically and the team is alerted. Do not resubmit it with a new key; contact support if it stays there |
A job also has exit_code, cost_usd, instance_id, and timestamps. GET /v1/jobs/{id}/logs returns the last ~4 KB of the command’s output and GET /v1/jobs/{id}/events its history. A job keeps running if your session ends, and stops at its limits.
5. Or rent an instance
POST /v1/gpu/instances. Needs Authorization: Bearer <api key>; keys start with cgw-sk- and are created at cloudgpu.app/api.
curl -s https://cloudgpu.app/v1/gpu/instances \
-H "Authorization: Bearer $CLOUDGPU_API_KEY" -H 'Content-Type: application/json' \
-d '{
"quote_id": "quote_...",
"max_minutes": 40,
"max_spend_usd": 1.00,
"idle_minutes": 20
}'
| Field | Meaning |
|---|---|
gpu_model or template |
What to start. With quote_id and no gpu_model, the quote’s first candidate is used |
quote_id |
Optional. Binds the start to the quote: gpu_count, template and disk_gb you leave out are taken from the quote, and ones you send must match it. Fails with 409 and starts nothing if the quote expired, the configuration differs, the GPU is gone or its price went up (see the error table) |
max_minutes, max_spend_usd, idle_minutes |
Limits that CloudGPU’s servers enforce. The instance is destroyed when one is reached, even if your session has ended |
setup_script |
Optional bash script run as root after the instance is handed over |
disk_gb |
Optional larger system disk, one of the catalog’s disk_options. Charged per hour on top of the GPU price |
The call returns 202 with status: "creating". Poll GET /v1/gpu/instances/{id} until status is running; the response then carries SSH details and, for templates with a web service, an api_url.
Do not retry an instance create blindly. Unlike jobs, POST /v1/gpu/instances takes no idempotency key: the same request sent twice can start two machines. If it times out, list your instances with GET /v1/gpu/instances and look for one created in the last few minutes with the same GPU model before you try again; when in doubt, destroy the extra one. For work that fits one command, use a job instead.
6. Run and collect results
Use the SSH details from the instance, or the MCP server’s upload, run_command, tail_logs and download tools. Copy results off before you destroy the instance: destroying it erases its disk.
7. Stop
POST /v1/gpu/instances/{id}/destroy. Billing stops when the instance is destroyed. If you set max_minutes or max_spend_usd, the instance is also destroyed when the limit is reached.
Billing
Per minute, rounded up, with a 1-minute minimum. One hour is pre-authorised while an instance runs, and unused minutes go back to your balance when you destroy the instance. There is no subscription and no minimum term.
Limits and errors
| HTTP | code |
What the agent should do |
|---|---|---|
| 409 | quote_expired |
Request a new quote |
| 409 | gpu_unavailable |
Choose another GPU from a new quote |
| 409 | price_changed |
Request a new quote and check it against the budget again |
| 409 | quote_mismatch |
Fix gpu_count, template or disk_gb to match the quote, or request a quote for the new configuration |
| 402 | insufficient_balance |
Ask the user to top up; do not retry until the balance covers required_starting_balance |
| 401 | missing_authorization, invalid_api_key |
Ask the user for a valid API key |
| 429 | too_many_instances |
Destroy an instance you no longer need, or wait |
| 429 | rate limit | Wait and retry later. The catalog and quote endpoints are limited per IP, the GPU API per key |
| 400 | invalid_request |
Fix the field named in the message |
| 400 | idempotency_key_required |
Send an Idempotency-Key header with the job |
| 409 | idempotency_key_reused |
That key belongs to a different job. Use a new key, or resend the original request unchanged to get that job |
| 400 | max_spend_required |
This key has a spending limit; set max_spend_usd |
| 403 | key_budget_exceeded |
Lower max_spend_usd, or ask the owner to connect the agent again with a higher limit |
| 429 | key_concurrency_exceeded |
Wait for a job or instance of this key to finish, or cancel one |
| 401 | credential_revoked |
The key was revoked or expired; connect again |
Safety rules for agents
- Show the user the quote, including
required_starting_balance, and get their approval before starting a paid instance. - Always set
max_minutesandmax_spend_usd. - Reuse the same
Idempotency-Keywhen you retry a job, and a new one for a new job. - Never print or log the API key.
- Download results before destroying an instance.
- If anything fails, destroy the instance rather than leave it running.
A ready-made skill for agents that load skill files is at /skills/cloudgpu-compute/SKILL.md.
What CloudGPU does not do
CloudGPU does not keep files. A job’s or an instance’s disk is erased when it ends, so results must be uploaded to your own storage or downloaded first. A job runs exactly one command; for interactive work, rent an instance.
FAQ
Is the quote a price guarantee?
No. It is an estimate valid for 5 minutes. Pass quote_id when you start the instance: if the price went up or the GPU is gone, the call fails instead of starting a dearer GPU.
Why can a task be within budget but not startable?
within_budget compares the estimated cost with your budget. Starting also needs required_starting_balance in your account, because one hour is pre-authorised. Send your API key with the quote to get can_start_with_balance.
Can a job start two GPUs if my request is sent twice?
No, as long as both requests carry the same Idempotency-Key: the second returns the first job. If CloudGPU’s service restarts while it is starting the GPU, it checks for that GPU before trying again and stops the job as needs_attention when it cannot tell, rather than start another.
Can an agent use CloudGPU without MCP?
Yes. Every step is a plain HTTPS request, described in the OpenAPI file.
What happens if the agent’s session ends while an instance or job runs?
It keeps running until it finishes, is destroyed, or reaches a limit. Set max_minutes and max_spend_usd so CloudGPU’s servers shut it down.