Troupeby AliveDocsSign in

Troupe Cloud

Usage and limits

Troupe Cloud meters the time your devices, builds and test shards run and the storage you hold, and caps what an organization and each of its projects may run at once. This page lists what is metered, the limits, what a call answers at a limit, and the request budgets.

What is metered

Usage is counted per calendar month in UTC, per organization and per project, in five dimensions.

DimensionUnitWhat it counts
device_secondssecondsA device session, from the moment a device host takes it until it ends. Time in the queue is not counted
build_secondssecondsA build, while it runs on a build machine; the inspection of its source is counted separately
test_run_secondssecondsA test shard, while it runs
placement_unit_secondssecondsA device host's slot held for the organization or the project, including the clean-up after its last device
storage_byte_hoursbyte hoursWhat is stored for you, such as sources, artifacts and caches, sampled every hour

The times come from Troupe's own record of when work started and ended, never from what a device or a build reports about itself.

Owners and admins read it on the organization's Usage page, for the organization or one project, for any of the last six months. Over the API it is usage.get, which needs usage:read:

Shell
curl "https://api.troupe.ali.ve/v1/usage/get?orgId=org_…&month=2026-11" \
  --header "Authorization: Bearer $TROUPE_API_KEY" \
  --header "Troupe-Contract: 1.0"
JSON
{
  "orgId": "org_…",
  "month": "2026-11",
  "periodStart": "2026-11-01T00:00:00.000Z",
  "periodEnd": "2026-12-01T00:00:00.000Z",
  "lines": [
    { "dimension": "device_seconds", "unit": "s", "quantity": "5412" },
    { "dimension": "build_seconds", "unit": "s", "quantity": "1893" },
    { "dimension": "test_run_seconds", "unit": "s", "quantity": "0" },
    { "dimension": "placement_unit_seconds", "unit": "s", "quantity": "7260" },
    { "dimension": "storage_byte_hours", "unit": "byte·h", "quantity": "1288490188800" }
  ]
}

A quantity is a decimal number written as a string. Leave month out for the current month, and add projectId for one project. A key for one project always gets its own project's usage.

Limits

A limit is one of two kinds.

KindCapsDimensions
concurrencyHow much exists at oncedevices, queued_sessions, builds, test_shards, projects, placement_units, storage_bytes
periodHow much is used in a calendar monthdevice_seconds, build_seconds, test_run_seconds
DimensionWhat it caps
devicesDevice sessions that exist at once, queued ones included
queued_sessionsDevice sessions waiting for a device
buildsBuilds in progress
test_shardsTest shards in progress
projectsProjects that are not archived
placement_unitsDevice host slots held at once. With project placement, every project with live devices holds its own
storage_bytesBytes stored

A limit also has a class: all, or one platform (ios-simulator), or one OS family for builds (ios, android), so a plan can cap one platform apart from the total.

An organization's limits come from its plan. An organization without a plan of its own gets the default plan, which sets these concurrency limits and no period limits:

DimensionDefault plan
devices4
builds2
test_shards4
projects100
placement_units2
queued_sessions100

A dimension with no limit is not capped. A limit of 0 suspends: nothing new of that kind starts.

The Usage page lists the limits that apply to you, with what is in use now. quotas.get returns the same. Each line has the dimension, the kind, the class, the limit, where it comes from (plan, org or project), and either inUse, for a concurrency limit, or used, this month's use, for a period limit.

Limits for one project

An owner or admin can give one project a smaller share with quotas.setProject, so one project cannot use up the organization's devices or build time.

Shell
curl https://api.troupe.ali.ve/v1/quotas/set-project \
  --header "Authorization: Bearer $TROUPE_API_KEY" \
  --header "Troupe-Contract: 1.0" \
  --header "Content-Type: application/json" \
  --data '{
    "orgId": "org_…", "projectId": "prj_…",
    "kind": "concurrency", "dimension": "devices", "class": "all", "limit": 2
  }'

A project's limit must stay within the organization's, or the call is refused with CONFLICT (quota-above-org-limit). null removes the project's own limit, and 0 suspends the project. The call needs org:manage, which a key may carry.

At a limit

A limit is checked when work starts. Work that is already running is not stopped by it.

SituationAnswer
A concurrency limit is reachedQUOTA_EXCEEDED, HTTP 429, with kind: "concurrency". Retry when something of yours ends
A month's budget is spentQUOTA_EXCEEDED, HTTP 429, with kind: "period" and retryAfterMs until the month ends, also sent as Retry-After
A limit is 0, or the organization or project is suspendedORG_SUSPENDED or PROJECT_SUSPENDED, HTTP 403
Within your limits, but every matching device is busyNot a refusal: the session is queued for up to 15 minutes
Within your limits, but nothing can serve the request nowCAPACITY_UNAVAILABLE, HTTP 503, with retryAfterMs

QUOTA_EXCEEDED carries only your own numbers:

JSON
{
  "code": "QUOTA_EXCEEDED",
  "message": "…",
  "data": { "scope": "org", "kind": "concurrency", "dimension": "devices", "limit": 4, "used": 4 },
  "retryable": false,
  "requestId": "req_…"
}

scope says whether the organization's or the project's limit refused. Releasing a session, canceling a build or a run and deleting are never refused by a limit or a suspension.

Request budgets

Calls are rate limited per caller, apart from the limits above.

CallerBudget
One person, one API key, or one approved app or exchanged identity600 calls a minute
All callers of one organization together3000 calls a minute
No credential, per address120 calls a minute
Credentials that fail to verify, per address20 a minute
The token endpoint, per address120 requests a minute

A call over budget is refused with RATE_LIMITED, HTTP 429. Its retryAfterMs, and the Retry-After header in whole seconds, say when to try again. The token endpoint answers the OAuth error slow_down instead. A call spends its caller's budget first and its organization's second, so one busy key cannot use up the budget of the others.

Next

esc

    Type to search every page, the protocol reference and the cloud API.