Limits
This page is the single owner of the API's numeric caps and rate quotas. For per-endpoint request/response schemas and the error.code list, see ReDoc; for how to handle each error, see Errors.
Input length per task
All six per-task POST /v1/tasks/{task}/predict endpoints accept 1–500,000 bp. Sequences must contain only A/C/G/T/N (case-insensitive); oversized, undersized, or out-of-alphabet inputs are rejected with 422 validation_failed before any GPU work. Request body cap: 16 MiB.
The expression model uses a fixed 9,198 bp TSS-centered window (±4,599 bp). Off-length sequences are accepted (the tokenizer truncates or pads to this window), but predictions on non-TSS-centered or off-length input are not guaranteed to be biologically meaningful. If you have raw genomic input where the TSS isn't pre-known, server-side annotation→expression chaining can be enabled per account.
Latency
Rough sync latency at the recommended input size, on a warm model.
| Task | Sync latency at recommended size | When to go async |
|---|---|---|
| Promoter / enhancer / chromatin | 0.3–10 s | inputs above 100,000 bp |
| Splice | 0.3–10 s | inputs above 250,000 bp |
| Annotation | 1–60 s | inputs above 30,000 bp |
| Expression (fixed 9,198 bp window) | 0.5–3 s | n/a — sync is always safe |
Cold start
If a task's model isn't already loaded into GPU memory, the first request
pays a model-load cost. The response carries meta.cold_start: true and
meta.model_load_time_ms. Cold start adds 5–15 s for the smaller models
and 30–90 s for expression and annotation. Subsequent calls are warm.
Sync delivery: timeout and guidance
Sync delivery (the default, no Prefer header) is best-effort within the
upstream HTTP read timeout of 300 seconds. A request that takes longer than
that is terminated by the edge proxy and surfaces to your client as a
connection reset or 504 gateway_timeout. The body in this case is the
proxy's, not the unified {error: {...}} envelope; this is the only place
where that happens. Pick async whenever you expect a request to push past
~60 s.
Hard sync cap
The six per-task POST /v1/tasks/{task}/predict endpoints enforce no hard sync
cap: each accepts sync up to its per-task max (see "Input length per task"
above). The one exception is the composite find-genes-and-predict-expression
workflow (POST /v1/workflows/find-genes-and-predict-expression): submitted
synchronously above 50,000 bp it returns 413 sync_too_large.
Submit it with Prefer: respond-async (as the MCP server's
find_genes_and_predict_expression tool always does) to lift that cap. No
other endpoint emits sync_too_large.
Recommended async opt-in (client-side guidance, not enforced)
Use Prefer: respond-async when your input exceeds the threshold below.
These are calibrated against typical inference times. Sync still works
under them, but bursty traffic plus GPU contention can push individual
requests past the 300 s proxy window without warning.
| Task | Recommended async above |
|---|---|
| Promoter | 100,000 bp |
| Splice | 250,000 bp |
| Enhancer | 100,000 bp |
| Chromatin | 100,000 bp |
| Annotation | 30,000 bp |
| Expression (TSS-centered) | n/a — input is the fixed 9,198 bp window, so sync is always safe |
If sync is critical for your workload and these guidelines push more traffic to async than you'd like, contact us; we can profile your distribution and tune the proxy timeout for your account.
Per-key quotas
Three limiters run side by side. Each is configured per partner; your account owner tells you the values issued for your key. Defaults for the partner tier:
| Setting | Default for partner tier | Enforced? |
|---|---|---|
| Concurrent in-flight requests | 2 | Yes. Exceeding the cap returns 429 too_many_requests with Retry-After: 1. |
| Per-minute request rate | 60 | Yes. Token bucket; capacity = rate / 6 (10-second burst, default 10). An empty bucket returns 429 too_many_requests with Retry-After set to approximately the seconds until the next token. |
| Edge per-IP cap | 10 r/s burst 20 on api.* | Yes. At the edge, returns 429 too_many_requests with the unified {error: {...}} envelope (see errors). |
RateLimit-* headers (every authenticated response)
The application emits the IETF
httpapi-ratelimit-headers
draft set on every authenticated 2xx and on every 429 from this
service, so you can pace from header state without inferring rate from
429s:
| Header | Meaning |
|---|---|
RateLimit-Limit | Token-bucket capacity (= rate_per_minute / 6). |
RateLimit-Remaining | Tokens left after this request, integer. |
RateLimit-Reset | Seconds until the bucket refills to full. |
RateLimit-Policy | <capacity>;w=60, capacity per 60-second window. |
Retry-After | On 429 only, seconds until at least one token is available. |
Pacing guidance
- Pace at ~80% of your issued
RateLimit-Limitto leave headroom for bursts; serialize a small worker pool against your concurrency cap rather than firing N parallel requests at it. - The token bucket and concurrency semaphore are independent: a 429 can
come from either. Both carry the same
RateLimit-*spine, but only the rate-bucket 429 has aRetry-Aftercalibrated against refill time; the concurrency 429 usesRetry-After: 1.
Async result store
Async job results are retained for 24 h from last activity, then expire.
A fetch after the TTL (or after a service restart) returns 410 job_expired,
so retrieve results promptly and re-submit if a job has expired.
Back to the workflow: REST API guide · Tasks.