Billing
Prepaid credits, per-second metering, and spend limits for orgs and members.
Platinum billing is prepaid. You buy credits, and metered usage draws down the org balance. Manage billing in the dashboard or with the /v1/billing/* endpoints.
What is billed
Platinum samples usage every 60 seconds. It prices vCPU-seconds, GiB-seconds of RAM, and GiB-hours of disk. Volumes, snapshots, and GPU attachments are free.
| Sandbox state | CPU | RAM | Disk |
|---|---|---|---|
running | billed | billed | billed |
stopped | — | — | billed |
archived | — | — | — |
deleted | — | — | — |
Compute charges stop when a sandbox stops. A stopped sandbox costs at most its disk. Set auto_stop_minutes (Sandboxes) to control idle cost.
Zero balance
Usage debits the org balance. At balance ≤ 0:
| Surface | What happens |
|---|---|
| New compute — create, start, resume, restore, restore-from-backup, clone, fork | 402 insufficient_credits |
| Running sandboxes | 60-minute grace period; then auto-stopped (never deleted) |
| Stopped sandboxes | Platinum keeps the disk. After the balance is positive, call /start or /resume. Credits alone do not restart them. |
| Preview URLs | Traffic to an exposed port does not restart a stopped sandbox |
Add credits
New signups get a one-time $10 trial credit on the personal org. Later orgs start at $0.
Only owners and admins can buy credits. Any member can read the balance, ledger, limits, and usage.
To add credits:
- Open dashboard → Billing.
- Pick a credit pack, or enter a custom amount.
- Complete the Stripe-hosted checkout.
The balance updates when the payment clears, usually seconds after checkout. There is no API or CLI route to buy credits.
Set spend limits
Only owners and admins can set limits. Limits reset on the first day of each calendar month (UTC). A limit is independent of the balance: it blocks spend even when credits remain.
An alert only warns. A limit blocks. After you hit a limit, spend-increasing calls (POST/PUT/PATCH) return 402. Reads, deletes, and /v1/billing/* stay open, so you can watch usage, shed cost, or raise the limit.
| Setting | Applies to | Notifies at | Blocks at |
|---|---|---|---|
usage_alert_usd | whole org | the amount you set — email + org.usage_alert webhook | never |
usage_limit_usd | whole org | 50 / 80 / 90 / 100% — email + org.usage_limit_reached webhook | 100% → 402 usage_limit_exceeded |
limit_usd | one member | 50 / 80 / 90 / 100% — email to that member | 100% → 402 user_usage_limit_exceeded |
Webhook payloads: Webhooks.
The SDKs have no typed billing helpers. Use client.request().
Read current limits
Any member can read /v1/billing/limits. It returns the org caps plus the caller's own cap and spend. /v1/billing/user-limits is owner/admin only. It lists each member's cap and spend.
const limits = await client.request("GET", "/v1/billing/limits");
const userLimits = await client.request("GET", "/v1/billing/user-limits");limits = client.request("GET", "/v1/billing/limits")
user_limits = client.request("GET", "/v1/billing/user-limits")pt api /v1/billing/limits
pt api /v1/billing/user-limitscurl -H "Authorization: Bearer $PT_TOKEN" "$PT_API_URL/v1/billing/limits"
curl -H "Authorization: Bearer $PT_TOKEN" "$PT_API_URL/v1/billing/user-limits"Set the org cap
The org limit cannot drop below the total already allocated to members.
await client.request("PUT", "/v1/billing/limits", {
usage_limit_usd: 200,
usage_alert_usd: 150,
});client.request("PUT", "/v1/billing/limits", {"usage_limit_usd": 200, "usage_alert_usd": 150})pt api PUT /v1/billing/limits -d '{"usage_limit_usd":200,"usage_alert_usd":150}'curl -X PUT "$PT_API_URL/v1/billing/limits" -H "Authorization: Bearer $PT_TOKEN" \
-H "Content-Type: application/json" -d '{"usage_limit_usd":200,"usage_alert_usd":150}'Cap a member
You cannot cap an owner. Member caps cannot sum past the org limit; over-allocation returns 400. limit_usd: null clears a cap. The API rejects the value 0.
await client.request("PUT", `/v1/billing/user-limits/${userId}`, { limit_usd: 50 });client.request("PUT", f"/v1/billing/user-limits/{user_id}", {"limit_usd": 50})pt api PUT /v1/billing/user-limits/$USER_ID -d '{"limit_usd":50}'curl -X PUT "$PT_API_URL/v1/billing/user-limits/$USER_ID" -H "Authorization: Bearer $PT_TOKEN" \
-H "Content-Type: application/json" -d '{"limit_usd":50}'Query usage
GET /v1/billing/usage rolls up the org's metered usage. The default window is the last 30 days. For one sandbox, use GET /v1/sandboxes/:id/usage (SDK: sbx.usage()).
const usage = await client.request("GET", "/v1/billing/usage?group=sandbox");
const one = await sbx.usage(); // one sandboxusage = client.request("GET", "/v1/billing/usage?group=sandbox")
one = sbx.usage() # one sandboxpt api /v1/billing/usage --query "group=sandbox"
pt api /v1/sandboxes/$SBX/usagecurl -H "Authorization: Bearer $PT_TOKEN" "$PT_API_URL/v1/billing/usage?group=sandbox"
curl -H "Authorization: Bearer $PT_TOKEN" "$PT_API_URL/v1/sandboxes/$SBX/usage"| Param | Effect |
|---|---|
since_ms | window start (epoch ms) |
group=sandbox | per-sandbox breakdown, spend-ordered |
series | hour or day — time-bucketed points; hour defaults to the last 24 h |