🎥 AI 시대 옵저버빌리티 전략 웨비나 | 무료 다시보기 (~4/9)
Top
도입문의
2026-09-01

429 Too Many Requests 원인과 해결 방법

결제나 지도뿐 아니라 챗봇이나 문서 요약까지 외부 회사가 만든 API를 불러 쓰는 기능이 늘었습니다. 호출이 쌓이다 보면 어느 날 응답 대신 429 Too Many Requests가 돌아오기 시작합니다.

429는 어느 제한에 걸렸는지 특정한 다음 호출량을 줄이거나 제한 설정을 조정해야 해소되는 응답입니다. 503 Service Unavailable과 자주 함께 거론되는데, 응답 코드만 보고는 요청 제한에 걸린 것인지조차 분명하지 않을 때가 있습니다.

429 에러 뜻과 발생 상황

429 Too Many Requests는 정해진 시간 안에 클라이언트가 너무 많은 요청을 보냈다는 뜻으로 서버가 돌려주는 HTTP 상태 코드입니다. 429를 처음 규정한 RFC 6585 4장에 적힌 정의입니다. 서버가 요청을 처리하지 못하는 상태에 빠진 것이 아니라, 지금은 그만 보내라고 답한 것입니다. 그래서 서버의 CPU나 메모리를 아무리 확인해도 원인이 나오지 않습니다.

실제로 429를 만나는 상황은 대개 네 가지입니다.

  • 외부 API를 호출하는 코드에서 제공자가 계정이나 API 키 단위로 걸어 둔 한도를 넘긴 경우입니다. 결제, 지도, 번역, AI API를 쓰는 기능이 여기 해당합니다.
  • 직접 운영하는 게이트웨이나 프록시에서 스스로 걸어 둔 제한이 작동한 것입니다. 호출이 몰릴 때 서비스를 지키려고 넣어 둔 설정이 정상 사용자까지 막는 일이 생깁니다.
  • AI API에 긴 입력을 보낼 때 요청 수는 몇 건 되지 않는데 토큰 수 한도에서 먼저 막히는 경우입니다.
  • 트래픽이 짧은 구간에 몰릴 때 분당 총량에는 여유가 있어도 초 단위로 몰린 순간에 한도를 넘습니다.

네 상황 모두 호출량이나 호출 시점을 조정하거나, 걸어 둔 제한을 바꿔야 해소됩니다. 그래서 어느 상황인지부터 먼저 확인해야 합니다.

429 Too Many Requests와 503 Service Unavailable 차이

503 Service Unavailable은 요청이 거부되고 잠시 뒤 다시 시도해야 한다는 점이 429와 같습니다. 그래서 같은 상황에서 나온 응답처럼 보입니다.

그런데 서버가 놓인 상태가 다릅니다. 429는 요청량이 서버가 정한 제한을 넘은 상태입니다. 503은 서버가 지금 요청을 처리할 수 없는 상태이고, RFC 9110은 그 사유를 일시적인 과부하나 예정된 점검으로 정의합니다.

상태 코드 서버가 놓인 상태 대응
429 Too Many Requests 요청량이 정해진 제한을 넘음 호출 속도를 조절한다
503 Service Unavailable 지금은 요청을 처리하지 못함 서버 쪽 복구를 기다린다

요청 제한에 걸렸을 때 항상 429가 돌아오는 것은 아닙니다. Nginx의 요청 제한 모듈은 한도를 넘긴 요청에 기본적으로 503을 반환합니다. limit_req_status의 기본값이 503이기 때문에 429를 사용하려면 설정값을 직접 바꿔야 합니다. 동시 연결 수를 제한하는 limit_conn 모듈도 기본값은 503입니다.

따라서 503이 발생했다고 해서 서버 과부하라고 단정하면 안 됩니다. 요청 경로에 Nginx 같은 게이트웨이나 프록시가 있다면 서버 상태뿐 아니라 요청 제한 설정도 확인합니다.

또 하나 확인할 것은 어떤 기준으로 요청 한도를 계산하는지입니다. RFC 6585는 서버가 사용자를 어떻게 식별하고 요청을 어떤 단위로 세는지 정하지 않습니다. 그래서 IP 주소, API 키, 엔드포인트 중 무엇을 기준으로 세는지가 서비스마다 다릅니다. 외부 API에서 429가 반복된다면 해당 서비스의 요청 제한 문서를 먼저 확인하는 편이 빠릅니다.

