# API Rate Limits and Error Codes

## Rate limits

OmniSocials enforces a per-API-key rate limit of **100 requests per minute** on every endpoint. Limits reset on a rolling 60 second window.

Every response includes three headers describing your current budget:

| Header | Meaning |
|--------|---------|
| `X-RateLimit-Limit` | The ceiling for this key. Currently `100`. |
| `X-RateLimit-Remaining` | Requests left in the current window. |
| `X-RateLimit-Reset` | Unix timestamp when the window resets. |

When the limit is exceeded, the API returns `429 Too Many Requests` with a `Retry-After` header (seconds) and a `retry_after` value in the body. Back off for that long, then retry.

```json
{
  "error": {
    "code": "rate_limit_exceeded",
    "message": "Rate limit exceeded. Try again in 42 seconds."
  },
  "retry_after": 42
}
```

Rate limits are enforced per key, not per workspace. Splitting heavy workloads across multiple keys is a valid scaling strategy.

## HTTP status codes

| Status | Meaning |
|--------|---------|
| `200` | Success |
| `201` | Resource created |
| `204` | Success with no body (typically deletions) |
| `400` | Validation error. See `error.details` for the field(s) that failed. |
| `401` | Missing or invalid API key |
| `403` | Valid key, but missing the scope required for this endpoint |
| `404` | Resource not found |
| `409` | Conflict (e.g. trying to publish a post that is already published) |
| `429` | Rate limit exceeded |
| `500` | Internal error. Retry with exponential backoff. |
| `503` | Temporary outage or scheduled maintenance |

## Error body shape

Every 4xx and 5xx response follows the same shape:

```json
{
  "error": {
    "code": "validation_error",
    "message": "content.default is required when no per-platform content is set",
    "details": [
      { "field": "content.default", "message": "content.default is required" }
    ]
  }
}
```

- `code` is a stable machine-readable identifier. Use this for branching logic. Field-level validation uses `validation_error`.
- `message` is a human-readable explanation and may change between versions.
- `details` is an optional array of `{ field, message }` objects, present on field-level validation errors. There is no top-level `error.field`.

## Retry strategy

Retry `429`, `500`, `502`, `503`, and `504` with exponential backoff. Do not retry `400`, `401`, `403`, `404`, or `409` without first fixing the underlying request.

A reasonable default is three attempts with delays of 1s, 2s, and 4s. Cap total wait time at 30 seconds.
