- Published on
OpenAI Responses API 402 결제·크레딧 오류 해결
- Authors
- Name
- 스타차일드
- https://x.com/ETFBITX
서버가 4xx를 돌려줄 때 가장 난감한 케이스가 402 Payment Required다. 인증(401)이나 권한(403)처럼 “설정 한 군데”를 고치면 끝나는 문제가 아니라, **크레딧/결제/조직·프로젝트 스코프/한도(usage limit)**가 얽혀 재현이 들쑥날쑥해 보이기 때문이다. 특히 OpenAI Responses API를 운영 환경에 붙인 뒤 갑자기 402가 터지면, 코드 결함이 아니라 과금 상태 변화(크레딧 소진, 결제 실패, 한도 초과, 프로젝트 선택 오류)일 가능성이 높다.
이 글은 “왜 402가 나는지”를 추측하는 수준이 아니라, 로그로 원인을 분류하고(재현 포함), 즉시 복구 + 재발 방지까지 가는 절차를 체크리스트로 정리한다.
> 참고: 네트워크 지연/서버 지연으로 인한 실패는 402가 아니라 408/5xx로 나타나는 경우가 많다. 타임아웃 계열은 별도 가이드(OpenAI Responses API 408 타임아웃 재현과 해결 실전 가이드)를 함께 보자.
1) 402의 의미: “결제 필요”가 아니라 “과금 경로가 막힘”
HTTP 표준에서 402는 실험적인 상태 코드지만, API에서는 관례적으로 요금 청구가 불가능하거나 허용되지 않음을 뜻한다. OpenAI Responses API에서 402가 나오면 대체로 아래 범주 중 하나다.
- 크레딧(무료/프로모션/선불) 소진
- 결제 수단 실패/만료/차단(카드 이슈, 청구 주소, 3DS 등)
- 조직(Organization) 또는 프로젝트(Project) 단위의 사용 한도 초과
- 요청이 잘못된 스코프로 청구되도록 설정됨(조직/프로젝트 선택 오류)
- 키는 유효하지만 결제 가능한 엔티티에 연결되지 않음(예: 개인/조직 전환 후 키/프로젝트 혼선)
핵심은 “API 키가 맞다”는 사실만으로 과금이 보장되지 않는다는 점이다. 운영에서는 API 키 + 청구 주체(조직/프로젝트) + 한도 정책이 모두 맞물린다.
2) 먼저 할 일: 에러 바디를 그대로 로그에 남겨 분류하기
402는 메시지에 단서가 들어있는 경우가 많다. 문제는 운영 로그에서 이를 누락하거나, “OpenAIError: 402”처럼 뭉개버리는 경우다. 아래처럼 HTTP status, request id(있다면), error message, org/project 관련 헤더를 최대한 보존하자.
Node.js(공식 SDK)에서 402 로깅 예시
import OpenAI from "openai";
const client = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
async function callResponses() {
try {
const res = await client.responses.create({
model: "gpt-4.1-mini",
input: "ping",
});
return res.output_text;
} catch (err) {
// SDK 에러는 형태가 바뀔 수 있으니, 안전하게 분기
const status = err?.status ?? err?.response?.status;
const headers = err?.headers ?? err?.response?.headers;
const data = err?.error ?? err?.response?.data;
console.error("OpenAI call failed", {
status,
requestId: headers?.["x-request-id"] ?? headers?.get?.("x-request-id"),
type: data?.type,
code: data?.code,
message: data?.message,
});
throw err;
}
}
callResponses();
여기서 얻고 싶은 건 단 하나다.
- 이 402가 “크레딧 부족”인지, “결제 실패”인지, “한도 초과”인지
메시지/코드가 모호하면, 아래 체크리스트를 순서대로 밟아가며 원인을 좁힌다.
3) 진단 체크리스트(10분 안에 원인 좁히기)
3.1 대시보드에서 크레딧/결제 상태 확인
가장 흔한 원인은 단순하다.
- 무료/프로모션 크레딧이 0이 됨
- 결제 수단이 만료/실패
- 결제 수단을 등록했지만 청구 활성화가 완료되지 않음
운영자가 “어제까지 됐는데 오늘 안 된다”고 말하면, 코드 변경보다 청구 상태 변화를 먼저 의심하자.
즉시 복구 팁
- 결제 수단 업데이트 후에도 402가 계속되면, 아래 3.2/3.3의 프로젝트 스코프 문제일 수 있다.
3.2 조직/프로젝트 스코프가 바뀌었는지 확인
OpenAI는 조직/프로젝트 단위로 키와 사용량이 묶이는 패턴이 흔하다. 다음 상황에서 402가 “갑자기” 터진다.
- 개인 계정에서 조직으로 옮기면서 기존 API 키가 다른 스코프를 가리키게 됨
- 프로젝트를 새로 만들고 키를 발급했는데, 서버에는 옛 키가 배포됨
- CI/CD에서 환경변수는 바뀌었는데, 런타임(컨테이너/람다)은 이전 시크릿을 계속 사용
이건 401이 아니라 402로도 나타날 수 있다(키 자체는 유효하지만, 청구 가능한 엔티티가 아니거나 제한된 엔티티일 때).
확인 방법(운영 관점)
- 배포된 서버가 실제로 어떤 키를 쓰는지(마스킹된 fingerprint라도) 확인
- “현재 호출이 어떤 프로젝트로 청구되는지”를 문서/대시보드에서 확인
3.3 사용 한도(Usage limit) / 예산(Budget) / 캡(Cap) 확인
402가 “크레딧 0”이 아니라 한도 초과로 발생할 수 있다. 대표적으로:
- 월간 사용 한도(soft/hard limit) 도달
- 프로젝트 예산이 0 또는 너무 낮게 설정됨
- 팀/조직 정책으로 특정 모델 사용이 차단되어 우회 호출이 실패
운영에서 자주 벌어지는 시나리오:
- 트래픽이 늘어났는데, 예산은 테스트 시절 값(예: $5)에 고정
- 배치 작업이 밤 사이 토큰을 대량 소비 → 아침에 실시간 API가 402
이 경우는 “결제 수단”이 정상이어도 402가 날 수 있다.
3.4 모델/기능이 더 비싼 경로로 바뀌었는지 확인
Responses API는 멀티모달, 툴 호출, 파일 입력 등으로 쉽게 비용이 늘어난다. 예를 들어:
- 텍스트만 쓰다가 이미지 입력을 추가
- 스트리밍 + 장문 컨텍스트로 토큰 사용량 급증
- 시스템 프롬프트에 정책/지침을 과도하게 누적
402가 떴다면 “요금이 갑자기 늘었나?”를 반드시 확인해야 한다. 비용 급증은 402로 끝나지 않고, 운영비 사고로 이어진다. 비용 관리 전반은 CloudWatch Logs 비용 폭증 원인과 절감 10가지처럼 관측/상한/샘플링 관점으로 접근하는 게 안전하다(LLM 호출 로그도 마찬가지).
3.5 프록시/게이트웨이에서 402를 “변조”하는지 확인
드물지만, 사내 API 게이트웨이/프록시가 업스트림 오류를 자체 규칙으로 402로 바꾸는 경우가 있다.
- WAF/프록시가 특정 헤더/바디 패턴을 유료 결제 페이지로 리다이렉트하려다 402
- 사내 “청구 태그”가 누락되면 402로 차단
이 경우 OpenAI 응답 바디에 있는 에러 구조가 아니라, 프록시 HTML/커스텀 JSON이 내려온다. 로그에서 content-type과 바디를 확인하자.
4) 재현 가능한 최소 테스트로 “서버 문제 vs 계정 문제”를 분리
운영 서버에서만 402가 난다면, 계정이 아니라 배포/시크릿/네트워크 경로 문제일 확률이 높다. 반대로 로컬에서도 같은 키로 402가 나면 계정/한도/결제 문제일 확률이 높다.
curl로 최소 재현
export OPENAI_API_KEY="..."
curl https://api.openai.com/v1/responses \
-H "Authorization: Bearer $OPENAI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-4.1-mini",
"input": "ping"
}' -i
여기서 확인할 것:
- HTTP status가 진짜 402인지
- 응답 바디가 OpenAI 에러 포맷인지(프록시 변조 여부)
x-request-id같은 추적 헤더가 있는지
“키가 바뀌었는지”를 배포 환경에서 검증
키 자체를 로그로 찍으면 안 된다. 대신 키의 일부 fingerprint(예: 마지막 4자리)만 남기거나, 시크릿 버전/리비전 번호를 남겨 서버가 어떤 시크릿을 참조 중인지를 확인한다.
function keyFingerprint(key) {
if (!key) return "(missing)";
return `len=${key.length}, tail=${key.slice(-4)}`;
}
console.log("OPENAI_API_KEY", keyFingerprint(process.env.OPENAI_API_KEY));
이 출력이 배포마다 다르게 나오는지 확인하면, “분명 시크릿을 바꿨는데도 402” 같은 상황에서 원인을 빨리 잡는다.
5) 해결 시나리오별 처방
5.1 크레딧 소진: 즉시 복구 + 비용 폭주 방지
- 결제 수단 추가/수정 또는 크레딧 충전
- 하드 캡(예산 상한)을 현실적인 값으로 설정
- 애플리케이션에서 토큰 상한을 강제
예: 입력 길이를 제한하고, 출력 토큰을 제한한다.
const res = await client.responses.create({
model: "gpt-4.1-mini",
input: userText.slice(0, 4000),
// SDK/모델에 따라 옵션명은 다를 수 있으니 문서 확인
max_output_tokens: 512,
});
또한 402는 “재시도”로 해결되지 않는다. 5xx처럼 무한 재시도를 걸면 결제/한도 문제가 해결될 때까지 워커가 계속 두드리며 장애를 키운다. 5xx 재시도/서킷브레이커는 별도 원칙으로 다루는 게 맞다(OpenAI Responses API 500·503 대응 재시도 폴백 서킷브레이커).
5.2 결제 수단 실패: “정상 결제”가 확인될 때까지 트래픽을 줄여라
결제 수단 이슈는 대시보드에서 수정해도 반영까지 시간이 걸릴 수 있고, 은행/카드사 측 거절 사유는 개발자가 제어하기 어렵다.
- 결제 수단 업데이트 후에도 402가 지속되면
- 다른 결제 수단으로 교체
- 조직/프로젝트 소유자에게 청구 상태 확인 요청
- 장애 공지(사용자에게 “일시적으로 제한”을 명확히)
애플리케이션 레벨에서는:
- 402 발생 시 즉시 폴백(더 싼 모델, 캐시된 답변, 기능 제한 모드)
- 백그라운드 배치/요약 작업은 중단
5.3 프로젝트/조직 스코프 불일치: 키/프로젝트를 “한 곳”으로 고정
가장 많이 꼬이는 패턴은 “키는 맞는데 청구 주체가 다름”이다. 해결 원칙:
- 운영/스테이징/개발을 프로젝트 단위로 분리
- 각 환경은 해당 프로젝트에서 발급한 키만 사용
- CI/CD 시크릿 이름에 프로젝트/환경을 포함
예:
OPENAI_API_KEY_PROD_PROJECT_XOPENAI_API_KEY_STG_PROJECT_X
그리고 배포 파이프라인에서 “시크릿 교체가 실제 런타임에 반영됐는지”를 검증하는 헬스체크를 넣는다.
5.4 사용 한도 초과: 한도 상향 + 호출량 제어(레이트/큐)
한도 초과는 “결제 수단을 넣으면 해결”이 아니다. 조직 정책/예산/캡을 올리거나, 트래픽을 제어해야 한다.
- 실시간 요청: 사용자별 레이트 리밋
- 배치/워커: 큐 기반으로 처리량 제한
- 고비용 기능(긴 컨텍스트, 파일, 이미지)은 별도 플랜/권한으로 분리
간단한 인메모리 레이트 리밋(예시)만 넣어도 사고를 줄일 수 있다.
const bucket = new Map();
function allow(userId, limit = 30, windowMs = 60_000) {
const now = Date.now();
const item = bucket.get(userId) ?? { count: 0, resetAt: now + windowMs };
if (now > item.resetAt) {
item.count = 0;
item.resetAt = now + windowMs;
}
item.count += 1;
bucket.set(userId, item);
return item.count <= limit;
}
// 사용 예
if (!allow(userId)) {
throw new Error("Rate limited");
}
운영 환경에서는 Redis/Upstash 같은 외부 저장소 기반으로 바꾸는 것을 권장한다.
6) 운영에서의 베스트 프랙티스: 402를 ‘장애’가 아니라 ‘상태’로 다뤄라
402는 서버가 망가진 게 아니라, 과금 상태가 요청을 거부하는 것이다. 따라서 처리 방식도 5xx와 달라야 한다.
6.1 알림 기준
- 402가 1~2건 발생: 사용자별 과다 사용/테스트일 수 있음
- 402가 급증: 크레딧 소진/한도 초과/결제 실패 가능성이 큼 → 즉시 알림
6.2 사용자 경험
- “잠시 후 다시 시도”만 띄우지 말고
- 결제/한도 문제로 기능이 제한되었음을 명확히 안내
- 가능하면 읽기 전용/캐시 응답/저비용 모드로 폴백
6.3 재시도 정책
- 402: 재시도 금지(즉시 실패 처리)
- 408/5xx: 제한적 재시도 + 지수 백오프 + 서킷브레이커
앞서 언급한 것처럼, 타임아웃(408)이나 서버 오류(500/503)와 섞이면 더 복잡해진다. 스트리밍/프록시 환경에서는 499/502 같은 다른 코드로도 증상이 나타날 수 있으니, LLM 트래픽을 프록시로 중계한다면 LLM SSE 스트리밍 499 502 급증과 응답 끊김을 잡는 프록시 튜닝 체크리스트도 함께 점검하는 게 좋다.
7) 현장에서 가장 많이 해결된 “원인 TOP 5”
- 프로모션 크레딧 소진(생각보다 빨리 닳음)
- 월간 사용 한도/프로젝트 예산이 너무 낮음
- 조직/프로젝트 전환 후 키 스코프가 꼬임(서버는 옛 키)
- 결제 수단 만료/실패(카드 교체/해외 결제 차단)
- 배치 작업이 야간에 토큰을 과다 사용 → 아침에 402
8) 결론: 402는 “코드 디버깅”이 아니라 “청구 경로 디버깅”이다
OpenAI Responses API에서 402를 만나면, 프롬프트나 SDK 버전을 붙잡고 있기보다 청구 주체(조직/프로젝트)와 한도, 크레딧, 결제 상태를 먼저 확인해야 한다. 그리고 운영 시스템은 402를 재시도로 해결하려 하지 말고, 즉시 폴백/기능 제한/명확한 알림으로 사용자 경험을 지키는 방향이 안전하다.
마지막으로, 402를 한 번 겪었다면 “다음에는 안 나겠지”가 아니라, 다음을 반드시 남겨두자.
- 402 에러 바디/요청 ID를 포함한 로그
- 프로젝트/환경별 키 관리 원칙
- 예산/한도 상향 프로세스(누가, 어디서, 얼마나)
- 토큰/요청량 제한(레이트 리밋, 큐, max tokens)
이 4가지만 갖추면 402는 장애가 아니라 예측 가능한 운영 이벤트가 된다.