429가 어느 계층에서 온 응답인지 확인하기

요청 하나는 대개 여러 계층을 지납니다. 클라이언트가 보낸 요청은 API 게이트웨이나 프록시를 거쳐 애플리케이션에 도착하고, 애플리케이션은 다시 외부 API를 호출합니다. 계층마다 요청 제한이 걸려 있을 수 있으므로, 어느 계층이 돌려준 429인지 알면 볼 곳이 좁혀집니다.

429를 돌려준 곳 판별 단서 확인할 곳
직접 운영하는 게이트웨이나 프록시 애플리케이션 로그에 요청 기록이 없음 게이트웨이의 요청 제한 설정
직접 만든 애플리케이션 응답 본문이 자체 오류 포맷 애플리케이션의 제한 로직과 카운터 저장소
외부 API 제공자 제공자 고유 헤더가 응답에 붙음 제공자 사용량 화면과 한도 문서

외부 API 제공자

판별이 가장 쉬운 쪽은 외부 API 제공자입니다. 제공자마다 응답에 고유한 헤더를 붙이기 때문입니다. OpenAI는 남은 요청 수와 남은 토큰 수를 x-ratelimit-remaining-requests, x-ratelimit-remaining-tokens 같은 헤더로 내려보내고, Anthropic은 anthropic-ratelimit으로 시작하는 헤더로 같은 정보를 내려보냅니다. 응답에 이런 헤더가 있으면 외부 API 제공자가 돌려준 429입니다.

429가 발생하는 요청 한도 확인하기

요청은 몇 건 보내지 않았는데 429가 돌아오는 경우가 있습니다. 요청 수를 확인해 봐도 한도까지 여유가 남아 있습니다. 한도가 하나가 아니기 때문입니다.

한도는 보통 세 가지 기준으로 걸립니다. 일정 시간 동안의 요청 수, 주고받은 데이터 양, 동시에 처리 중인 연결 수입니다. 기준마다 따로 세기 때문에 요청 수에 여유가 있어도 데이터 양 한도를 넘긴 순간 429가 납니다.

AI API에서 특히 그렇습니다. OpenAI 공식 문서는 한도 단위로 분당 요청 수(RPM), 하루 요청 수(RPD), 분당 토큰 수(TPM), 하루 토큰 수(TPD), 분당 이미지 수(IPM)를 나열하고, 이 가운데 어느 하나에 먼저 도달하면 429가 난다고 설명합니다. 토큰은 모델이 글을 잘게 나눠 세는 단위입니다.

429 Too Many Requests 원인, 분당 요청 수는 38%로 여유가 있는데 분당 토큰 수가 한도에 도달해 429 응답이 나가는 것을 보여 주는 도식
긴 입력을 보내는 기능일수록 요청 수보다 토큰 수가 먼저 찹니다.

Anthropic은 입력과 출력을 나눠 셉니다. Anthropic 공식 문서는 한도를 분당 요청 수, 분당 입력 토큰 수(ITPM), 분당 출력 토큰 수(OTPM)로 정의하고, 어떤 한도를 넘겼는지 429 응답에 적어 보낸다고 안내합니다.

짧은 구간을 주의하라는 설명도 같은 문서에 붙어 있습니다. 분당 60건 한도가 초당 1건 단위로 적용될 수 있어, 짧게 몰아 보내면 분당 총량이 아직 차지 않았어도 한도 초과가 발생한다는 내용입니다. 분 단위 평균 그래프에 여유가 남아 보여도 초 단위로 몰린 구간에서는 429가 납니다.

429 에러의 해제 시점

RFC 6585는 429 응답에 Retry-After 헤더를 넣을 수 있다고 정해 두었습니다. MDN 문서에 따르면 값은 대기할 초 단위 정수이거나, 다시 시도해도 되는 시각을 적은 날짜 문자열입니다. 두 형식이 모두 올 수 있으므로 코드에서 둘 다 처리합니다.

Anthropic 문서는 Retry-After가 알려준 시간이 지나기 전에 재시도하면 실패한다고 명시합니다. 값을 무시하고 바로 다시 보내면 한도만 더 쓰고 응답은 똑같이 429입니다.

상황별 429 해결 방법

