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

# Rate limits

> How many requests a key can make, the headers that tell you where you stand, and how to back off.

Each API key can make **120 requests a minute**. Up to **30 of those can be writes**: the calls that need a `full` key, such as creating an automation or setting one live. The minute is a sliding window, so there's no reset on the hour to wait for.

Limits are per key, so two integrations with their own keys never slow each other down.

## Headers

Once a key is accepted, every response tells you where it stands:

| Header | Meaning |
| - | - |
| `RateLimit-Limit` | Requests this key may make per minute, across every route. |
| `RateLimit-Remaining` | Requests left in the current window. `0` when a write is refused for the write limit. |
| `RateLimit-Reset` | Seconds until room opens up again. |

The headers count every request together. Writes have their own, smaller allowance inside that, so keep your own count of writes if you make many in a burst.

## When you go past the limit

You get `429` with the code [`rate_limited`](https://docs.indm.social/errors#rate_limited) and a `Retry-After` header: the number of seconds to wait. The `message` says which limit you reached, all requests or writes. Wait that long, then send the request again. A refused write doesn't count against the overall allowance.

```javascript Node theme={null}
async function indm(path, init = {}) {
  for (let attempt = 0; attempt < 5; attempt++) {
    const res = await fetch(`https://api.indm.social/v1${path}`, {
      ...init,
      headers: {
        Authorization: `Bearer ${process.env.INDM_API_KEY}`,
        "Content-Type": "application/json",
        ...init.headers,
      },
    });
    if (res.status !== 429) return res;
    const wait = Number(res.headers.get("Retry-After") ?? 1);
    await new Promise((resolve) => setTimeout(resolve, wait * 1000));
  }
  throw new Error("Still rate limited after 5 attempts");
}
```

```python Python theme={null}
import os
import time
import requests

def indm(method, path, **kwargs):
    headers = {"Authorization": f"Bearer {os.environ['INDM_API_KEY']}"}
    for _ in range(5):
        res = requests.request(
            method, f"https://api.indm.social/v1{path}", headers=headers, **kwargs
        )
        if res.status_code != 429:
            return res
        time.sleep(int(res.headers.get("Retry-After", "1")))
    raise RuntimeError("Still rate limited after 5 attempts")
```

## Staying well inside it

* **Read once, use many times.** A post's id doesn't change. Keep the ids you need instead of listing media before every call.
* **Poll a refresh gently.** Every few seconds is plenty for [Get a refresh](https://docs.indm.social/api-reference/accounts/get-refresh.md).
* **Spread scheduled reports.** A nightly job across many automations can pace itself by `RateLimit-Remaining` rather than firing everything at once.

## Your API limit and Instagram's pace are different things

The limit on this page is about calls to inDM. How fast DMs reach people is separate: each Instagram account sends at a steady pace rather than all at once, because Instagram restricts an account that sends in bursts, and that costs far more than a short wait. When a post takes off, messages queue and drain, and none are dropped.

[Get account health](https://docs.indm.social/api-reference/accounts/get-account-health.md) reports an account's current speed, what's waiting, and about how long it takes to clear.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.