Как считать стоимость запросов к Claude и не разориться
Первый счёт от Anthropic, который я получила, был на 380 долларов за десять дней теста на маленьком пет-проекте. И это с моделями Sonnet, не Opus. После этого я пересобрала весь учёт стоимости и сделала так, чтобы цифра в конце месяца не была сюрпризом. Расскажу, как.
Главная мысль: токены — это не «маленькие копейки». Токен на входе и токен на выходе стоят разное, кэшированный токен — третье, а если ты гоняешь длинный системный промпт без кэша, счёт растёт линейно от числа запросов.
Из чего складывается счёт
У Claude API три типа токенов в счёте:
- Входные токены — то, что ты отправляешь модели. Сюда входит system, messages и tools.
- Выходные токены — то, что модель пишет в ответе.
- Кэш-операции — отдельно запись в кэш и отдельно чтение из кэша. Чтение в разы дешевле обычного входа, запись чуть дороже.
Цены легко найти на anthropic.com/pricing. Я не буду их перепечатывать, потому что они меняются. Важнее понимать механику.
Считаем токены до отправки
Anthropic SDK даёт удобный метод countTokens. Им можно посчитать токены любого сообщения без вызова модели — это бесплатно.
import Anthropic from '@anthropic-ai/sdk';
const client = new Anthropic();
const tokens = await client.messages.countTokens({
model: 'claude-sonnet-4-5',
system: SYSTEM_PROMPT,
messages: [
{ role: 'user', content: userText }
]
});
console.log(tokens.input_tokens);Я гоняю countTokens на каждый запрос пользователя в фоне и складываю в Postgres. Это даёт мне реальную картину: сколько в среднем стоит один пользовательский запрос, какие пользователи дороже остальных, где у меня раздутые промпты.
Учёт по факту
В ответе messages.create есть поле usage:
const response = await client.messages.create({ ... });
const { usage } = response;
// {
// input_tokens: 1320,
// output_tokens: 415,
// cache_creation_input_tokens: 8400,
// cache_read_input_tokens: 0
// }Я кладу это в таблицу llm_usage с колонками model, user_id, feature, input_tokens, output_tokens, cache_read_tokens, cache_creation_tokens, created_at. Раз в час Cron-job считает стоимость по актуальным ценам и пишет в llm_costs.
Бюджет на пользователя
Главная защита от взрыва счёта — лимит на пользователя в день. У меня в проекте у каждого юзера есть mtu_per_day (millions of tokens used per day). Перед каждым запросом я проверяю текущее потребление и, если близко к лимиту, либо переключаюсь на Haiku, либо отказываю. Лимит подстраивается под план подписки.
const usedToday = await getTodayUsage(userId);
if (usedToday.input_tokens > user.dailyInputLimit) {
return sendError('LIMIT_REACHED');
}
if (usedToday.input_tokens > user.dailyInputLimit * 0.7) {
// переключаемся на дешёвую модель
modelToUse = 'claude-haiku-4';
}Где деньги утекают чаще всего
На своём проекте я нашла четыре типичные дыры:
- Большой system-промпт, который не закэширован. Если у тебя system на 8k токенов и 1000 запросов в день — это уже миллионы лишних входных токенов.
- Передача всей истории чата без обрезки. У меня после 30 сообщений в одном диалоге вход рос до 30k токенов.
- Tool definitions раздуты. Описание 12 инструментов с подробными schemas — ещё 4–6k токенов на каждый запрос.
- Streaming с длинными max_tokens. Если ты не остановишь модель явно, она может ответить на 4k токенов вместо нужных 200.
Кэширование — must have
В прошлой статье у нас Артур уже писал про кэш. Я добавлю практическую цифру: на нашем проекте после правильно настроенного кэша счёт упал почти в три раза при том же объёме запросов. Кэшируем system и tools, иногда — длинный документ из RAG.
await client.messages.create({
model: 'claude-sonnet-4-5',
system: [
{ type: 'text', text: SYSTEM_PROMPT, cache_control: { type: 'ephemeral' } }
],
tools: TOOLS.map(t => ({ ...t, cache_control: { type: 'ephemeral' } })),
messages
});Алерты
Я держу два простых алерта в Grafana: суточный расход в долларах и среднее число выходных токенов на запрос. Когда среднее растёт — значит, у моделей вдруг стало больше «болтливости», и пора посмотреть промпты или max_tokens. Когда расход скачет — лезть в логи и смотреть, какой пользователь и какая фича разогнались.
Прогноз
Раз в неделю я строю простой прогноз: «если последние 7 дней — нормальный темп, в конце месяца счёт будет такой». Делается это сложением дневных трат за последние 7 дней, делением на 7 и умножением на оставшиеся дни месяца. Грубо, но в моей реальности отклоняется от факта на 5–10%, и этого хватает.
Что забрать с собой
Стоимость LLM не нужно считать «потом», когда в Stripe прилетит чек. Считать нужно по каждому запросу: сколько токенов на входе, сколько на выходе, что закэшировано. Эти три числа пишутся в свою таблицу, и дальше можно строить любую аналитику. Лимиты на пользователя, переключение на дешёвую модель и алерты — три вещи, которые спасают, когда что-то идёт не так. И самое скучное, но самое полезное — перед каждым новым промптом задавай себе вопрос «а сколько это стоит» и считай через countTokens, не глядя в счёт постфактум.