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

# Rate limits

> Hourly per-key quotas, the headers on every response, and how to back off.

Limits are **per key, per hour**, counted separately for reads and writes.

| Limit                                | Value                  |
| ------------------------------------ | ---------------------- |
| Read requests (GET)                  | 1000 per hour, per key |
| Write requests (POST, PATCH, DELETE) | 200 per hour, per key  |
| Request body                         | 512 KB                 |
| Page size                            | 100                    |
| Page size, event log                 | 500                    |
| Active API keys per organization     | 20                     |

Reads are `GET`. Writes are `POST`, `PATCH` and `DELETE`. The two quotas are independent —
exhausting your writes leaves reads working.

## Headers

Every response carries your current standing, including errors:

```
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 999
X-RateLimit-Reset: 1768482000
X-Request-Id: req_4f2a9c1e8b7d4a5f9e3c2b1a8d7f6e5c
```

`X-RateLimit-Reset` is Unix time in seconds. On a `429` you also get:

```
Retry-After: 120
```

<Tip>
  Honour `Retry-After` rather than writing your own backoff — it's the header every HTTP
  client library already knows how to respect, and it means you don't have to parse our
  error body to know when to try again.
</Tip>

## Staying inside the limits

<AccordionGroup>
  <Accordion title="Use a separate key per integration" icon="key">
    Quotas are per key, so an ATS sync and a nightly report export on separate keys can't
    starve each other. You can also revoke one without disturbing the other.
  </Accordion>

  <Accordion title="Poll on a schedule, not in a loop" icon="clock">
    Grading takes minutes, not seconds. Polling a submission every few seconds burns quota
    to no benefit — every 30–60 seconds is plenty, and only for submissions actually
    awaiting a report.
  </Accordion>

  <Accordion title="Filter server-side" icon="filter">
    `GET /v1/submissions?assessment_id=…&status=evaluated&since=…` costs one request.
    Fetching everything and filtering in your code costs many, and gets worse as you grow.
  </Accordion>

  <Accordion title="Be careful with the event log" icon="list">
    A busy attempt produces thousands of events, and a full walk at 500 per page can be
    several requests for a single submission. Pull events only when you need the detail,
    not as part of a routine sync.
  </Accordion>
</AccordionGroup>

## Other limits

Not rate limits, but the other ceilings worth knowing:

* **Request body**: 512 KB. Every documented body is well under a kilobyte; the headroom is
  for a large `selected_task_ids` array.
* **Page size**: 100 (500 for events). Over the max is a `400`, not a silent clamp.
* **Active API keys**: 20 per organization.

<Note>
  Limits are per key rather than per organization, so more keys means more total quota. That
  is not a workaround to lean on — if you need materially more throughput, talk to us rather
  than minting keys to farm quota.
</Note>
