Skip to content

Limits, quotas & concurrency

Authoritative platform values — plan around them rather than discovering them at runtime. All per-tenant unless noted.

All are per-tenant unless noted. Numbers are the platform's authoritative values.

Rate limits (token buckets; 429 + Retry-After, and the SDK auto-retries): - reads ~500/s sustained (burst 1000) · writes ~100/s (burst 200) · upload-init ~100/s (burst 200) - shared-GPU inference (semantic search, foundation/ZERO): 30 burst, ~0.5/s sustained — deliberately tight; batch work belongs in jobs, not loops.

Concurrency caps (409, typed codes): - 3 active jobs (JOB_PER_TENANT_CAP) — embed/train/export/bulk compete tenant-wide; serialize. - 3 live model deployments · auto-label run scope ≤ 20,000 explicit asset ids · 1 active API key per user (revoke before re-creating; multi-actor work needs one key per seeded user).

Resource quotas (RESOURCE_LIMIT_EXCEEDED, 409, checked at create): - assets: 1M per tenant AND per dataset · datasets 100 · projects 200 - per project: versions 100, classes 1,000, members 50 - per asset: annotations 5,000, comments 200 - models 100 · users 50 per workspace — pending invitations count toward it

Size caps (413/422): - image ≤ 50 MB and ≤ 80 megapixels; > 100 MB objects need multipart (the SDK's upload_paths handles this) - sync batch endpoints: ≤ 1000 items AND ≤ 5 MB body · annotation batch-delete ≤ 500 ids · staged NDJSON import: ≤ 500k rows / 256 MB - guidelines: 1 MB markdown / 10 MB per image · page limit ≤ 100 (a few wide list endpoints allow 200)

Gotchas (cost hours if unknown)

  • Every dict-shaped parameter has a TypedDict in superb_ai/types/requests.py (AssetFilterParam, MappedAnnotationCreateParam, ProjectScopeParam, …) — read that file for the exact keys instead of guessing (e.g. the filename filter key is q, not filename). Plain dict literals still work; the TypedDicts are the reference (and a type-checker will validate your keys against them).
  • Debugging: export SUPERB_AI_LOG=debug — every request logs as METHOD path [request-id] -> status in Nms plus retry warnings, and the bracketed id equals the request_id in error envelopes AND in the platform's server logs (the SDK sends it as x-request-id). Quote it when reporting.
  • GPU endpoints are SCHEDULED: GPU-backed endpoints (semantic-search embedder, SAM) run on a schedule and are torn down outside their window — a 503 EMBEDDER_UNAVAILABLE / SAM-unavailable may mean scheduled-off, not cold-start. Check the current window with your operator; don't retry-loop. Ask the operator to bring them up; don't retry-loop.
  • Job cap 3 is per-tenant — your parallel jobs compete with everyone on the tenant.
  • Sync caps: batch endpoints cap at 1000 items (413 → use the bulk twin); annotation batch-delete caps at 500 ids.
  • Uploads confirm asynchronously — S3 PUT success ≠ asset row exists; always poll.
  • Presigned URLs: never send your Authorization header to them (different host — the SDK's download_to/upload_paths handle this; don't fetch them with the client).
  • /jobs hides cleanup_s3_objects unless you filter for it explicitly.
  • Idempotency: job kickoffs auto-generate an Idempotency-Key; pass your own to make retries replay (same body) — a REUSED key with a DIFFERENT body is a 409.
  • Deletes are hard — no undo. Version/asset/annotation deletes are permanent (annotation revision snapshots from past submits remain readable).