429를 돌려준 계층과 걸린 한도, 서버가 알려준 대기 시간을 확인하면 고칠 곳이 정해집니다.

확인 결과 고칠 곳
게이트웨이가 돌려준 429 제한을 세는 단위와 기준값
외부 API의 요청 수 한도 호출 속도 조절과 요청 병합
외부 API의 토큰 한도 프롬프트 길이와 요청 분할
분당 총량이 남은 상태의 429 호출 시점 분산
어느 경우든 재시도 설계

게이트웨이가 돌려준 429라면 제한 기준을 본다

직접 걸어 둔 제한이라면 값을 올리기 전에 세는 단위를 확인합니다. 표준이 식별 방식을 정하지 않기 때문에 구현마다 IP 단위, API 키 단위, 엔드포인트 단위가 다릅니다. IP 단위로 세면 사무실이나 통신사 장비 뒤에 있는 사용자 여러 명이 한 몫을 나눠 쓰게 되어, 한 명의 호출이 다른 사용자까지 막습니다. 로그인한 사용자라면 계정이나 키 단위로 바꾸는 편이 정확합니다.

제한을 걸 대상도 좁힐 수 있습니다. 서비스 전체에 같은 값을 적용하는 대신, 처리 비용이 큰 엔드포인트에만 낮은 값을 두는 방식입니다.

외부 API의 요청 수 한도라면 호출 속도를 조절한다

한도를 올려 달라고 요청하기 전에 보낼 요청 수를 줄일 여지를 봅니다. 같은 데이터를 여러 번 조회하고 있으면 결과를 캐시해 호출 자체를 없앨 수 있습니다. 건별로 보내는 호출은 제공자가 배치 요청을 지원하는지 확인해 한 번에 모아 보냅니다. 사용자 요청과 무관한 백그라운드 작업이 섞여 있으면 사용자 호출과 분리해 한가한 시간대로 옮기는 방법도 있습니다.

호출 수를 줄인 뒤에도 모자라면 상향을 요청합니다. 줄이기 전에 상향부터 요청하면, 늘어난 한도까지 불필요한 호출이 그대로 채웁니다.

토큰 한도라면 프롬프트와 요청 단위를 줄인다

토큰 한도는 요청 수와 따로 걸리므로 호출 횟수만 줄여서는 해소되지 않습니다. 긴 문서를 프롬프트에 그대로 붙이고 있으면 필요한 부분만 골라 보내고, 매 요청에 반복해서 붙이는 지시문이 있으면 길이를 줄입니다. 한 번에 처리하던 긴 입력을 나눠 보내면 짧은 구간에 몰리는 토큰 양은 줄어듭니다. 다만 분당 총량 한도에는 요청을 나누는 것만으로 효과가 없습니다.

출력 토큰을 따로 세는 제공자라면 응답 길이 상한도 함께 봅니다. 출력이 길어질수록 같은 요청 수로도 한도에 빨리 도달합니다.

분당 총량이 남았는데 걸린다면 호출 시점을 분산한다

분 단위 그래프에 여유가 보이는데 429가 난다면 짧은 구간에 몰린 것입니다. 매시 정각에 실행되는 배치 작업이 대표적인 경우입니다. 호출을 큐에 넣고 일정한 간격으로 꺼내 보내면 같은 총량을 고르게 내보낼 수 있습니다. 여러 인스턴스가 같은 대상을 호출한다면 인스턴스별로 나눠 쓸 몫을 정해 두는 방법도 있습니다.

어느 경우든 재시도는 지수 백오프와 지터로 설계한다

429를 받고 곧바로 다시 보내면 한도 카운터가 초기화되기 전이라 또 429가 돌아옵니다. 실패한 요청이 재시도로 두 배, 세 배가 되어 한도를 더 빨리 소진하고, 그만큼 정상 요청까지 밀려납니다. 재시도 증폭은 서버를 여러 대로 띄운 환경에서 더 커집니다. 같은 시점에 429를 받은 인스턴스들이 동시에 다시 보내기 때문입니다.

그래서 재시도에는 두 가지 장치를 함께 씁니다.

  • 지수 백오프(exponential backoff): 재시도할 때마다 대기 시간을 1초, 2초, 4초처럼 배로 늘리는 방식입니다. 실패가 이어질수록 호출 간격이 벌어집니다.
  • 지터(jitter): 계산한 대기 시간에 무작위 값을 섞어 재시도 시점을 분산하는 방식입니다. 여러 인스턴스가 같은 시각에 몰리는 것을 막습니다.

