본문으로 건너뛰기
문서 메뉴레이트리밋과 에러 코드

서버

레이트리밋과 에러 코드

오류는 모두 같은 형식으로 돌려줘요. 코드로 분기하고, 메시지는 사람이 읽을 수 있게 보여 주세요.

오류 형식

429 응답 예
{
  "error": {
    "code": "rate_limited",
    "message": "요청이 너무 많습니다. 잠시 후 다시 시도하세요",
    "details": {
      "retryAfterSeconds": 120
    }
  }
}

code는 아래 표의 값 중 하나예요. message는 한국어 안내 문구이고 바뀔 수 있으니 분기에 쓰지 마세요. details는 선택이며, validation_failed일 때는 어떤 필드가 잘못됐는지 담겨요.

에러 코드

에러 코드와 HTTP 상태
codeHTTP의미
bad_request400요청 형식이 잘못됐어요.
validation_failed422필드 값이 올바르지 않아요. details를 확인하세요.
unauthenticated401키나 토큰이 없거나 맞지 않아요.
token_expired401세션 토큰이 만료됐어요. 다시 부팅하세요.
forbidden403이 작업을 할 권한이 없어요.
not_found404대상을 찾을 수 없어요.
conflict409대상의 현재 상태와 충돌해요. 최신 상태를 다시 읽고 시도하세요.
rate_limited429요청이 너무 많아요. Retry-After 뒤에 다시 시도하세요.
otp_invalid400인증 코드가 틀렸어요.
otp_expired400인증 코드가 만료됐어요. 새 코드를 받으세요.
otp_used400이미 사용한 인증 코드예요.
internal500엔트로피 쪽 오류예요. 잠시 후 다시 시도하세요.

레이트리밋

요청 한도
대상한도
인증 코드 보내기 — 목적지별15분에 5번
인증 코드 보내기 — IP별1시간에 20번
인증 코드 확인코드 하나당 5번

한도를 넘으면 429와 함께 Retry-After 헤더(초)를 보내요. 가능한 경우 details.retryAfterSeconds에도 같은 값을 담아요. 그 시간이 지나기 전에는 다시 보내지 마세요.

재시도

  • 429와 5xx, 네트워크 오류만 재시도하세요. 4xx는 요청을 고쳐야 해요.
  • 간격을 늘려 가며(예: 1초, 2초, 4초…) 무작위 지연을 더해 재시도하세요.
  • 메시지를 보낼 때는 clientMsgId를 넣으세요. 같은 값으로 다시 보내도 메시지는 한 번만 저장돼요.
안전하게 다시 보내기
const clientMsgId = crypto.randomUUID(); // 재시도해도 같은 값을 써요

for (let attempt = 0; attempt < 5; attempt++) {
  const res = await fetch(`${base}/v1/messenger/conversations/${id}/messages`, {
    method: 'POST',
    headers: { Authorization: `Bearer ${sessionToken}`, 'Content-Type': 'application/json' },
    body: JSON.stringify({ body: text, clientMsgId }),
  });
  if (res.ok) break;
  if (res.status !== 429 && res.status < 500) throw new Error(await res.text());
  const wait = Number(res.headers.get('retry-after')) * 1000 || 2 ** attempt * 1000;
  await new Promise((r) => setTimeout(r, wait + Math.random() * 250));
}