> ## Documentation Index
> Fetch the complete documentation index at: https://docs.bettertoken.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# BetterToken API 오류 해결과 요청 속도 제한

> HTTP 상태, 응답 본문, 요청 ID로 BetterToken API 오류를 진단하세요. 인증, 속도 제한, 타임아웃의 확인 방법과 재시도 조건을 Python·JavaScript 예제로 살펴보세요.

BetterToken API 오류는 HTTP 상태와 응답 본문을 함께 보고 진단하세요. `error.message`의 요청 ID를 기록하고, 인증이나 설정 오류를 수정한 뒤 다시 보내세요. 일시적인 장애로 확인된 경우에만 재시도하세요.

## 문제 해결 전 준비

Model ID, Base URL, 실패한 응답을 준비하세요. [BetterToken Dashboard](https://bettertoken.ai/workspace)에서 Key 상태와 현재 잔액을 확인하세요. 아직 모델 요청을 성공시키지 못했다면 [SDK 예제](/ko/guides/sdk-examples.md) 또는 [도구 설정 가이드](/ko/ai-tools/overview.md)부터 시작하세요.

## 오류 응답 읽기

BetterToken은 API 오류를 JSON 형태로 반환합니다. 예를 들어 Key 없는 요청은 다음 응답을 반환할 수 있습니다.

```json theme={null}
{
  "error": {
    "code": "",
    "message": "Token not provided (request id: …)",
    "type": "new_api_error"
  }
}
```

`error.code`가 비어 있어도 `error.message`를 읽으세요. 메시지의 요청 ID로 실패한 시도를 식별할 수 있습니다. Key 없이 `/v1/models`에 요청하면 `401`이 반환됩니다. 이 요청으로는 모델의 응답 생성 여부를 확인할 수 없습니다.

지원팀에 문의하기 전에 다음 정보를 기록하세요.

| 항목 | 기록할 내용 |
| - | - |
| HTTP 상태 | 응답 상태. HTTP 응답이 없으면 클라이언트의 타임아웃 또는 네트워크 예외 |
| 응답 본문 | 비밀 정보를 제거한 오류 JSON 또는 반환된 본문 |
| 요청 ID | `error.message`에 식별자가 있으면 기록 |
| 시간 | 시도한 날짜, 시간, 시간대 |
| Model ID | `model`로 전송한 정확한 값 |
| Base URL | 설정한 Base URL과 최종 요청 경로 |

스크린샷, 지원 문의, 공유 로그에 BetterToken API Key를 포함하지 마세요.

## HTTP 상태 확인하기

아래 표는 일반적인 HTTP/API 의미와 연결된 설정 안내를 기준으로 합니다. BetterToken 고유 오류 코드 목록은 아니므로 실제 응답을 읽고 해결 방법을 선택하세요.

| 상태 | 일반적인 의미 | 먼저 확인할 사항 | 재시도 여부 |
| - | - | - | - |
| `400` | 잘못된 요청, 필드 또는 매개변수 조합 | JSON 구조, 필수 필드, 모델, 엔드포인트 명세 | 요청부터 수정 |
| `401` | 인증 정보 누락, 유효하지 않음, 만료 또는 폐기 | Key 상태, 공백, 프로토콜에 맞는 인증 헤더 | 인증부터 수정 |
| `403` | 요청 거부 | 응답 메시지, 계정 잔액, Key 상태, 클라이언트 설정 | 거부 원인부터 해결 |
| `404` | 경로나 요청한 리소스를 찾을 수 없음 | Base URL, `/v1` 중복·누락, 최종 경로, 정확한 Model ID | URL이나 리소스부터 수정 |
| `408` / 클라이언트 타임아웃 | 서버가 제시간에 전체 요청을 받지 못했거나 클라이언트가 대기를 중단함 | HTTP 상태 수신 여부, 타임아웃 설정, 네트워크, Dashboard의 요청 상태 | 요청 완료 여부를 확인한 뒤 결정 |
| `409` / `422` 반환 시 | 일반적으로 충돌 또는 처리할 수 없는 내용 | 응답 상세 정보와 전송한 상태 또는 필드 | 원인부터 해결 |
| `429` | 제한에 도달함 | 응답 본문과 재시도 헤더. [제한 진단 FAQ](/ko/faq/codex/usage-limit-vs-api-rate-limit.md) 참고 | 일시적인 요청 속도 제한으로 확인된 경우에만 |
| `500` | 서버 측 장애 | 요청 기록, 메시지, 장애 지속 여부 | 일시적인 장애라면 횟수를 제한해 재시도 |
| `502` / `503` | 게이트웨이·업스트림 장애 또는 일시적인 사용 불가 | 응답 본문과 `Retry-After` 헤더 유무 | 일시적인 장애라면 횟수를 제한해 재시도 |
| `504` | 게이트웨이가 업스트림 응답을 기다리다 시간 초과 | Dashboard 기록과 요청 완료 여부 | 완료 여부 확인 후 재시도 |

프로토콜과 경로 접미사 오류는 [Base URL FAQ](/ko/faq/model-calling/base-url-config.md)를, 캐시 관련 질문은 [캐시 과금 FAQ](/ko/faq/service-assurance/cache-billing.md)를 참고하세요.

## 일시적인 장애 재시도하기

일시적인 `429` 또는 서버 장애로 확인되면 무작위 지연을 더한 지수 백오프를 상한 내에서 사용하세요. `Retry-After`가 반환되면 따르되 재시도 대기 한도를 넘으면 루프를 중단하고 밖에서 기다리세요. SDK 재시도와 직접 구현한 루프의 시도 횟수는 합산되므로 직접 정책을 구현할 때는 SDK 재시도를 끄세요.

원인을 수정하지 않은 `400`, `401`, `403`, 잔액 또는 설정 오류는 재시도하지 마세요. 클라이언트 타임아웃이나 스트림 중단이 처리 전에 실패했음을 의미하지는 않습니다. 특히 이미지 생성이나 작업이 중복될 수 있는 워크플로는 먼저 **Dashboard**를 확인하세요.

아래 예제는 비스트리밍 Chat Completions 요청을 최대 네 번 시도합니다. HTTP `500`, `502`, `503`을 재시도합니다. `429`는 응답이 일시적인 요청 속도 제한임을 확인할 때까지 재시도하지 않습니다. 타임아웃, 네트워크 예외, `504`에서는 중단하여 완료 여부를 확인할 수 있게 합니다. 시도당 재시도 대기는 최대 30초입니다.

### 요청 준비하기

[모델 광장](https://bettertoken.ai/pricing)에서 현재 Model ID를 복사하고 두 자리표시자를 모두 바꾸세요. [Chat Completions](/ko/api-reference/chat-completions.md)에서 사용 가능한 텍스트 모델을 선택하세요.

```bash theme={null}
export BETTERTOKEN_API_KEY="YOUR_API_KEY"
export BETTERTOKEN_MODEL_ID="YOUR_MODEL_ID"
```

Python 예제는 표준 라이브러리를 사용합니다. JavaScript 예제는 `.mjs` 파일로 저장하고 `fetch`와 `AbortSignal.timeout`을 제공하는 Node.js 버전으로 실행하세요.

**Python**

```python theme={null}
import json
import os
import random
import time
import urllib.error
import urllib.request
from datetime import datetime, timezone
from email.utils import parsedate_to_datetime

def retry_after_seconds(value):
    try:
        return max(0.0, float(value))
    except ValueError:
        return max(0.0, (
            parsedate_to_datetime(value) - datetime.now(timezone.utc)
        ).total_seconds())

retry_temporary_429 = False  # Enable only after diagnosing the returned limit.
payload = json.dumps({
    "model": os.environ["BETTERTOKEN_MODEL_ID"],
    "messages": [{"role": "user", "content": "Reply with one word: OK"}],
}).encode()
for attempt in range(4):
    request = urllib.request.Request(
        "https://bettertoken.ai/v1/chat/completions", data=payload,
        headers={
            "Authorization": "Bearer " + os.environ["BETTERTOKEN_API_KEY"],
            "Content-Type": "application/json",
        },
    )
    try:
        with urllib.request.urlopen(request, timeout=120) as response:
            print(json.load(response)["choices"][0]["message"]["content"])
        break
    except urllib.error.HTTPError as error:
        body = error.read().decode("utf-8", errors="replace")
        retryable = error.code in (500, 502, 503) or (
            error.code == 429 and retry_temporary_429
        )
        if not retryable or attempt == 3:
            raise RuntimeError(f"HTTP {error.code}: {body}") from error
        delay = random.uniform(0, min(8.0, 2.0 ** attempt))
        retry_after = error.headers.get("Retry-After")
        if retry_after:
            delay = max(delay, retry_after_seconds(retry_after))
        if delay > 30:
            raise RuntimeError("Retry-After exceeds this retry budget") from error
        time.sleep(delay)
```

**JavaScript**

```javascript theme={null}
const retryTemporary429 = false; // Enable only after diagnosing the returned limit.
const payload = {
  model: process.env.BETTERTOKEN_MODEL_ID,
  messages: [{ role: "user", content: "Reply with one word: OK" }],
};
for (let attempt = 0; attempt < 4; attempt++) {
  const response = await fetch("https://bettertoken.ai/v1/chat/completions", {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.BETTERTOKEN_API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify(payload),
    signal: AbortSignal.timeout(120000),
  });
  const body = await response.text();
  if (response.ok) {
    console.log(JSON.parse(body).choices[0].message.content);
    break;
  }
  const retryable = [500, 502, 503].includes(response.status) ||
    (response.status === 429 && retryTemporary429);
  if (!retryable || attempt === 3) {
    throw new Error(`HTTP ${response.status}: ${body}`);
  }
  let delay = Math.random() * Math.min(8000, 1000 * 2 ** attempt);
  const retryAfter = response.headers.get("Retry-After");
  if (retryAfter !== null) {
    const seconds = Number(retryAfter);
    const wait = Number.isFinite(seconds)
      ? seconds * 1000 : Date.parse(retryAfter) - Date.now();
    if (!Number.isFinite(wait)) throw new Error("Invalid Retry-After header");
    delay = Math.max(delay, wait, 0);
  }
  if (delay > 30000) throw new Error("Retry-After exceeds this retry budget");
  await new Promise(resolve => setTimeout(resolve, delay));
}
```

응답 본문에 지속적인 설정, 잔액, 접근 문제가 나타나면 상태 코드만으로 재시도가 가능해 보여도 루프를 중단하고 수정하세요. 이 짧은 텍스트 예제를 다른 작업에 적용하기 전에 앱에 맞는 완료 확인을 추가하세요.

## 증상에 맞는 가이드 찾기

| 증상 | 참고할 가이드 |
| - | - |
| Claude Code 인증, URL 또는 모델 오류 | [Claude Code의 흔한 오류](/ko/ai-tools/claude-code.md#common-errors) |
| Codex가 로그인을 요구하거나 제공자 변경을 무시함 | [Codex CLI / App의 흔한 오류](/ko/ai-tools/codex.md#common-errors) |
| Cursor에서 Key를 활성화할 수 없거나 내장 모델이 작동하지 않음 | [Cursor의 흔한 오류](/ko/ai-tools/cursor.md#common-errors) |
| Cline 인증, `404` 또는 모델 오류 | [Cline의 흔한 오류](/ko/ai-tools/cline.md#common-errors) |
| OpenCode 제공자, 모델 또는 프로젝트별 설정 덮어쓰기 오류 | [OpenCode의 흔한 오류](/ko/ai-tools/opencode.md#common-errors) |
| Claude Desktop Gateway 연결 실패 | [Claude Desktop의 흔한 오류](/ko/faq/claude-desktop-bettertoken-api.md#common-errors) |
| 공식 Codex 사용량 경고 또는 API `429` | [사용량 한도와 API 요청 속도 제한의 차이](/ko/faq/codex/usage-limit-vs-api-rate-limit.md) |
| 변경 사항이 반영되지 않음 | [충돌하는 환경 변수 정리](/ko/best-practices.md#clear-conflicting-environment-variables) |
| 출력이 잘리거나 출력 한도 오류 발생 | [출력 토큰 한도](/ko/faq/model-calling/max-tokens.md) |

## 해결 여부 확인하기

실패했던 클라이언트, Base URL, Model ID를 그대로 사용해 중요한 작업에 영향을 주지 않는 짧은 요청을 한 번 보내세요. 정상 응답을 확인하고 **Dashboard**에서 해당 기록을 찾으세요. 모델, 상태, 토큰 사용량, 요금을 확인한 뒤 더 큰 워크플로를 재개하세요.

## 자주 묻는 질문

### 429가 반환되면 항상 기다렸다가 재시도해야 하나요?

[사용량 제한 진단 FAQ](/ko/faq/codex/usage-limit-vs-api-rate-limit.md)를 보고 확인할 사항과 해당 응답에 재시도가 적절한지 판단하세요.

### 한 번 실패하면 타임아웃을 늘려야 하나요?

먼저 클라이언트, 프록시, 업스트림 요청 중 어디서 대기를 중단했는지 확인하세요. [타임아웃 설정 안내](/ko/best-practices.md#set-a-generous-timeout)를 적용한 뒤 작은 요청으로 검증하세요.

## 관련 가이드

* [SDK 요청 예제](/ko/guides/sdk-examples.md)
* [스트리밍과 도구 호출](/ko/guides/streaming-and-tool-calling.md)
* [API 레퍼런스](/ko/api-reference/introduction.md)
* [지원 도구](/ko/ai-tools/overview.md)
* [캐시 과금 FAQ](/ko/faq/service-assurance/cache-billing.md)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.