지터가 필요한 이유는 AWS 아키텍처 블로그의 Exponential Backoff And Jitter(Marc Brooker, 2015)에 정리되어 있습니다. 실험에서는 대기 시간을 배로 늘리기만 했을 때 재시도가 특정 시점에 모였고, 0과 상한 사이의 난수로 대기 시간을 잡았을 때 클라이언트가 보낸 총 호출 수가 크게 줄었습니다.

429 재시도에 지수 백오프만 쓸 때와 지터를 함께 쓸 때의 차이, 위 트랙은 인스턴스 다섯 대가 1·2·4·8초에 그대로 겹치고 아래 트랙은 같은 구간 안에서 재시도 시점이 흩어지는 모습을 비교한 도식
겹친 재시도는 한도를 한 번에 소진해 정상 요청까지 밀어냅니다.

Python 표준 라이브러리만 써도 대기 시간 계산 코드는 몇 줄이면 됩니다.

def wait_seconds(attempt, retry_after=None, base=1.0, cap=32.0, spread=1.0):    
  # retry_after는 헤더에서 초 단위로 바꿔 넘긴 값    
    if retry_after is not None:        
    # 서버가 알려준 시간은 최소 대기 기준으로 두고 지터를 더한다        
      return retry_after + random.uniform(0, spread)    
    ceiling = min(cap, base * (2 ** attempt))    
    return random.uniform(0, ceiling)

서버가 Retry-After로 대기 시간을 알려주었다면 직접 계산하지 않고 그 값을 기준으로 씁니다. 한도가 언제 초기화되는지는 서버가 알고 있고, 백오프 계산은 그것을 모르는 상태에서 어림잡는 방식이기 때문입니다. 헤더가 날짜 문자열로 오면 현재 시각과의 차이를 초로 바꿔 넘깁니다. 값이 없을 때만 재시도 횟수에 따라 대기 시간을 계산하고, 상한을 두어 대기가 끝없이 길어지지 않게 합니다.

여러 인스턴스가 같은 429 응답을 받았다면 Retry-After 값을 그대로 쓰지 않습니다. 그 값을 최소 대기 기준으로 두고 짧은 지터를 더해야 재시도 시점을 분산할 수 있습니다. 값을 그대로 쓰면 대기가 끝나는 같은 시점에 모든 인스턴스가 한꺼번에 다시 보냅니다.

재시도가 어디에서 일어나는지도 함께 확인합니다. 공식 SDK 가운데는 429를 자동으로 다시 보내도록 만들어 둔 것이 있습니다. 자동 재시도가 들어 있는 SDK를 쓰면서 애플리케이션에서 같은 호출을 한 번 더 재시도로 감싸면, 전송 횟수가 3회씩 두 겹으로 겹쳐 최대 9회가 됩니다. 재시도는 한 곳에서만 하도록 정리하는 편이 관리하기 쉽습니다.

429 원인 분석에 필요한 기록

429는 지나간 뒤에 원인을 확인하기 어려운 응답입니다. 어느 시간대에 어느 호출이 한도를 많이 썼는지, 재시도가 몇 배로 늘었는지는 그 시점의 기록이 있어야 알 수 있습니다.

호출할 때 다음 값을 남겨 두면 나중에 원인을 좁힐 수 있습니다.

  • 호출 수와 429 응답 수: 호출 대상별로 나눠 셉니다. 분 단위로만 모아 두면 초 단위로 몰린 구간이 평균에 묻히므로, 짧은 구간으로도 볼 수 있게 남깁니다.
  • 최초 시도와 재시도 구분: 둘을 섞어 세면 호출이 늘어난 원인이 트래픽 증가인지 재시도 증폭인지 알 수 없습니다. 재시도 횟수와 몇 번째 시도인지를 함께 남깁니다.
  • 응답 헤더의 남은 한도와 초기화 시각: 429가 나기 전에 여유가 줄어드는 구간을 확인할 수 있어 알림 기준으로도 쓸 수 있습니다.
  • 엔드포인트와 기능별 분포: 어떤 호출이 한도를 많이 썼는지 좁힙니다. API 키를 여러 개 쓴다면 키 단위로도 나눠 둡니다.
  • Retry-After 값과 실제 대기 시간: 서버가 알려준 값대로 실제로 기다렸는지 확인하는 값입니다. 둘이 어긋나면 재시도 코드부터 봅니다.

