LeafcloudOpenAPI

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=true or pretty=false to force either way.
  • Table: add format=table for 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_instances checks GPUs and memory per host, so four free A100s spread over four hosts means four 1× instances, not one 4× instance. max_per_instance tells 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_of is when it was measured; stale: true means 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.
  • amount is your price, after any discount agreed with Leafcloud. list_amount is the public list price and discount_percent the difference.
  • Amounts are strings with exactly two decimals ("2.20"). Parse them as decimals, not floats.
GET

/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

NameInTypeDescription
gpu_modelquerystringOnly this GPU model (case-insensitive). Unknown models return 404 with the list of valid ones.
available_onlyquerybooleanOnly flavors with at least one instance available.
min_gpusqueryintegerOnly flavors with at least this many GPUs per instance (1-16).
formatqueryjson | tableResponse format. `table` returns aligned plain text, handy in a terminal. Default `json`.
prettyquerybooleanIndent 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

FieldTypeDescription
as_ofstring (ISO 8601)When the underlying capacity snapshot was taken.
stalebooleanTrue when the snapshot is older than 15 minutes. Treat numbers as indicative.
refresh_interval_secondsintegerHow often the data refreshes (300). Polling faster gains nothing.
filtersobjectThe filters that were applied (gpu_model, available_only, min_gpus).
gpus[].modelstringGPU model.
gpus[].availableintegerGPUs of this model free right now across all hosts.
gpus[].max_per_instanceintegerMost GPUs of this model free on a single host: the largest instance you could launch now.
gpus[].launchable_flavorsstring[]Public flavors of this model with at least one instance available.
gpus[].from_price_per_gpu_hourobject | nullCheapest price per GPU-hour across this model's flavors, after your discount.
flavors[].namestringFlavor name, as used when launching an instance (e.g. eg1.a100x1.V12-84).
flavors[].gpu_modelstringGPU model, e.g. A100, A30, RTXP6000.
flavors[].gpu_countintegerGPUs per instance.
flavors[].vcpusinteger | nullvCPUs per instance.
flavors[].ram_gbinteger | nullMemory per instance in GB.
flavors[].disk_gbinteger | nullLocal disk in GB, if the flavor includes one.
flavors[].descriptionstring | nullOptional notes from Leafcloud.
flavors[].available_instancesintegerHow many instances of this flavor you could launch right now.
flavors[].price.amountstringYour hourly price, after any agreed discount. Two decimals, e.g. "1.48".
flavors[].price.list_amountstringPublic list price per hour.
flavors[].price.discount_percentnumberYour discount off the list price (0 if none).
flavors[].price.currencystringISO 4217 currency, currently always EUR. Prices exclude VAT.
flavors[].price.unitstringAlways "hour".
GET

/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

NameInTypeDescription
formatqueryjson | tableResponse format. `table` returns aligned plain text, handy in a terminal. Default `json`.
prettyquerybooleanIndent 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

FieldTypeDescription
as_ofstring (ISO 8601)Snapshot time.
stalebooleanTrue when older than 15 minutes.
gpus[]object[]Same objects as `gpus` in /availability.
GET

/api/v1/flavors/{name}

One flavor

Specs, your price and current availability for a single flavor. Flavor names are case-insensitive.

Parameters

NameInTypeDescription
namepathstringFlavor name.
formatqueryjson | tableResponse format. `table` returns aligned plain text, handy in a terminal. Default `json`.
prettyquerybooleanIndent 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

FieldTypeDescription
as_ofstring (ISO 8601)Snapshot time.
stalebooleanTrue when older than 15 minutes.
flavorobjectSame object as an entry in `flavors` from /availability.
GET

/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

NameInTypeDescription
formatqueryjson | tableResponse format. `table` returns aligned plain text, handy in a terminal. Default `json`.
prettyquerybooleanIndent 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

FieldTypeDescription
currencystringCurrency of all prices (EUR).
prices_exclude_vatbooleanAlways true.
discount_percentnumberYour discount off the list price.
flavors[].namestringFlavor name, as used when launching an instance (e.g. eg1.a100x1.V12-84).
flavors[].gpu_modelstringGPU model, e.g. A100, A30, RTXP6000.
flavors[].gpu_countintegerGPUs per instance.
flavors[].vcpusinteger | nullvCPUs per instance.
flavors[].ram_gbinteger | nullMemory per instance in GB.
flavors[].disk_gbinteger | nullLocal disk in GB, if the flavor includes one.
flavors[].descriptionstring | nullOptional notes from Leafcloud.
flavors[].price.amountstringYour hourly price, after any agreed discount. Two decimals, e.g. "1.48".
flavors[].price.list_amountstringPublic list price per hour.
flavors[].price.discount_percentnumberYour discount off the list price (0 if none).
flavors[].price.currencystringISO 4217 currency, currently always EUR. Prices exclude VAT.
flavors[].price.unitstringAlways "hour".
GET

/api/v1/history

Availability over time

Hourly average of free GPUs per model, for spotting quiet hours or planning launches.

Parameters

NameInTypeDescription
rangequery24h | 7d | 30dTime window. Default 7d.
gpu_modelquerystringOnly this GPU model (case-insensitive).
formatqueryjson | tableResponse format. `table` returns aligned plain text, handy in a terminal. Default `json`.
prettyquerybooleanIndent 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

FieldTypeDescription
rangestringThe window used.
intervalstringBucket size, always "1h".
gpu_modelstring | nullThe model filter, if any.
points[].tstring (ISO 8601)Start of the hour (UTC).
points[].gpu_modelstringGPU model.
points[].availableintegerAverage 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."
}
StatusCodeMeaning
400bad_request / bad_rangeA query parameter is invalid. The message says which one.
401unauthorizedMissing or invalid API key.
403forbidden_ipYour key is restricted to other IP addresses.
404unknown_gpu_model / unknown_flavorNo such GPU model or public flavor. The message lists valid values where possible.
429rate_limitedToo many requests. Wait for the number of seconds in Retry-After.
500internal_errorSomething went wrong on our side. Safe to retry with backoff.
503no_dataCapacity 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 minutes

JavaScript / 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.