# Rate limits

> API version: v1

## Limits

| Limit | Value |
|---|---|
| Requests per minute, per key | 60 |
| Requests per day, per key | 10,000 |
| `GET /status` and `GET /openapi.json` (no key), per IP address | 60 per minute |

The minute window starts at your first request in it. The day is the UTC calendar day and resets at 00:00 UTC.

**Need more?** [Contact us for higher limits](mailto:contact@studyingstocks.com).

## Headers

Every response from an endpoint that needs a key carries:

| Header | Meaning |
|---|---|
| `X-RateLimit-Limit` | Requests allowed in the current window |
| `X-RateLimit-Remaining` | Requests left in the current window |
| `X-RateLimit-Reset` | Unix time (seconds) when the window resets |
| `X-RateLimit-Day-Limit` | Requests allowed per day |
| `X-RateLimit-Day-Remaining` | Requests left today |

The first three describe the minute window, or the day when the day is the one that is used up.

A request with a wrong key is answered `401` with `X-RateLimit-Auth-Limit`, `X-RateLimit-Auth-Remaining` and `X-RateLimit-Auth-Reset`: that is a separate limit on failed attempts per IP address, not your key's limit. A `403` carries the headers of your key, without counting the request.

## When you hit the limit

The response is `429` with the code `rate_limited` and a `Retry-After` header: the number of seconds to wait.

```json
{ "error": { "code": "rate_limited", "message": "Rate limit reached. Retry after the number of seconds in the Retry-After header.", "docs": "https://studyingstocks.com/developers/errors#rate_limited" } }
```

Wait for `Retry-After` seconds, then retry. Do not retry in a tight loop.

## Caching

Responses are cached for up to 1 hour and refreshed whenever a company's data is updated, so the same request is cheap. Company data changes at most a few times a day, so there is little to gain from polling more often than that. Requests served from the cache still count against your limits.
