Retry-After 헤더: 429·503 후 얼마나 기다려야 하나요?
Retry-After는 재시도 전에 얼마나 기다릴지 클라이언트에 알려 주는 헤더로, 초 단위 숫자나 HTTP 날짜이며 429나 503과 함께 옵니다. 읽는 법과 대응 방법을 정리했습니다.

Retry-After는 클라이언트가 다음 요청 전에 얼마나 기다려야 하는지 알려 주는 HTTP 응답 헤더입니다. 값은 Retry-After: 120처럼 초 단위 숫자이거나 HTTP 날짜입니다. 서버는 429 Too Many Requests와 503 Service Unavailable에 이 헤더를 함께 보낼 수 있습니다. 재시도 전에 최소한 그만큼 기다리고, 재시도마저 실패하면 더 길게 백오프하세요.
정의는 RFC 9110과 RFC 6585에서 인용했고, 2026-09-28에 확인했습니다. Sume 예시는 인증과 Run 웹훅 (영문) 문서, 그리고 따로 표시한 곳은 현재 API 코드에서 가져왔습니다.
Retry-After는 초 단위인가요, 밀리초 단위인가요?
초 단위입니다. RFC 9110은 이 값을 HTTP-date 또는 delay-seconds로 정의하며, delay-seconds는 시간을 초로 나타내는 음이 아닌 십진 정수입니다. 밀리초 형식은 없습니다. RFC가 드는 예시 두 개는 Retry-After: Fri, 31 Dec 1999 23:59:59 GMT와 Retry-After: 120이며, 뒤의 것은 이 분 동안 기다리라는 뜻입니다.
두 형식을 모두 처리하세요. 아래 파서는 현재 코드에 있는 Sume 웹훅 전송기의 파서와 같은 규칙을 따르며, 과거 날짜는 기다리지 않는다는 뜻으로 처리합니다.
// Wait in ms from a Retry-After value; null if absent or invalid.
function retryAfterMs(header: string | null, now = Date.now()): number | null {
const value = header?.trim();
if (!value) return null;
if (/^-?\d+$/.test(value)) {
const seconds = Number(value); // delay-seconds
return Number.isSafeInteger(seconds) && seconds >= 0 ? seconds * 1000 : null;
}
const date = Date.parse(value); // HTTP-date
return Number.isNaN(date) ? null : Math.max(0, date - now);
}429 Too Many Requests는 얼마나 지속되나요?
서버의 요청 한도가 다시 요청을 통과시켜 줄 때까지이며, 그때가 언제인지 서버가 알려 주는 수단이 Retry-After입니다. RFC 6585는 429를 주어진 시간 동안 요청이 너무 많은 상태로 정의하고, 응답에 Retry-After를 넣을 수 있다고 말합니다. 요청을 어떻게 셀지는 서버가 정합니다.
Sume API에서 429 rate_limited는 키의 분당 읽기 또는 쓰기 예산을 다 썼다는 뜻이며, retry-after가 기다릴 초를 알려 줍니다. 현재 코드에서 창은 키와 예산별로 고정되어 있고, retry-after는 창이 초기화될 때까지 남은 초(최소 1)이며, 기본 창은 60초이므로 429 rate_limited는 일 분 안에 풀립니다. 요금제별 예산과 요청 속도를 맞추는 데 쓸 수 있는 ratelimit-* 헤더는 Sume API 오류와 요청 한도에서 다룹니다.
모든 429와 503에 Retry-After가 있나요?
아니요, 그렇지 않습니다. RFC 9110은 서버가 503에 이 헤더를 보낼 수 있다고 하고 RFC 6585는 429에 이 헤더를 넣을 수 있다고 하므로, 헤더가 없는 경우도 코드에서 처리하세요. Sume 영상 Job 동시성과 큐에서 설명하듯 Sume의 429 queue_full은 요청 한도가 아니라 용량 한도이므로, 시간이 지나서가 아니라 Job이 끝나야 풀립니다. 현재 코드 기준으로 정리하면 다음과 같습니다.
| 응답 | 대기 힌트 | 할 일 |
|---|---|---|
429 rate_limited | retry-after 헤더: 키의 창이 초기화될 때까지 남은 초 | 창이 초기화될 때까지 기다린 뒤 다시 보내세요. |
429 queue_full | 본문의 retry_after_seconds: 30, 헤더 없음 | 실행 중인 Job이 끝나기를 기다리거나, 큐에 있는 Job 중 필요 없는 것을 취소하세요. 같은 Idempotency-Key로 다시 보내면 같은 queue_full이 돌아옵니다. |
503 deploy_draining | retry-after: 5 | 기다린 뒤 같은 요청을 다시 보내세요. |
503 database_busy | retry-after: 1 | 기다린 뒤 다시 보내세요. |
Sume는 내 웹훅 엔드포인트가 보낸 Retry-After를 따르나요?
Run 웹훅이라면 따릅니다. 엔드포인트가 전달에 429나 503과 Retry-After로 응답하면, Sume는 자체 백오프와 여러분이 보낸 값 중 더 긴 쪽만큼 기다리며, 최대 한 시간으로 제한합니다. 문서는 이 일정을 최대 10회 시도에 걸친 min(max(30s × 2^(attempt−1) with jitter, Retry-After), 1h)로 제시하며, 현재 코드에서 전송기는 초 형식과 날짜 형식을 모두 읽습니다.
Job 웹훅은 다르게 동작합니다. 시도 사이에 고정된 지연을 두고 재시도하며, 기본값은 30초입니다.
Retry-After가 없으면 어떻게 해야 하나요?
지터를 더한 지수 백오프를 쓰고 시도 횟수에 상한을 두세요. 그래야 함께 실패한 클라이언트들이 한꺼번에 재시도하지 않습니다. Sume의 TypeScript SDK는 기본적으로 이렇게 하며, 재시도는 두 번이고, 응답에 retry-after가 있으면 그 값을 따릅니다. 애초에 어떤 오류를 재시도할 만한지, POST는 언제 안전하게 다시 보낼 수 있는지는 재시도 가능한 HTTP 상태 코드에서 다룹니다.
출처
관련 글
개발자 카테고리의 다른 글
- Java 음성 인식 API: HttpClient로 오디오를 텍스트로
JDK HttpClient와 Jackson으로 Java에서 음성 인식 API를 호출하세요. 오디오 URL을 POST하고 Job을 폴링한 뒤, 전사문과 단어별 시간을 읽습니다.
- Stateless MCP 서버: 세션·Mcp-Session-Id·핸들
Stateless MCP 서버는 요청 사이에 세션을 유지하지 않습니다. Mcp-Session-Id의 역할, 2026-07-28 개정판이 없앤 것, 상태를 대신 어디에 두는지 정리합니다.
- Text to Image API: 프롬프트 보내고 이미지 URL 받기
Text-to-image API는 HTTPS로 보낸 프롬프트를 생성된 이미지로 바꿉니다. 요청과 응답, 오래 걸리는 Job, 비용까지 호출 방법을 정리했습니다.
- 웹훅 보안 모범 사례: 수신기 체크리스트
HTTPS로만 받고, 원본 본문의 HMAC을 상수 시간으로 검증하고, 오래된 타임스탬프는 거부하고, 이벤트 ID로 중복을 제거하고, 2xx로 빠르게 응답하세요.
작성자 Sume