값을 남겨 두면 429가 났을 때 확인하는 순서가 짧아집니다. 429 비율이 오른 구간을 찾고, 그 구간에서 호출이 늘었는지 재시도가 늘었는지 나눠 보고, 어떤 엔드포인트가 몰렸는지까지 한 화면에서 확인하는 흐름입니다.

자주 묻는 질문

429가 떴는데 Retry-After 헤더가 없으면 얼마나 기다려야 하나요?

RFC 6585는 Retry-After를 넣을 수 있다고만 정해 두었고 반드시 넣으라고 요구하지 않습니다. 헤더가 없으면 응답에 함께 오는 남은 한도와 초기화 시각 계열 헤더를 먼저 확인합니다. 그마저 없으면 지수 백오프와 지터로 직접 계산합니다. 첫 대기 시간을 너무 짧게 잡으면 한도가 초기화되기 전에 다시 걸리기 쉽습니다.

여러 서비스가 같은 API 키를 쓰면 429를 어떻게 나눠 맡나요?

한도가 API 키 단위로 걸려 있으면 그 키를 쓰는 서비스들이 한 몫을 나눠 씁니다. 한 서비스에서 호출이 늘면 다른 서비스까지 429를 받습니다. 서비스마다 키를 따로 발급받으면 영향 범위가 나뉘고, 어느 서비스가 한도를 많이 썼는지도 키 단위로 구분됩니다. 키를 분리할 수 없는 사정이 있다면 호출하는 쪽에서 서비스별 상한을 두어 한 서비스가 전체 한도를 소진하지 못하게 막습니다.

429를 사용자에게 그대로 전달해도 되나요?

정답이 하나로 정해져 있지는 않고, 기능 성격에 따라 선택이 달라집니다. 즉시 결과가 필요 없는 작업이면 큐에 넣고 나중에 처리하는 방식이 있고, 조회 성격이면 직전에 캐시해 둔 결과를 보여주는 방식이 있습니다. 사용자에게 429를 그대로 전달할 때는 Retry-After를 함께 붙여 언제 다시 시도하면 되는지 알려줍니다. 다만 429를 500으로 바꿔 내보내면 호출하는 쪽에서 서버 장애로 처리해 재시도 정책이 다르게 작동할 수 있으므로 주의합니다.

검색 엔진 크롤러에 429를 돌려줘도 되나요?

크롤러 요청이 과하면 429로 수집 속도를 낮춰 달라고 알릴 수 있습니다. MDN 문서는 Googlebot을 비롯한 일부 크롤러가 Retry-After 값을 따른다고 설명합니다. 다만 이 헤더 지원은 클라이언트마다 일정하지 않다는 설명도 같은 문서에 함께 있습니다. 상시 대응보다는 부하가 몰리는 구간의 임시 조치로 두는 편이 안전합니다.

마치며

429 Too Many Requests가 뜨면 서버 상태를 확인하기 전에 응답 자체를 읽는 편이 빠릅니다. 어느 계층이 돌려준 응답인지, 어떤 한도에 걸렸는지, 얼마나 기다리라고 했는지가 헤더와 본문에 들어 있습니다. 계층과 한도와 대기 시간이 정해지면 고칠 곳도 따라 정해집니다. 게이트웨이 설정을 볼지, 호출 수를 줄일지, 프롬프트를 줄일지, 호출 시점을 분산할지가 달라집니다. 재시도 설계는 어느 경우든 함께 봅니다.

호출량과 재시도가 어느 시점에 몰렸는지는 그때 남긴 기록으로만 되짚을 수 있습니다. 와탭 APM은 애플리케이션이 외부로 보낸 HTTP 호출을 트랜잭션 단위로 기록합니다. 요청 하나가 어느 주소를 언제 호출했고, 응답에 얼마나 걸렸으며 오류로 끝났는지를 순서대로 확인할 수 있습니다. Python 애플리케이션 모니터링 소개 (와탭 공식 문서)에서 수집 항목을 확인해 보세요.

더 읽을거리

와탭 모니터링을 무료로 체험해보세요!