Claude Code가 Max 구독 대신 API 키로 과금될 때 해결법
Max를 구독하는데 platform.claude.com에 API 요금이 찍힌다면 ANTHROPIC_API_KEY가 구독 로그인보다 먼저 선택된 것입니다. 인증 우선순위, 키가 숨는 다섯 곳, unset 명령, /config 토글까지 되돌리는 순서를 정리합니다.
증상: Max 한도는 그대로인데 API 요금이 쌓인다
Claude Code는 셸 환경에 ANTHROPIC_API_KEY가 있으면 /login으로 저장한 Max 구독 로그인보다 그 키를 먼저 씁니다. 그래서 Max를 구독하면서 서버나 노트북에 API 키를 export 해 두면, 구독 한도는 손도 대지 않은 채 platform.claude.com의 Console 계정에 종량제 요금이 쌓입니다. 고치는 방법은 unset ANTHROPIC_API_KEY로 키를 치우고 /status로 어떤 인증이 살아 있는지 확인하는 것입니다. 나머지는 그 키가 어디에 숨어 있었는지 찾는 일입니다.
흔한 흐름은 이렇습니다. 한국에서 Max 요금제를 결제하고, 집 PC를 꺼도 작업이 이어지도록 VPS에 Claude Code를 올리고 tmux 안에서 돌리는 구성을 만듭니다. 그런데 그 서버의 ~/.bashrc에는 몇 달 전 SDK를 테스트하며 넣어 둔 export ANTHROPIC_API_KEY=sk-ant-... 한 줄이 남아 있습니다. claude를 처음 실행하면 브라우저 로그인 화면 대신 이 키를 승인할지 묻는 화면이 뜨고, 바쁜 손은 Enter를 누릅니다. 아니면 그 화면을 본 기억조차 없습니다. 몇 주 뒤 Console에서 결제 알림 메일이 오고, claude.ai 쪽 Max 사용량은 여전히 거의 비어 있습니다.
공식 문서는 이 동작을 한 문장으로 못 박습니다. "If you've set the ANTHROPIC_API_KEY environment variable, Claude Code skips the login prompt and asks you to approve the key instead." 로그인 화면을 건너뛰고 키 승인을 묻는다는 뜻입니다. 버그가 아니라 설계된 우선순위입니다.
왜 그런가: ANTHROPIC_API_KEY가 구독 로그인보다 먼저 선택된다
Claude Code는 자격 증명이 여러 개 있을 때 정해진 순서로 하나만 고릅니다. 2026년 9월 16일 기준 공식 인증 문서(code.claude.com/docs/en/authentication)가 밝히는 순서입니다.
- 클라우드 제공자 자격 증명.
CLAUDE_CODE_USE_BEDROCK,CLAUDE_CODE_USE_VERTEX,CLAUDE_CODE_USE_FOUNDRY중 하나가 켜져 있을 때 적용됩니다. ANTHROPIC_AUTH_TOKEN환경 변수.Authorization: Bearer헤더로 전송되며, 게이트웨이나 프록시를 거칠 때 씁니다.ANTHROPIC_API_KEY환경 변수.X-Api-Key헤더로 전송되는 Console API 키입니다.apiKeyHelper설정이 실행한 스크립트의 출력.CLAUDE_CODE_OAUTH_TOKEN환경 변수.claude setup-token으로 만든 장기 OAuth 토큰입니다.- Anthropic 프로필과 페더레이션 자격 증명.
antCLI와 Workload Identity Federation이 쓰는 것입니다. /login으로 저장한 구독 OAuth 자격 증명. Pro, Max, Team, Enterprise 사용자의 기본값입니다.
Max 구독 로그인은 이 목록의 맨 마지막입니다. 그 위에 있는 여섯 가지 중 하나라도 존재하면 구독은 선택되지 않습니다. 이것이 원인의 전부입니다. 키가 환경에 있으니 3번에서 멈추고, 7번까지 내려가지 않습니다.
대화형 모드에서는 한 번 승인 여부를 묻습니다. 문서 표현으로는 "you are prompted once to approve or decline the key, and your choice is remembered", 즉 한 번 물어보고 그 선택을 기억합니다. 그래서 첫날 Enter를 눌렀다면 이후로는 다시 묻지 않고 조용히 키를 씁니다. -p 비대화형 모드에서는 묻지도 않습니다. "In non-interactive mode (-p), the key is always used when present." 키가 있으면 언제나 키를 씁니다. cron이나 스크립트에서 claude -p를 돌리는 서버라면 승인 화면을 본 적이 없어도 요금은 API로 나갑니다.
키를 쓸지 구독을 쓸지는 선택의 문제이고, 그 선택의 기준은 구독 로그인과 API 키 중 무엇으로 Claude Code에 로그인할지 고르는 기준에서 따로 다룹니다. 여기서는 그 선택을 이미 Max로 끝낸 사람이 환경 변수 하나 때문에 반대 결과를 얻는 상황만 다룹니다.
지금 무엇이 쓰이고 있는지 확인하기
셸에서 먼저 봅니다.
env | grep -i anthropicANTHROPIC_API_KEY=sk-ant-... 같은 줄이 나오면 그 셸에서 띄우는 Claude Code는 이 키를 후보로 잡습니다. ANTHROPIC_AUTH_TOKEN이나 CLAUDE_CODE_OAUTH_TOKEN이 함께 나와도 메모해 둡니다. 아무것도 안 나오면 셸 환경은 깨끗한 것이고, 뒤에서 다룰 설정 파일과 apiKeyHelper를 봐야 합니다.
다음은 Claude Code 안에서 확인합니다.
claude프롬프트에 /status를 입력합니다. 문서는 로그인과 API 키가 둘 다 잡혀 있을 때 /status가 "marks the credential that isn't in use", 즉 사용되지 않는 쪽 자격 증명에 표시를 붙인다고 설명합니다. 정확한 문구는 버전마다 다를 수 있으니 화면에서 직접 읽는 편이 낫습니다. Login method 행을 찾고, 어느 쪽에 사용 안 함 표시가 붙었는지 보면 됩니다. 구독 로그인 쪽에 표시가 붙어 있다면 지금 이 순간 요금은 API로 나가고 있습니다.
Console 쪽에서도 교차 확인이 됩니다. platform.claude.com에 로그인해 사용량 페이지를 열면 날짜별 요청과 토큰이 보입니다. Claude Code를 쓴 날짜와 겹치면 확정입니다. 반대로 claude.ai 쪽 Max 사용량은 거의 움직이지 않았을 것입니다.
키가 숨어 있는 다섯 곳
unset은 현재 셸에서만 효력이 있습니다. 새 SSH 세션을 열면 어딘가의 파일이 키를 다시 export 하고, 문제는 그대로 돌아옵니다. 그래서 진짜 작업은 키의 출처를 찾는 일입니다. 서버에서 흔한 순서대로 살펴봅니다.
셸 시작 파일
grep -n ANTHROPIC ~/.bashrc ~/.profile ~/.bash_profile ~/.zshrc 2>/dev/null~/.bashrc:42:export ANTHROPIC_API_KEY=sk-ant-... 형태로 파일 이름과 줄 번호가 나옵니다. 편집기로 열어 그 줄을 지웁니다. 2>/dev/null은 없는 파일에 대한 오류 메시지를 감추는 것뿐입니다. 이 줄이 가장 흔한 범인입니다. SSH 로그인 셸은 ~/.profile을 읽고, Ubuntu의 기본 ~/.profile은 다시 ~/.bashrc를 읽습니다. 그래서 어느 쪽에 넣었든 매 접속마다 다시 export 됩니다. VPS를 처음 세팅하며 일단 넣어 두자고 쓴 줄이 여기에 남습니다.
프로젝트 디렉터리의 .env와 .envrc
cd ~/my-project
grep -n ANTHROPIC .env .envrc 2>/dev/null.env 파일 자체는 셸이 읽지 않습니다. 하지만 direnv를 쓰면 디렉터리에 들어갈 때 .envrc가 실행되어 변수가 export 되고, 그 상태에서 claude를 띄우면 키가 환경에 들어갑니다. set -a; source .env; set +a 같은 줄을 습관적으로 치는 사람도 같은 결과를 얻습니다. 프로젝트마다 다른 키를 쓰는 SDK 개발 환경에서는 자연스러운 구성이지만, 그 프로젝트 안에서 Max 구독으로 Claude Code를 쓰고 싶다면 두 가지가 충돌합니다. .envrc에서 해당 줄을 빼고 direnv allow를 다시 실행하거나, 아래에서 다룰 /config 토글로 Claude Code만 키를 무시하게 만듭니다.
설정 파일의 env 블록
Claude Code 설정 파일에는 env 키가 있고, 여기에 적은 변수는 셸과 무관하게 모든 세션에 주입됩니다.
grep -n -E 'ANTHROPIC|apiKeyHelper' ~/.claude/settings.json .claude/settings.json .claude/settings.local.json 2>/dev/null세 파일의 범위는 다릅니다. ~/.claude/settings.json은 이 사용자의 모든 프로젝트, .claude/settings.json은 저장소에 커밋되어 팀 전체, .claude/settings.local.json은 이 프로젝트에서 나만 해당합니다. 이런 모양이면 범인입니다.
{
"env": {
"ANTHROPIC_API_KEY": "<Console에서 만든 키>"
}
}문서가 밝히는 두 가지 성질 때문에 이 위치가 특히 찾기 어렵습니다. 첫째, "When the same variable is set in both your shell and a settings file env block, the settings file value applies." 셸과 설정 파일에 같은 변수가 있으면 설정 파일이 이깁니다. 그러니 셸에서 unset을 해도 소용이 없고, env | grep에 아무것도 안 나오는데 Claude Code는 계속 키를 씁니다. 둘째, "Removing a variable from the file doesn't unset it in a running session; the removal takes effect the next time you launch claude." 파일에서 지워도 켜져 있는 세션에는 남고, 다음 실행부터 사라집니다. 지운 뒤에는 반드시 Claude Code를 종료하고 다시 띄워야 합니다.
팀 저장소의 .claude/settings.json에 키가 들어 있다면 그건 또 다른 문제입니다. 커밋된 API 키는 git 기록에 그대로 남으니, 지우는 것으로 끝내지 말고 Console에서 그 키를 폐기하고 새로 만드는 것이 맞습니다.
apiKeyHelper
apiKeyHelper는 설정 파일에 적은 스크립트 경로이고, Claude Code가 그 스크립트를 실행해 출력값을 API 키로 씁니다. 볼트에서 단기 토큰을 꺼내 오는 회사 환경용 기능인데, 예전에 실험하다 남긴 한 줄이 우선순위 4번 자리에서 구독을 계속 밀어냅니다. 위의 grep -E 'ANTHROPIC|apiKeyHelper' 명령이 이 항목도 같이 잡아 줍니다. 문서에 따르면 Claude Code는 기본적으로 5분마다 이 스크립트를 다시 실행하므로, 스크립트가 있는 한 키는 계속 공급됩니다. 설정 파일에서 apiKeyHelper 항목을 지우면 됩니다.
오래 살아 있는 tmux 서버
VPS에서 tmux를 쓴다면 한 가지 더 있습니다. tmux 서버는 처음 시작될 때의 환경을 기억하고, 그 뒤에 만드는 모든 창과 패인에 물려줍니다. ~/.bashrc에서 export 줄을 지웠어도, 그 줄이 있던 시절에 시작된 tmux 서버가 아직 살아 있으면 새 창의 셸은 키를 물려받은 채 시작합니다.
tmux show-environment -g | grep -i anthropic여기에 ANTHROPIC_API_KEY=...가 나오면 tmux가 출처입니다. 가장 확실한 정리는 작업을 저장하고 tmux kill-server로 서버를 내린 뒤 다시 시작하는 것입니다. 세션을 유지해야 한다면 tmux set-environment -gu ANTHROPIC_API_KEY로 전역 환경에서 변수를 빼고, 이후에 만드는 새 창에서 env | grep -i anthropic으로 확인합니다. 이미 열려 있는 창의 셸은 영향을 받지 않으니 그 창에서는 unset을 직접 칩니다.
해결: unset 뒤 /status로 확인
출처를 찾았으면 순서는 짧습니다.
unset ANTHROPIC_API_KEY
env | grep -i anthropic
claude두 번째 명령은 아무것도 출력하지 않아야 합니다. 그 상태에서 claude를 띄우고 /status를 다시 봅니다. 이번에는 구독 로그인 쪽이 사용 중이어야 합니다. 문서의 처방도 정확히 이 두 줄입니다. "Run unset ANTHROPIC_API_KEY to fall back to your subscription, and check /status to confirm which method is active."
/status에 로그인 정보 자체가 없다면 이 서버에서는 /login을 한 적이 없는 것입니다. 키가 로그인 화면을 건너뛰게 만들었으니 당연한 결과입니다. 프롬프트에서 /login을 입력합니다. 브라우저가 열리지 않는 SSH 세션에서는 화면에 표시된 로그인 URL을 로컬 브라우저에서 열고, 브라우저가 코드를 보여 주면 터미널의 Paste code here if prompted에 붙여 넣습니다. 문서는 이 코드 방식이 SSH 세션과 컨테이너에서 흔하다고 설명합니다. Login successful이 뜨면 끝입니다. 리눅스에서는 이 로그인이 ~/.claude/.credentials.json에 0600 권한으로 저장되고, 이후 세션은 이 파일을 읽습니다.
마지막으로 영구성을 확인합니다. SSH 연결을 끊고 다시 접속한 뒤 env | grep -i anthropic을 한 번 더 칩니다. 여기서 키가 다시 나타나면 위의 다섯 곳 중 아직 못 찾은 곳이 있는 것입니다.
키를 유지한 채 Claude Code만 구독으로 쓰기
같은 서버에서 SDK 스크립트나 다른 도구가 ANTHROPIC_API_KEY를 필요로 한다면 변수를 아예 지울 수 없습니다. 이럴 때는 첫날의 승인 선택을 뒤집으면 됩니다. Claude Code 안에서 /config를 열면 "Use custom API key" 토글이 있습니다. 이 토글을 끄면 키가 환경에 있어도 대화형 세션은 구독 로그인을 씁니다. 문서가 덧붙이는 조건 하나는 기억할 만합니다. "The toggle only appears while ANTHROPIC_API_KEY is set in your environment." 변수가 없으면 토글도 안 보이니, 토글이 안 보인다고 당황할 일은 아닙니다. 끈 뒤에는 /status로 결과를 확인합니다.
단, 이 토글은 대화형 세션에만 적용됩니다. -p 모드는 "always used when present"이므로 토글과 무관하게 키를 씁니다. 스크립트에서 구독을 쓰려면 그 호출에서만 변수를 빼야 합니다.
env -u ANTHROPIC_API_KEY claude -p "이 디렉터리의 테스트 결과를 요약해 줘"env -u는 지정한 변수를 제거한 환경으로 다음 명령을 실행합니다. 셸 전체의 변수는 그대로 남고, 이 claude 프로세스만 키를 못 봅니다. 그런데 -p 모드는 브라우저 로그인을 할 수 없으니 구독 자격 증명이 따로 있어야 합니다. 그 용도가 claude setup-token입니다.
claude setup-token브라우저 승인을 거치면 1년짜리 OAuth 토큰이 터미널에 출력됩니다. 문서에 따르면 이 명령은 토큰을 어디에도 저장하지 않으니 직접 복사해 CLAUDE_CODE_OAUTH_TOKEN 환경 변수로 넣습니다. Pro, Max, Team, Enterprise 구독이 있어야 발급되고, 모델 요청만 할 수 있습니다. 여기서 우선순위 목록을 다시 봐야 합니다. CLAUDE_CODE_OAUTH_TOKEN은 5번이고 ANTHROPIC_API_KEY는 3번입니다. 두 변수가 같이 있으면 여전히 API 키가 이깁니다. cron에 OAuth 토큰을 넣었는데도 Console 요금이 계속 나온다면 그 cron 환경에 API 키가 같이 들어 있는 것입니다. 한 가지 더, --bare 플래그를 쓰는 스크립트는 CLAUDE_CODE_OAUTH_TOKEN을 읽지 않습니다. 그 경우는 API 키나 apiKeyHelper만 쓸 수 있으니, 구독으로 돌리려면 --bare를 빼야 합니다.
Claude Desktop과 웹 세션은 이 변수를 읽지 않는다
서버를 정리하고 나면 노트북의 Claude Desktop 앱도 같은 방식으로 새는지 궁금해집니다. 그렇지 않습니다. 2026년 9월 기준 문서는 이렇게 말합니다. "Claude Desktop and cloud sessions do not call apiKeyHelper or read these environment variables: they use OAuth". Desktop 앱과 클라우드 세션은 apiKeyHelper를 실행하지 않고 이 환경 변수들도 읽지 않으며, OAuth, 즉 claude.ai 로그인을 씁니다. 예외는 하나뿐입니다. Desktop에서 서드파티 추론 구성(third-party inference configuration)을 켠 세션은 그 구성의 자격 증명으로 인증합니다. 그런 구성을 직접 설정한 적이 없다면 해당 사항이 아닙니다.
클라우드 세션은 더 단순합니다. "If you set ANTHROPIC_API_KEY or ANTHROPIC_AUTH_TOKEN in the cloud environment, it doesn't override your subscription credentials." 클라우드 환경에 키를 넣어도 구독 자격 증명을 덮어쓰지 못합니다. 그러니 API 요금이 나온 출처는 CLI와 CLI를 감싸는 것들 중 하나입니다. 문서는 터미널의 claude, VS Code 확장, Agent SDK, GitHub Actions 네 가지를 apiKeyHelper와 두 환경 변수가 적용되는 범위로 명시합니다.
요금은 어디서 확인하고 어디서 막나
새어 나간 금액은 platform.claude.com의 사용량 페이지에서 날짜별로 확인할 수 있습니다. 같은 작업량을 구독으로 했을 때와 비교하고 싶다면 API 종량제와 구독 요금이 실제 사용량에서 어떻게 갈리는지를 보면 됩니다. Claude Code는 매 턴 컨텍스트 전체를 다시 보내므로 토큰이 어디서 그렇게 많이 나가는지를 알고 나면 종량제 청구액이 왜 빨리 불어나는지도 이해됩니다. Max를 이미 내고 있다면 API로 이중 지출할 이유가 거의 없습니다.
이미 발생한 청구는 되돌릴 수 없지만 재발은 막을 수 있습니다. 더 이상 쓰지 않는 키라면 Console에서 그 키를 폐기하는 것이 어떤 환경 변수 정리보다 확실합니다. 폐기된 키는 어디에 남아 있든 인증에 실패하고, 그 실패가 invalid API key 오류가 났을 때 확인할 것들로 이어지니 오히려 남은 출처를 찾기 쉬워집니다. 문서도 같은 경고를 합니다. 비활성화되거나 만료된 조직의 키가 환경에 남아 있으면 구독이 있어도 인증 실패가 납니다.
구독 한도가 부족해서 API를 병행하려던 경우라면 방향이 다릅니다. 그때는 키를 심을 것이 아니라 Max 5x와 Max 20x 중 어느 쪽이 내 작업량에 맞는지를 먼저 따지는 편이 쌉니다. 한국에서 결제할 때 카드에 실제로 찍히는 금액은 한국에서 Claude 요금제에 실제로 내는 금액에 정리되어 있습니다. 어느 쪽을 고르든, 환경 변수 하나가 그 계산을 무효로 만들지 않도록 env | grep -i anthropic 한 줄을 서버 세팅 체크리스트에 넣어 두는 것이 가장 싼 보험입니다.
FAQ
Max 구독 중인데 Claude Code가 왜 API 키로 과금하나요?
환경에 ANTHROPIC_API_KEY가 있기 때문입니다. Claude Code는 자격 증명을 정해진 순서로 고르고, 환경 변수의 API 키는 /login으로 저장한 구독 로그인보다 앞에 있습니다. 대화형 세션에서는 첫 실행 때 한 번 승인을 묻고 그 선택을 기억하며, -p 모드에서는 묻지 않고 키가 있으면 항상 씁니다. unset ANTHROPIC_API_KEY 뒤에 /status로 어느 인증이 사용 중인지 확인하면 됩니다.
unset을 했는데 다시 접속하면 또 키 승인을 묻습니다. 왜 그런가요?
unset은 현재 셸에만 적용되고, 새 셸은 시작 파일을 다시 읽어 키를 export 하기 때문입니다. grep -n ANTHROPIC ~/.bashrc ~/.profile ~/.bash_profile ~/.zshrc로 줄을 찾아 지웁니다. 그다음 프로젝트의 .envrc, 설정 파일의 env 블록, apiKeyHelper 항목, tmux 서버의 전역 환경까지 네 곳을 더 확인합니다. tmux는 tmux show-environment -g | grep -i anthropic으로 봅니다. 설정 파일의 env 블록은 셸 값보다 우선하고, 지운 뒤 다음 실행부터 반영됩니다.
API 키는 그대로 두고 Claude Code만 구독으로 쓸 수 있나요?
대화형 세션이라면 가능합니다. /config의 "Use custom API key" 토글을 끄면 키가 환경에 있어도 구독 로그인을 씁니다. 이 토글은 변수가 설정된 동안에만 보입니다. -p 모드는 이 토글과 무관하게 키를 항상 쓰므로, 스크립트에서는 env -u ANTHROPIC_API_KEY claude -p ...처럼 그 호출에서만 변수를 빼고, 구독 자격 증명은 claude setup-token으로 만든 CLAUDE_CODE_OAUTH_TOKEN으로 넣습니다.
Claude Desktop 앱도 ANTHROPIC_API_KEY를 읽나요?
읽지 않습니다. 공식 문서는 Claude Desktop과 클라우드 세션이 apiKeyHelper를 실행하지 않고 이 환경 변수들도 읽지 않으며 OAuth 로그인을 쓴다고 명시합니다. 유일한 예외는 Desktop에서 서드파티 추론 구성을 직접 켠 세션이고, 그 세션은 해당 구성의 자격 증명으로 인증합니다. 환경 변수와 apiKeyHelper가 적용되는 범위는 CLI, VS Code 확장, Agent SDK, GitHub Actions입니다.