Leafcloud · API v1
GPU availability API
See which GPUs and flavors you can launch on Leafcloud right now, with your prices. Read-only, JSON, refreshed every five minutes. Base URL: https://availability.leaf.cloud
Quick start
Ask your Leafcloud contact for an API key, put it in an environment variable, and ask what's free:
export LEAFCLOUD_KEY="prisim_..." curl -H "Authorization: Bearer $LEAFCLOUD_KEY" \ "https://availability.leaf.cloud/api/v1/gpus?format=table"
GPU AVAILABLE MAX/INSTANCE FROM/GPU-HOUR LAUNCHABLE FLAVORS -------- --------- ------------ ------------- ------------------------------------------------------ A100 2 1 EUR 1.48 eg1.a100x1.V12-84 A30 14 6 EUR 0.55 eg1.a30x1.V8-32, eg1.a30x2.V16-64, eg1.a30x4.V32-128 as of 2026-10-19T12:30:09.638Z
Everything is GET; there's nothing to create or delete. Start with /gpus for a one-line-per-model overview, then use /availability to see exactly which flavors fit.
Authentication
Send your key as a bearer token on every request. Keys start with prisim_. Treat it like a password: keep it server-side, never in a browser app or a public repository.
Authorization: Bearer prisim_...
Keys can be restricted to your IP addresses; requests from elsewhere get 403 forbidden_ip. If a key leaks, tell us and we'll revoke it and issue a new one.
Output formats
- JSON (default). Compact for apps; indented automatically when you use curl, wget or HTTPie. Add
pretty=trueorpretty=falseto force either way. - Table: add
format=tablefor aligned plain text. Made for terminals and quick checks; use JSON in code.
Every response ends with a newline and carries an X-API-Version: 1 header.
How availability works
- Available means free right now on hosts that accept new instances. Capacity on hosts in maintenance is never counted.
- An instance must fit on one host.
available_instanceschecks GPUs and memory per host, so four free A100s spread over four hosts means four 1× instances, not one 4× instance.max_per_instancetells you the largest instance that fits right now. - Per-flavor counts aren't additive. Each flavor's count assumes you only launch that flavor; launching a 4× uses capacity a 1× could have used.
- Freshness. Data refreshes every 5 minutes (
refresh_interval_seconds).as_ofis when it was measured;stale: truemeans it's older than 15 minutes, so treat the numbers as indicative. - Not a reservation. Availability can change between your check and your launch. Scheduling capacity for a future date is on the roadmap.
Prices & discounts
- Prices are per instance per hour, in EUR, excluding VAT.
amountis your price, after any discount agreed with Leafcloud.list_amountis the public list price anddiscount_percentthe difference.- Amounts are strings with exactly two decimals (
"2.20"). Parse them as decimals, not floats.
/api/v1/availability
What you can launch right now
Free GPUs per model and, for every public flavor, how many instances fit right now. Counts only include hosts that accept new instances, and each instance must fit on a single host (GPUs and memory).
Parameters
| Name | In | Type | Description |
|---|---|---|---|
gpu_model | query | string | Only this GPU model (case-insensitive). Unknown models return 404 with the list of valid ones. |
available_only | query | boolean | Only flavors with at least one instance available. |
min_gpus | query | integer | Only flavors with at least this many GPUs per instance (1-16). |
format | query | json | table | Response format. `table` returns aligned plain text, handy in a terminal. Default `json`. |
pretty | query | boolean | Indent the JSON. On by default for curl, wget and HTTPie; `pretty=false` turns it off. |
Example
curl -H "Authorization: Bearer $LEAFCLOUD_KEY" \ "https://availability.leaf.cloud/api/v1/availability?gpu_model=A100&available_only=true"
{
"as_of": "2026-10-19T12:30:09.638Z",
"stale": false,
"refresh_interval_seconds": 300,
"filters": {
"gpu_model": "A100",
"available_only": true,
"min_gpus": null
},
"gpus": [
{
"model": "A100",
"available": 2,
"max_per_instance": 1,
"launchable_flavors": [
"eg1.a100x1.V12-84"
],
"from_price_per_gpu_hour": {
"amount": "1.48",
"currency": "EUR",
"unit": "gpu_hour"
}
}
],
"flavors": [
{
"name": "eg1.a100x1.V12-84",
"gpu_model": "A100",
"gpu_count": 1,
"vcpus": 12,
"ram_gb": 84,
"disk_gb": null,
"description": null,
"available_instances": 2,
"price": {
"amount": "1.48",
"list_amount": "1.61",
"discount_percent": 8,
"currency": "EUR",
"unit": "hour"
}
}
]
}With format=table:
GPUs GPU AVAILABLE MAX/INSTANCE FROM/GPU-HOUR LAUNCHABLE FLAVORS ---- --------- ------------ ------------- ------------------ A100 2 1 EUR 1.48 eg1.a100x1.V12-84 Flavors FLAVOR GPU AVAILABLE VCPU RAM GB PRICE/HOUR ----------------- ------- --------- ---- ------ --------------------- eg1.a100x1.V12-84 1x A100 2 12 84 EUR 1.48 (list 1.61) as of 2026-10-19T12:30:09.638Z
Response fields
| Field | Type | Description |
|---|---|---|
as_of | string (ISO 8601) | When the underlying capacity snapshot was taken. |
stale | boolean | True when the snapshot is older than 15 minutes. Treat numbers as indicative. |
refresh_interval_seconds | integer | How often the data refreshes (300). Polling faster gains nothing. |
filters | object | The filters that were applied (gpu_model, available_only, min_gpus). |
gpus[].model | string | GPU model. |
gpus[].available | integer | GPUs of this model free right now across all hosts. |
gpus[].max_per_instance | integer | Most GPUs of this model free on a single host: the largest instance you could launch now. |
gpus[].launchable_flavors | string[] | Public flavors of this model with at least one instance available. |
gpus[].from_price_per_gpu_hour | object | null | Cheapest price per GPU-hour across this model's flavors, after your discount. |
flavors[].name | string | Flavor name, as used when launching an instance (e.g. eg1.a100x1.V12-84). |
flavors[].gpu_model | string | GPU model, e.g. A100, A30, RTXP6000. |
flavors[].gpu_count | integer | GPUs per instance. |
flavors[].vcpus | integer | null | vCPUs per instance. |
flavors[].ram_gb | integer | null | Memory per instance in GB. |
flavors[].disk_gb | integer | null | Local disk in GB, if the flavor includes one. |
flavors[].description | string | null | Optional notes from Leafcloud. |
flavors[].available_instances | integer | How many instances of this flavor you could launch right now. |
flavors[].price.amount | string | Your hourly price, after any agreed discount. Two decimals, e.g. "1.48". |
flavors[].price.list_amount | string | Public list price per hour. |
flavors[].price.discount_percent | number | Your discount off the list price (0 if none). |
flavors[].price.currency | string | ISO 4217 currency, currently always EUR. Prices exclude VAT. |
flavors[].price.unit | string | Always "hour". |
/api/v1/gpus
One line per GPU model
A compact summary per GPU model: free GPUs, the largest instance you can launch now, which flavors fit, and the cheapest price per GPU-hour.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
format | query | json | table | Response format. `table` returns aligned plain text, handy in a terminal. Default `json`. |
pretty | query | boolean | Indent the JSON. On by default for curl, wget and HTTPie; `pretty=false` turns it off. |
Example
curl -H "Authorization: Bearer $LEAFCLOUD_KEY" \ "https://availability.leaf.cloud/api/v1/gpus"
{
"as_of": "2026-10-19T12:30:09.638Z",
"stale": false,
"refresh_interval_seconds": 300,
"gpus": [
{
"model": "A100",
"available": 2,
"max_per_instance": 1,
"launchable_flavors": [
"eg1.a100x1.V12-84"
],
"from_price_per_gpu_hour": {
"amount": "1.48",
"currency": "EUR",
"unit": "gpu_hour"
}
},
{
"model": "A30",
"available": 14,
"max_per_instance": 6,
"launchable_flavors": [
"eg1.a30x1.V8-32",
"eg1.a30x2.V16-64",
"eg1.a30x4.V32-128"
],
"from_price_per_gpu_hour": {
"amount": "0.55",
"currency": "EUR",
"unit": "gpu_hour"
}
}
]
}With format=table:
GPU AVAILABLE MAX/INSTANCE FROM/GPU-HOUR LAUNCHABLE FLAVORS -------- --------- ------------ ------------- ------------------------------------------------------ A100 2 1 EUR 1.48 eg1.a100x1.V12-84 A30 14 6 EUR 0.55 eg1.a30x1.V8-32, eg1.a30x2.V16-64, eg1.a30x4.V32-128 as of 2026-10-19T12:30:09.638Z
Response fields
| Field | Type | Description |
|---|---|---|
as_of | string (ISO 8601) | Snapshot time. |
stale | boolean | True when older than 15 minutes. |
gpus[] | object[] | Same objects as `gpus` in /availability. |
/api/v1/flavors/{name}
One flavor
Specs, your price and current availability for a single flavor. Flavor names are case-insensitive.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
name | path | string | Flavor name. |
format | query | json | table | Response format. `table` returns aligned plain text, handy in a terminal. Default `json`. |
pretty | query | boolean | Indent the JSON. On by default for curl, wget and HTTPie; `pretty=false` turns it off. |
Example
curl -H "Authorization: Bearer $LEAFCLOUD_KEY" \ "https://availability.leaf.cloud/api/v1/flavors/eg1.a100x1.V12-84"
{
"as_of": "2026-10-19T12:30:09.638Z",
"stale": false,
"flavor": {
"name": "eg1.a100x1.V12-84",
"gpu_model": "A100",
"gpu_count": 1,
"vcpus": 12,
"ram_gb": 84,
"disk_gb": null,
"description": null,
"available_instances": 2,
"price": {
"amount": "1.48",
"list_amount": "1.61",
"discount_percent": 8,
"currency": "EUR",
"unit": "hour"
}
}
}Response fields
| Field | Type | Description |
|---|---|---|
as_of | string (ISO 8601) | Snapshot time. |
stale | boolean | True when older than 15 minutes. |
flavor | object | Same object as an entry in `flavors` from /availability. |
/api/v1/pricing
Price list
Every public flavor with its hourly price, after your discount. Doesn't depend on live capacity, so it's safe to cache for a day.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
format | query | json | table | Response format. `table` returns aligned plain text, handy in a terminal. Default `json`. |
pretty | query | boolean | Indent the JSON. On by default for curl, wget and HTTPie; `pretty=false` turns it off. |
Example
curl -H "Authorization: Bearer $LEAFCLOUD_KEY" \ "https://availability.leaf.cloud/api/v1/pricing"
{
"currency": "EUR",
"prices_exclude_vat": true,
"discount_percent": 8,
"flavors": [
{
"name": "eg1.a100x1.V12-84",
"gpu_model": "A100",
"gpu_count": 1,
"vcpus": 12,
"ram_gb": 84,
"disk_gb": null,
"description": null,
"price": {
"amount": "1.48",
"list_amount": "1.61",
"discount_percent": 8,
"currency": "EUR",
"unit": "hour"
}
}
]
}Response fields
| Field | Type | Description |
|---|---|---|
currency | string | Currency of all prices (EUR). |
prices_exclude_vat | boolean | Always true. |
discount_percent | number | Your discount off the list price. |
flavors[].name | string | Flavor name, as used when launching an instance (e.g. eg1.a100x1.V12-84). |
flavors[].gpu_model | string | GPU model, e.g. A100, A30, RTXP6000. |
flavors[].gpu_count | integer | GPUs per instance. |
flavors[].vcpus | integer | null | vCPUs per instance. |
flavors[].ram_gb | integer | null | Memory per instance in GB. |
flavors[].disk_gb | integer | null | Local disk in GB, if the flavor includes one. |
flavors[].description | string | null | Optional notes from Leafcloud. |
flavors[].price.amount | string | Your hourly price, after any agreed discount. Two decimals, e.g. "1.48". |
flavors[].price.list_amount | string | Public list price per hour. |
flavors[].price.discount_percent | number | Your discount off the list price (0 if none). |
flavors[].price.currency | string | ISO 4217 currency, currently always EUR. Prices exclude VAT. |
flavors[].price.unit | string | Always "hour". |
/api/v1/history
Availability over time
Hourly average of free GPUs per model, for spotting quiet hours or planning launches.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
range | query | 24h | 7d | 30d | Time window. Default 7d. |
gpu_model | query | string | Only this GPU model (case-insensitive). |
format | query | json | table | Response format. `table` returns aligned plain text, handy in a terminal. Default `json`. |
pretty | query | boolean | Indent the JSON. On by default for curl, wget and HTTPie; `pretty=false` turns it off. |
Example
curl -H "Authorization: Bearer $LEAFCLOUD_KEY" \ "https://availability.leaf.cloud/api/v1/history?range=24h&gpu_model=A30"
{
"range": "24h",
"interval": "1h",
"gpu_model": "A30",
"points": [
{
"t": "2026-10-19T10:00:00.000Z",
"gpu_model": "A30",
"available": 14
},
{
"t": "2026-10-19T11:00:00.000Z",
"gpu_model": "A30",
"available": 12
}
]
}Response fields
| Field | Type | Description |
|---|---|---|
range | string | The window used. |
interval | string | Bucket size, always "1h". |
gpu_model | string | null | The model filter, if any. |
points[].t | string (ISO 8601) | Start of the hour (UTC). |
points[].gpu_model | string | GPU model. |
points[].available | integer | Average free GPUs in that hour, rounded. |
Errors
Errors use HTTP status codes and a JSON body with a stable error code and a human-readable message:
{
"error": "unknown_gpu_model",
"message": "No GPU model \"H200\". Known: A100, A30, RTXP6000, V100."
}| Status | Code | Meaning |
|---|---|---|
| 400 | bad_request / bad_range | A query parameter is invalid. The message says which one. |
| 401 | unauthorized | Missing or invalid API key. |
| 403 | forbidden_ip | Your key is restricted to other IP addresses. |
| 404 | unknown_gpu_model / unknown_flavor | No such GPU model or public flavor. The message lists valid values where possible. |
| 429 | rate_limited | Too many requests. Wait for the number of seconds in Retry-After. |
| 500 | internal_error | Something went wrong on our side. Safe to retry with backoff. |
| 503 | no_data | Capacity data is temporarily unavailable. |
Rate limits
Each key has a per-minute limit (60 unless agreed otherwise). Every response includes RateLimit-Limit, RateLimit-Remaining and RateLimit-Reset (seconds). Over the limit you get 429 with Retry-After. Requests without a valid key are limited per IP address.
The data changes every five minutes, so polling once a minute is plenty. /pricing can be cached for a day.
Recipes
Flavors I can launch right now
curl -H "Authorization: Bearer $LEAFCLOUD_KEY" \ "https://availability.leaf.cloud/api/v1/availability?available_only=true" \ | jq -r '.flavors[] | "\(.name)\t\(.available_instances)\tEUR \(.price.amount)/h"'
Is a 4-GPU A100 free?
curl -H "Authorization: Bearer $LEAFCLOUD_KEY" \ "https://availability.leaf.cloud/api/v1/availability?gpu_model=A100&min_gpus=4&available_only=true" \ | jq -e '.flavors | length > 0' >/dev/null && echo "yes" || echo "not right now"
Python: wait for capacity
import os, time, requests
URL = "https://availability.leaf.cloud/api/v1/availability"
HEADERS = {"Authorization": f"Bearer {os.environ['LEAFCLOUD_KEY']}"}
while True:
r = requests.get(URL, headers=HEADERS, params={"gpu_model": "A100", "available_only": "true"}, timeout=10)
r.raise_for_status()
flavors = r.json()["flavors"]
if flavors:
print("Launchable:", [f["name"] for f in flavors])
break
time.sleep(60) # data refreshes every 5 minutesJavaScript / TypeScript
const res = await fetch("https://availability.leaf.cloud/api/v1/gpus", {
headers: { Authorization: `Bearer ${process.env.LEAFCLOUD_KEY}` },
});
if (!res.ok) throw new Error(`Leafcloud API ${res.status}: ${(await res.json()).message}`);
const { gpus } = await res.json();
console.table(gpus.map((g) => ({ gpu: g.model, free: g.available, largest: g.max_per_instance })));OpenAPI & versioning
A machine-readable spec lives at /api/v1/openapi.json (no key needed). Import it into Postman, Insomnia or a client generator. /api/v1 lists all endpoints.
Within v1 we only add things: new endpoints, new fields, new optional parameters. Ignore fields you don't recognise. Anything breaking would ship as /api/v2 with notice.
Support
For questions, a new key, or a key to revoke, contact your Leafcloud account manager or support@leaf.cloud. If something failed, include the request time and the error code.