For AI agents: this page is also available as Markdown at https://docs.refabric.com/task-apis/limits.md, and the index of every page is https://docs.refabric.com/llms.txt.

Task APIs

Limits

Where to read every limit the API enforces — request and upload sizes, a task's field limits and your key's request rate — live from the API.

Every limit below is read live from the API, so this page always shows the values in force.

  • Transport limits (request body size, Prefer: wait cap, X-Refabric-Start-Timeout range, upload size and file types, idempotency-key retention, url fetch timeouts / size / redirects): GET /v1/meta → limits.
  • A task's own field limits (maxItems, maxLength, minimum / maximum, defaults): its inputSchema at GET /v1/tasks/{name}.
  • Page sizes (limit default and maximum of every list): the OpenAPI spec (openapi.json).
  • The request rate of your key: the X-RateLimit-* headers (Rate limits).
  • Prices: GET /v1/pricing (Pricing).

Transport

GET /v1/meta answers, among the receiver facts, a limits object:

{
  "limits": {
    "request": { "max_body_bytes": <n> },
    "prefer_wait": { "max_seconds": <n> },
    "start_timeout": { "min_seconds": <n>, "max_seconds": <n> },
    "upload": { "max_bytes": <n>, "media_types": ["<media type>", "…"] },
    "idempotency_key": { "ttl_seconds": <n or null> },
    "url_fetch": { "connect_timeout_seconds": <n>, "read_timeout_seconds": <n>, "max_bytes": <n>, "max_redirects": <n> }
  }
}
  • A body over request.max_body_bytes answers 413 body_too_large before the request is read.
  • Prefer: wait=N is clipped to prefer_wait.max_seconds.
  • X-Refabric-Start-Timeout outside start_timeout answers 422.
  • idempotency_key.ttl_seconds: null means a key is kept as long as the job it started.

A submit answers at once and the work runs as a job.

Uploads (POST /v1/files)

  • A file over upload.max_bytes answers 413 file_too_large; a file whose bytes are not one of upload.media_types answers 415 unsupported_media_type. The type is read from the bytes.
  • URLs are https only (http:// answers 422 invalid_request naming the field); an address that resolves to a private, loopback, link-local or metadata range is refused (image_not_public).

Images given by URL

An image url you send to a task is fetched from our servers when the job runs (a url of another site with its own query string is fetched when you submit, and kept as your file: — Files). Media urls are https only. The fetch's timeouts, size cap and redirect count are limits.url_fetch; each redirect hop is checked to be a public address.

Files

  • Retention: files are kept until you delete them (Files).
  • Download URL lifetime: none — urls are not signed and do not expire (Files).

Per task

Every count, length and range a task accepts is in its inputSchema (GET /v1/tasks/{name}). For example, to read how many references image.generate takes:

GET /v1/tasks/image.generate
{ "inputSchema": { "properties": { "references": { "type": "array", "maxItems": <n> } } } }

A request over a bound answers 422 naming the field; a shoot that would make more images than its task allows answers 422 too_many_outputs.

Account

The per-key request rate: Rate limits. There is no per-account concurrency limit.

Rate limits

One limit applies: a request rate per API key.

LimitCountsScope
request raterequests to the API in a fixed window — the allowance is X-RateLimit-Limitper API key

Every request made with the key counts, reads and submits alike. The window is fixed: it starts again when X-RateLimit-Reset says. Read the allowance from the headers, never from this page.

There is no per-account concurrency limit: no request is refused for how many of your jobs are running; jobs wait in the queue. What bounds a key besides its rate is its credit balance — every submit holds its credits before the job starts (Pricing), and a submit the balance does not cover answers 402 insufficient_credits. A moodboard.create job that cannot get capacity in time fails with capacity_busy (retryable).

If you set X-Refabric-Start-Timeout, a job that cannot start in time fails with start_timeout instead of starting late — see Jobs.

Headers

Every response to a request made with an API key carries the state of the key's window:

X-RateLimit-Limit: <limit>
X-RateLimit-Remaining: <remaining>
X-RateLimit-Reset: <seconds>
HeaderMeaning
X-RateLimit-Limitrequests allowed in the current window
X-RateLimit-Remainingrequests left in the window
X-RateLimit-Resetseconds until the window resets
Retry-Afteron 429: seconds until the window resets

When you hit a limit

HTTP 429
Retry-After: <seconds>

{ "error": { "code": "rate_limited", "type": "rate_limited",
             "message": "rate limit exceeded for this API key",
             "field": null, "retryable": true, "request_id": "req_…" } }
  • Wait Retry-After seconds, then retry with the same Idempotency-Key.
  • Prefer webhooks or Prefer: wait=N to tight polling loops. Poll a job no more than once every few seconds.
  • Spread batch submissions instead of sending them in one burst.
import time
def submit(session, url, body, key):
    while True:
        r = session.post(url, json=body, headers={"Idempotency-Key": key})
        if r.status_code != 429:
            return r
        time.sleep(float(r.headers.get("Retry-After", "5")))