Normalized read access to organization quotas, limits, and current usage — the primitives an application needs to build its own admission control or per-tenant limit logic on top of Daytona's org-level enforcement.
{:ok, overview} = ExDaytona.Quota.overview(client, org_id)
# => %{snapshots: %{used: 2, total: 10},
# volumes: %{used: 0.0, total: 100.0},
# regions: [%{region_id: "us", sandbox_class: "small",
# cpu: %{used: 4, total: 64}, memory: %{...}, ...}]}
{:ok, headroom} = ExDaytona.Quota.headroom(client, org_id, region: "us", class: "small")
# => %{cpu: 60, memory: 120, disk: 800, gpu: 0}
{:ok, limits} = ExDaytona.Quota.limits(client, org_id)
# => per-sandbox maxima, secret quota, create/lifecycle rate limitsThe SDK deliberately ships no per-user limit enforcement — these
are read primitives. An application imposing finer-grained limits than
the organization's (e.g. per end-user of a SaaS product) can combine
them with label-scoped sandbox listing
(ExDaytona.Sandbox.list(client, labels: %{"my-app/tenant" => id}))
and the metering rollups in ExDaytona.Platform. Note that any such
application-side check is advisory (check-then-act) — Daytona enforces
only the organization-level quota.
Summary
Types
A used-vs-total pair for one resource dimension.
Quota and usage for one region × sandbox-class combination.
Functions
Remaining capacity for one region × sandbox-class:
{:ok, %{cpu: n, memory: n, disk: n, gpu: n}} (each
total - used, nil when the provider reports no quota for the
dimension). Returns a NOT_FOUND-coded error when the organization
has no quota row for the combination.
The organization's limits: per-sandbox resource maxima, secret quota, and the create/lifecycle/API rate limits, normalized to snake_case.
The organization's quota/usage overview, normalized: snapshot and
volume gauges plus one region_class/0 entry per region ×
sandbox-class.
Types
A used-vs-total pair for one resource dimension.
@type region_class() :: %{ region_id: String.t() | nil, sandbox_class: String.t() | nil, cpu: gauge(), memory: gauge(), disk: gauge(), gpu: gauge(), max_per_sandbox: %{ cpu: number() | nil, memory: number() | nil, disk: number() | nil }, allowed_gpu_types: [String.t()] }
Quota and usage for one region × sandbox-class combination.
Functions
@spec headroom(ExDaytona.Client.t(), String.t(), keyword()) :: {:ok, %{ cpu: number() | nil, memory: number() | nil, disk: number() | nil, gpu: number() | nil }} | {:error, ExDaytona.Error.t()}
Remaining capacity for one region × sandbox-class:
{:ok, %{cpu: n, memory: n, disk: n, gpu: n}} (each
total - used, nil when the provider reports no quota for the
dimension). Returns a NOT_FOUND-coded error when the organization
has no quota row for the combination.
@spec limits(ExDaytona.Client.t(), String.t()) :: {:ok, %{ max_cpu_per_sandbox: number() | nil, max_memory_per_sandbox: number() | nil, max_disk_per_sandbox: number() | nil, max_secrets_per_sandbox: number() | nil, secret_quota: number() | nil, sandbox_create_rate_limit: %{ limit: number() | nil, ttl_seconds: number() | nil }, sandbox_lifecycle_rate_limit: %{ limit: number() | nil, ttl_seconds: number() | nil }, authenticated_rate_limit: %{ limit: number() | nil, ttl_seconds: number() | nil } }} | {:error, ExDaytona.Error.t()}
The organization's limits: per-sandbox resource maxima, secret quota, and the create/lifecycle/API rate limits, normalized to snake_case.
JWT only
The provider rejects API-key authentication on the underlying
organization endpoint (401 "Invalid credentials", verified live) —
this works with JWT sessions. With an API key, the per-sandbox
maxima are still available per region/class from overview/2
(max_per_sandbox).
@spec overview(ExDaytona.Client.t(), String.t()) :: {:ok, %{snapshots: gauge(), volumes: gauge(), regions: [region_class()]}} | {:error, ExDaytona.Error.t()}
The organization's quota/usage overview, normalized: snapshot and
volume gauges plus one region_class/0 entry per region ×
sandbox-class.