Quotas
Effective limits and current usage, in one call.
View as Markdowncurl -fsS "https://api.saved.sh/v1/workspaces/$WID/quotas" \
-H "Authorization: Bearer $TOKEN"{
"billing_state": "trial",
"quotas": [
{ "resource": "workers_per_workspace", "limit": 1, "used": 1 },
{ "resource": "backups_per_workspace", "limit": 5, "used": 3 },
{ "resource": "api_keys_per_workspace", "limit": 2, "used": 0 },
{ "resource": "members_per_workspace", "limit": 1, "used": 1 },
{ "resource": "destinations_per_workspace", "limit": 1, "used": 0 },
{ "resource": "connections_per_workspace", "limit": 1, "used": 0 },
{ "resource": "max_stored_bytes", "limit": 21474836480, "used": 4192104448 },
{ "resource": "max_artifact_bytes", "limit": 5368709120, "used": null },
{ "resource": "max_retention_days", "limit": 7, "used": null }
]
}Permission: workspaces:read.
The response is the complete set with defaults filled in, so you never have to know which limits exist. Read it rather than hard-coding the tables below.
Reading it
| Field | Meaning |
|---|---|
billing_state | Which set of limits applies |
resource | The identifier that appears in a quota_exceeded message |
limit | The ceiling. 0 means unlimited |
used | Current usage, or null where usage is not a running count |
limit: 0 is unlimited, not zero. Treating it as a ceiling would make every paid
workspace look like it had exhausted a quota it does not have.
used is null for limits that constrain a single value rather than counting things:
max_artifact_bytes bounds one artifact, and max_retention_days bounds a policy. There is
nothing to count.
The limits
| Resource | Trial | Paid | Bounds |
|---|---|---|---|
workers_per_workspace | 1 | 10 | Provisioned workers |
backups_per_workspace | 5 | 100 | Backup definitions |
api_keys_per_workspace | 2 | 20 | Active API keys |
members_per_workspace | 1 | 25 | Members |
destinations_per_workspace | 1 | 25 | Linked buckets |
connections_per_workspace | 1 | 25 | OAuth connections |
max_stored_bytes | 20 GiB | unlimited | Total held in our storage |
max_artifact_bytes | 5 GiB | unlimited | A single artifact |
max_retention_days | 7 | unlimited | expire_after and lock_for |
Workspaces per user is 5 on both.
max_retention_days on trial is 7. A trial workspace cannot set expire_after: 90d, and
since retention is write-once, a backup created on trial keeps whatever policy it was
given. Upgrading does not retroactively loosen it. If you intend a long retention, set it on
a paid plan, or expect to create the backup again.
Checking before you create
curl -fsS "https://api.saved.sh/v1/workspaces/$WID/quotas" -H "Authorization: Bearer $TOKEN" \
| jq -r '.quotas[] | select(.limit != 0 and .used != null and .used >= .limit)
| "at limit: \(.resource) \(.used)/\(.limit)"'Worth running before a bulk apply, which otherwise fails partway through with the earlier
backups created and the later ones not.
When a limit is hit
{
"error": {
"code": "quota_exceeded",
"message": "this workspace already has 5 backups",
"request_id": "req_..."
}
}Returned as 409. The message names the resource and the count.
Creating endpoints check the quota first, so a refused call creates nothing. Retrying will fail identically until the underlying count changes: delete something, or upgrade.
Storage limits behave differently
max_stored_bytes and max_artifact_bytes are not enforced at request time, because nothing
about an API call knows how large a dump will turn out to be. They bind during a run, and
exceeding one fails the run rather than returning an HTTP error.
Watch used against limit rather than waiting for a failure at 02:00.
max_stored_bytes counts only artifacts held in our storage. Copies delivered to your own
buckets do not count toward it, which is one reason
skip_permanent is the cheapest way to run us.
Billing state
billing_state decides which set applies, and also gates writes independently of quotas.
| State | New runs | Config changes | Reads and downloads |
|---|---|---|---|
trial, active, warned | Yes | Yes | Yes |
stopped | No | Yes | Yes |
blocked | No | No | Yes |
Reads and downloads are never gated. See Workspaces.