> ## Documentation Index
> Fetch the complete documentation index at: https://sudomock.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> The base URL is https://api.sudomock.com.
> Authenticate every request with the x-api-key header. Keys begin with sm_.
> A render returns the finished image at data.print_files[0].export_path. A request sent with is_async true returns a job_id to poll at GET /api/v1/jobs/{job_id}.
> Prefer the official SDKs over hand-written HTTP calls: npm install sudomock for Node, pip install sudomock for Python.

# Rate limits and concurrency limits

> The request rate, the parallel ceiling, the headers.

Two separate ceilings guard the API, and both answer with a `429`. A rate limit
counts how many requests you send per minute. A concurrency limit counts how
many long operations you keep running at the same time.

Read `error.type` to tell them apart. A rate limit means slow down. A
concurrency limit means wait for work already in flight to finish.

## Rate limits

The sustained rate is 1,000 requests per minute, and the window holds a little
headroom above that so a short burst is not punished. Every response carries
the current state of the window.

| Header                | Meaning                              |
| --------------------- | ------------------------------------ |
| `RateLimit-Limit`     | Requests allowed in the window.      |
| `RateLimit-Remaining` | Requests left in the window.         |
| `RateLimit-Reset`     | Seconds until the window resets.     |
| `RateLimit-Policy`    | The limit and the window length.     |
| `Retry-After`         | Seconds to wait, present on a `429`. |

```json 429, rate limit exceeded theme={"theme":{"light":"github-light","dark":"vesper"}}
{
  "detail": "Rate limit exceeded. Please slow down.",
  "error": {
    "type": "rate_limit_exceeded",
    "code": "RATE_LIMIT_EXCEEDED",
    "limit": 1000,
    "remaining": 0,
    "reset_seconds": 42,
    "retry_after": 42,
    "resource": "api"
  }
}
```

## Concurrency limits

Renders and uploads hold separate concurrency budgets, and the upload budget is
never the larger of the two. Only the endpoints that hold a slot report these
headers: a PSD upload, a synchronous render, and a background removal.

| Header                   | Meaning                         |
| ------------------------ | ------------------------------- |
| `X-Concurrent-Limit`     | Operations allowed at once.     |
| `X-Concurrent-Used`      | Operations in flight now.       |
| `X-Concurrent-Remaining` | Operations you can still start. |

```json 429, concurrency limit exceeded theme={"theme":{"light":"github-light","dark":"vesper"}}
{
  "detail": "Max concurrent render requests exceeded.",
  "error": {
    "type": "concurrent_limit_exceeded",
    "code": "CONCURRENT_LIMIT_EXCEEDED",
    "limit": 10,
    "current": 11,
    "remaining": 0,
    "resource": "concurrent-render"
  }
}
```

A concurrency `429` carries `Retry-After` as well, and five seconds is usually
enough: the slot frees as soon as the request holding it returns.

## Queue instead of waiting

A queued render never holds a concurrency slot while it waits. Send `is_async`
as `true` on [`POST /api/v1/renders`](/docs/api-reference/renders/render-mockup) and
the call returns a `202` with a job you poll through
[`GET /api/v1/jobs/{job_id}`](/docs/api-reference/jobs/get-async-job-status), or
receive over a [webhook](/docs/webhooks/overview). Video mockups are always queued
and behave the same way.

That is the right shape for a large batch: submit the whole set, then follow
the jobs.

## Read your own ceilings

Do not hardcode the concurrency numbers. Both budgets move with the plan
catalogue, so read them at runtime from
[`GET /api/v1/packages/plans`](/docs/api-reference/packages/get-all-subscription-plans),
which returns `max_concurrent_requests` and `max_concurrent_uploads` for the
plan you are on. Size your worker pool from those two fields and a `429` stops
being something you have to handle.

<Card title="Errors" icon="triangle-alert" href="/docs/errors" horizontal>
  Every status code, every `error_code`, and the retry loop.
</Card>
