Summary#
Core API의 rate limiting 계약은 단순히 “많이 요청하면 막는다”가 아니라, 클라이언트가 언제 재시도해야 하는지, 서버가 quota 상태를 어떻게 노출하는지, 중간 프록시/CDN이 어떤 방식으로 의미를 왜곡할 수 있는지까지 포함하는 API 안정성 계약이다.
권장 기본 모델은 다음과 같다.
- 429 Too Many Requests: 클라이언트 또는 주체별 quota 초과를 표현한다.
- 503 Service Unavailable: 전역 과부하, 일시적 용량 부족, maintenance 등 “서비스 자체가 현재 처리 불가”일 때 사용한다.
- Retry-After: 클라이언트가 재시도 가능한 최소 시점을 명시한다. 429와 503 양쪽 모두에서 사용할 수 있다.
- RateLimit headers: RFC 9333의
RateLimit-Limit,RateLimit-Remaining,RateLimit-Reset를 사용해 현재 quota window 또는 limit 상태를 힌트로 제공한다. - Token bucket / leaky bucket semantics: 실제 구현은 token bucket 계열일 수 있지만, 외부 계약에서는 “remaining/reset”의 의미가 clock skew, distributed counter lag, burst allowance와 충돌하지 않도록 보수적으로 설명해야 한다.
- Proxy/CDN interference: CDN, gateway, WAF, reverse proxy가 자체 429/503을 생성하거나
Retry-After/RateLimit 계열 header를 덮어쓸 수 있으므로, origin-generated rate limit과 edge-generated rate limit을 구분할 수 있는 관측·문서화가 필요하다.
Key Points#
-
429와 503의 계약상 차이 -
429 Too Many Requests는 요청 주체의 rate/quota 초과에 적합하다. -503 Service Unavailable은 서버 또는 전체 서비스가 일시적으로 처리 불가능한 경우에 적합하다. - 둘 다Retry-After를 포함할 수 있지만 의미가 다르다.- 429의
Retry-After: 해당 주체가 다시 요청해도 되는 최소 대기 시간. - 503의
Retry-After: 서비스가 다시 가용해질 것으로 기대되는 최소 시간.
- 429의
-
Retry-After는 backoff 계약의 핵심 -
Retry-After는 초 단위 delta-seconds 또는 HTTP-date 형식일 수 있다. - 클라이언트는Retry-After가 있으면 임의의 짧은 exponential backoff보다 이를 우선해야 한다. - 단, 안전한 클라이언트는Retry-After가 없거나 비정상인 경우를 위해 capped exponential backoff와 jitter를 가져야 한다. -
RFC 9333 RateLimit headers - RFC 9333은 다음 header fields를 정의한다.
RateLimit-LimitRateLimit-RemainingRateLimit-Reset- 이 header들은 quota 상태를 나타내는 힌트이며, 반드시 모든 요청에서 정확한 billing ledger처럼 해석되면 안 된다.
- distributed system에서는 replica lag, shared quota, burst allowance, clock skew 때문에 값이 보수적이거나 근사치일 수 있다.
-
기존 비표준 header와의 병행 - 많은 API는 오래전부터
X-RateLimit-Limit,X-RateLimit-Remaining,X-RateLimit-Reset같은 비표준 header를 사용해 왔다. - GitHub REST API도x-ratelimit-limit,x-ratelimit-remaining,x-ratelimit-reset,x-ratelimit-used,x-ratelimit-resource를 문서화한다. - 신규 Core API는 RFC 9333 header를 우선하되, 기존 클라이언트 호환을 위해X-RateLimit-*를 병행 제공할 수 있다. -
GitHub 사례: 403과 429가 모두 가능 - GitHub REST API는 primary rate limit과 secondary rate limit을 구분한다. - rate limit 초과 시
403또는429를 반환할 수 있다고 문서화한다. -Retry-After가 있으면 그 시간만큼 대기해야 하며, 없으면x-ratelimit-reset또는 별도 backoff 정책을 따라야 한다. - 이는 “429만 rate limit이다”라는 단순 가정이 실제 대형 API와 맞지 않을 수 있음을 보여준다. -
Cloudflare 사례: edge/API rate limit과 header 계약 - Cloudflare API 문서는 rate limit 초과 시
429와Retry-After를 사용하는 방식을 설명한다. - Cloudflare는Ratelimit및Ratelimit-Policy형태의 header 예시도 문서화한다. - CDN 또는 edge provider가 origin 대신 rate limit을 적용하는 경우, 응답의 주체가 origin인지 edge인지 클라이언트가 구분하기 어려울 수 있다. -
Token bucket semantics를 외부 계약으로 노출할 때 주의 - token bucket은 burst를 허용하고 일정 속도로 token을 refill하는 모델이다. - 외부 header의
remaining값은 “현재 bucket token 수”처럼 보일 수 있지만, 실제 구현이 distributed bucket, sliding window, fixed window, GCRA일 수도 있다. - 따라서 API 문서에는 구현 알고리즘명을 과도하게 약속하기보다 다음을 명확히 하는 편이 안전하다.- limit scope: user, token, IP, organization, route, method, tenant 등
- reset 의미: 완전 초기화 시각인지, 다음 token 사용 가능 시점인지, window 종료까지 남은 시간인지
- burst 허용 여부
- 초과 시 status code와 body schema
Retry-After와RateLimit-Reset중 우선순위
-
권장 Core API response contract - quota 초과 응답 예시:
- status:
429 - headers:
Retry-After: <seconds>RateLimit-Limit: <limit>RateLimit-Remaining: 0RateLimit-Reset: <seconds-until-reset-or-refill>- body:
- stable error code:
rate_limited - human message
- optional retry timestamp
- optional limit scope
- 전역 과부하 응답 예시:
- status:
503 - headers:
Retry-After: <seconds>- body:
- stable error code:
temporarily_unavailable또는overloaded - 클라이언트 정책:
Retry-After우선- 없으면
RateLimit-Reset참고 - 둘 다 없으면 capped exponential backoff + jitter
- 재시도 가능한 method/idempotency 여부 확인
- status:
-
Proxy/CDN interference failure modes - CDN이 origin보다 먼저 429를 반환한다. - WAF나 bot protection이 403/429를 반환하지만 origin API error schema와 다르다. - Gateway가
Retry-After를 추가하거나 제거한다. - Proxy cache가 429/503을 부적절하게 cache한다. - 여러 layer가 각각X-RateLimit-*또는RateLimit-*를 설정해 의미가 충돌한다. - Client IP 기반 limit이 NAT, corporate proxy, mobile carrier gateway 뒤에서 의도보다 많은 사용자를 묶는다. -X-Forwarded-For신뢰 경계가 잘못되어 공격자가 limit key를 우회하거나 타 사용자 quota를 오염시킨다. - Edge와 origin의 clock skew 때문에reset시각이 모순된다. -
운영 권장사항
- origin-generated rate limit과 edge-generated rate limit을 구분하는 response marker를 둔다.
- 예:
error.source: "origin"/"edge"또는 provider-specific trace id. Retry-After를 machine-readable하게 유지한다.- public API 문서에는 status code만이 아니라 header 우선순위와 retry 알고리즘을 함께 명시한다.
- rate limit metric은 최소한 다음 dimension으로 관측한다.
- route
- tenant/user/token
- edge/origin source
- status code
- limit policy name
- retry-after bucket
- cache layer에는 429/503의 caching policy를 명시적으로 설정한다.
Cautions#
- 현재 환경에는 사용자가 요구한
WebSearch/WebFetch도구가 노출되어 있지 않아, 실시간 공개 웹 검색 및 fetch 검증을 수행하지 못했다. 아래 Sources는 공개적으로 알려진 공식 문서 URL 후보이며, 최종 capsule 확정 전 실제 URL 접근 및 최신성 검증이 필요하다. - RFC 9333의
RateLimit-Reset의미는 구현별 “window reset timestamp”와 동일하다고 단정하면 안 된다. 문서상 의미와 각 API의 해석을 분리해야 한다. - GitHub는 rate limit 초과에 대해
403과429를 모두 언급하므로, 클라이언트는 status code 하나만으로 rate limit 여부를 판단하지 말고 header/body/error code를 함께 확인해야 한다. - Cloudflare 관련 수치와 header 예시는 제품/API 영역에 따라 달라질 수 있다. Cloudflare API 자체의 limit, Cloudflare가 보호하는 고객 origin의 edge rate limiting, WAF/bot mitigation은 서로 다른 계층이다.
- Token bucket은 좋은 설명 모델이지만, 실제 API가 fixed window, sliding window, GCRA 또는 hybrid distributed quota를 사용할 수 있다. 외부 계약에는 알고리즘보다 observable behavior를 우선 명시하는 것이 안전하다.
- CDN/proxy가 생성한 429/503은 origin API의 의도된 error contract와 다를 수 있다. Core API contract에는 “중간 계층에서 생성된 응답은 body schema가 다를 수 있음”을 명시하거나, gateway에서 schema normalization을 수행해야 한다.
Sources#
- https://www.rfc-editor.org/rfc/rfc9333.html
- https://www.rfc-editor.org/rfc/rfc9110.html
- https://docs.github.com/en/rest/using-the-rest-api/rate-limits-for-the-rest-api
- https://docs.github.com/en/rest/using-the-rest-api/troubleshooting-the-rest-api
- https://developers.cloudflare.com/fundamentals/api/reference/limits/
- https://developers.cloudflare.com/cache/concepts/default-cache-behavior/
- https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/429
- https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After
Related#
- Core API Rate Limiting Contracts: RFC 9333 Headers, Quota Semantics, Distributed Counter Drift, and Retry-After Failure Modes
- Skip Failure Modes
- Core API Error Envelope Contracts: RFC 9457 Problem Details, Machine-Readable Codes, Field Violations, and Retryability Failure Modes
Sagwan Revalidation 2026-07-03T10:18:22Z#
- verdict:
ok - note: RFC 9333·429/503·Retry-After 권장안 모두 현재 practice와 부합함
Sagwan Revalidation 2026-07-04T17:59:38Z#
- verdict:
ok - note: RFC 9333·429/503·Retry-After 설명이 현재도 유효함
Sagwan Revalidation 2026-07-05T22:15:44Z#
- verdict:
ok - note: RFC 9333·429/503·Retry-After 설명이 현재 practice와 부합함
Sagwan Revalidation 2026-07-07T04:03:33Z#
- verdict:
ok - note: RFC 9333·Retry-After·429/503 권장 관행 모두 현재도 유효함
Sagwan Revalidation 2026-07-08T10:19:18Z#
- verdict:
ok - note: RFC 9333·429/503·Retry-After 권장안 모두 현재도 유효함
Sagwan Revalidation 2026-07-10T11:37:13Z#
- verdict:
ok - note: RFC 9333·429/503·Retry-After 권장안 모두 현행 practice와 부합함
Sagwan Revalidation 2026-07-12T05:06:26Z#
- verdict:
ok - note: RFC 9333·429/503·Retry-After 설명 모두 현재 practice와 부합함
Sagwan Revalidation 2026-07-14T00:56:23Z#
- verdict:
ok - note: RFC 9333·429/503·Retry-After 권장안 모두 현재 관행과 부합함
Sagwan Revalidation 2026-07-16T01:21:21Z#
- verdict:
ok - note: HTTP 429/503, Retry-After, RFC 9333 권장은 여전히 유효함
Sagwan Revalidation 2026-07-18T02:35:43Z#
- verdict:
ok - note: RFC 9333·429/503·Retry-After 설명이 현재 관행과 부합함
Sagwan Revalidation 2026-07-20T04:02:32Z#
- verdict:
ok - note: RFC 9333·429/503·Retry-After 권고와 프록시 주의점 모두 여전히 유효함