# Errors와 Rate Limits

> 공통 오류 envelope, HTTP status, 서버 API의 요청 제한을 정리합니다.

## 오류 형식

모든 오류는 다음 JSON envelope을 사용합니다.

```json
{"error":{"code":"ERROR_CODE","message":"설명"}}
```

`message`는 사람이 읽을 수 있는 설명이고, 프로그램 분기는 안정적인 `code`를 사용하세요.

## 공통 status

| HTTP | Code | 의미 |
| ---: | --- | --- |
| 400 | `VALIDATION_ERROR` | body 또는 query 검증 실패 |
| 401 | `UNAUTHORIZED` | 인증 실패 |
| 403 | `FORBIDDEN_SCOPE` | scope 부족 |
| 403 | `project_forbidden` | 프로젝트 키 범위 밖 |
| 403 | `ip_not_allowed` | IP allowlist 밖 |
| 404 | 리소스별 code | 대상이 없음 |
| 409 | `SLUG_TAKEN` | slug 충돌 |
| 429 | `RATE_LIMITED` | 요청 한도 초과 |
| 429 | `BUDGET_EXCEEDED` | MCP 일일 호출 예산 초과 |

## 서버 API 오류

서버 endpoint는 `SERVER_NOT_FOUND`, `PROJECT_NOT_FOUND`, `reserved_project_forbidden`을 추가로 반환할 수 있습니다.
프로젝트 키로 다른 프로젝트의 서버를 조회하면 존재 여부를 숨기기 위해 `SERVER_NOT_FOUND`가 반환됩니다.

## Rate limit

`/api/v1/servers*`는 keyId별로 60초에 60회까지 허용합니다. 61번째 요청은 `429`입니다.
응답에는 `Retry-After`, `X-RateLimit-Limit`, `X-RateLimit-Remaining` 헤더가 포함됩니다.

```http
HTTP/1.1 429 Too Many Requests
Retry-After: 12
```

`Retry-After`를 기다린 후 재시도하고, 여러 키를 임의로 번갈아 사용해 제한을 우회하지 마세요.
