Оптимизация расходовИнструкция
Расходы на API по проектам и алерты бюджета на Python
Короткий скрипт на Python превращает лог запросов в отчёт «сколько стоит каждый проект» и предупреждает на 50, 75, 95 и 100% месячного бюджета.

Содержание
После каждого вызова API дописывайте в файл одну JSON-строку с именем проекта — а скрипт на 30 строк Python превратит файл в отчёт «сколько стоит каждый проект» и предупредит на 50, 75, 95 и 100% месячного бюджета. Это тот же обзор, который Anthropic только что дала командам Claude Enterprise, только собранный из вашего собственного лога.
Допустим, с одного баланса питаются агент, скрипт для черновиков блога и небольшой бот поддержки. Конец месяца, баланс ниже, чем хотелось бы, и непонятно, кто из троих постарался. Вот эту дыру мы и закрываем.
Что выпустила Anthropic и что можно повторить#
В анонсе Anthropic (28 октября 2026) — умные отчёты, опросы внутри приложения и общие групповые бюджеты; доступны для планов Claude Enterprise, только для них (источник). Для нас важны две вещи. Отчёт группирует работу команды в «потоки» и показывает расходы каждого. Общий бюджет — одна месячная сумма на группу, а администраторы биллинга получают письмо на 50%, 75%, 95% и 100% от неё. Anthropic добавляет: отчёты нужны, чтобы планировать вложения, а не оценивать отдельных людей.
Энтерпрайз-план для такой же схемы не нужен. «Поток» — это метка, которую выбираете вы, а четыре порога — один список в коде. Группируйте по проектам, а не по людям — по той же причине, что называет Anthropic.
Быстрая проверка
Предоплаченный баланс и так остановит запросы на нуле. Зачем предупреждения на 50% и 75%?
Показать ответ
Ответ: Потому что ноль — слишком поздно: зациклившийся агент заметен, пока ещё есть деньги, чтобы разобраться
Баланс — жёсткий стоп, а не сигнализация. Ранние пороги превращают сюрприз на 100% в вопрос на девятый день месяца.
Лог: одна строка на запрос#
В каждом ответе chat completions есть объект usage с полями prompt_tokens и completion_. Для отчёта больше ничего не нужно — только метка проекта, которую вы добавляете сами. Эта обёртка отправляет запрос и дописывает строку в файл текущего месяца (имя файла меняется каждый месяц, так что новый месяц начинается с нуля):
import json, os, time
from openai import OpenAI
client = OpenAI(base_url="https://api.cheaptokens.dev/v1",
api_key=os.environ["CHEAPTOKENS_API_KEY"])
def ask(project, model, messages):
r = client.chat.completions.create(model=model, messages=messages)
with open(time.strftime("usage-%Y-%m.jsonl"), "a") as f:
f.write(json.dumps({"project": project, "model": model,
"prompt_tokens": r.usage.prompt_tokens,
"completion_tokens": r.usage.completion_tokens}) + "\n")
return r
ask("support-bot", "zai-org/GLM-5.3-Flash",
[{"role": "user", "content": "Where is my order?"}])Полный id модели берите из документации CheapTokens (deepseek-, zai-): короткие названия из таблиц цен API не принимает. Ключ — из переменной окружения, не из файла.

Скрипт: стоимость по проектам и пороги#
Сохраните как report.py. Цены в нём — заглушки: подставьте актуальные из таблицы цен (у двух моделей здесь одна условная цена — только чтобы арифметика читалась).
import json, os, sys, time
from collections import defaultdict
LOG = time.strftime("usage-%Y-%m.jsonl")
BUDGET = float(os.environ.get("BUDGET_USD", "10")) # example, substitute your own
STATE = LOG + ".alert"
LEVELS = [50, 75, 95, 100]
PRICES = { # USD per 1M tokens (input, output): example, substitute current prices
"deepseek-ai/DeepSeek-V4-Flash-0731": (1.0, 2.0),
"zai-org/GLM-5.3-Flash": (1.0, 2.0),
}
spend = defaultdict(float)
with open(LOG) as f:
for line in f:
r = json.loads(line)
p_in, p_out = PRICES[r["model"]]
spend[r["project"]] += (r["prompt_tokens"] * p_in
+ r["completion_tokens"] * p_out) / 1_000_000
total = sum(spend.values())
if total == 0:
sys.exit("log is empty")
for name, usd in sorted(spend.items(), key=lambda kv: -kv[1]):
print(f"{name:<8} ${usd:6.2f} {usd / total:5.0%}")
print(f"{'total':<8} ${total:6.2f} of ${BUDGET:.2f} ({total / BUDGET:.0%})")
last = int(open(STATE).read()) if os.path.exists(STATE) else 0
crossed = [l for l in LEVELS if total >= BUDGET * l / 100 and l > last]
if crossed:
print(f"WARNING: budget passed {crossed[-1]}%")
open(STATE, "w").write(str(crossed[-1]))Последние четыре строки — то, о чём забывают. Без маленького файла состояния скрипт кричал бы «passed 50%» при каждом запуске до конца месяца. С ним каждый порог срабатывает один раз.
Запускаем и читаем результат#
Возьмём учебный лог из четырёх строк (настоящий — по строке на запрос, то есть тысячи):
{"project": "agent", "model": "deepseek-ai/DeepSeek-V4-Flash-0731", "prompt_tokens": 2400000, "completion_tokens": 300000}
{"project": "agent", "model": "deepseek-ai/DeepSeek-V4-Flash-0731", "prompt_tokens": 1000000, "completion_tokens": 100000}
{"project": "blog", "model": "zai-org/GLM-5.3-Flash", "prompt_tokens": 800000, "completion_tokens": 400000}
{"project": "bot", "model": "zai-org/GLM-5.3-Flash", "prompt_tokens": 500000, "completion_tokens": 100000}С условными ценами и бюджетом 10 команда python report.py выводит:
agent $ 4.20 65%
blog $ 1.60 25%
bot $ 0.70 11%
total $ 6.50 of $10.00 (65%)
WARNING: budget passed 50%Агент — почти две трети счёта, и предупреждение пришло на 50%, а не в конце. Запустите снова — до 75% скрипт промолчит.

Чтобы это работало каждый месяц, нужны три шага:
Шаг за шагом
Подключаем за три шага
Пишите лог на каждый вызов
Пропустите все вызовы через ask(project, model, messages) и выберите короткие неизменные метки проектов. Переименование посреди месяца разрежет один проект на две строки.
Укажите цены и бюджет
Впишите актуальные цены из таблицы в PRICES и задайте лимит: export BUDGET_.
Запускайте по расписанию
Хватит cron раз в час: 0 * * * * cd ~/. Выведите результат в почту или чат, если хотите, чтобы предупреждение приходило само.
Как выглядит месяц на дешёвых моделях#
Цифры полезны, когда они ваши. Допустим, агент из учебного лога в среднем тратит 30 000 входных и 2 000 выходных токенов на шаг, 20 шагов на задачу, две задачи в день. Один шаг на DeepSeek-V4-Flash стоит $0.00065, на GLM-5.3-Flash — $0.00068. В день это 40 шагов, в месяц 1 200. Подвиньте ползунки под своего агента:
Калькулятор стоимости
Один день агента: шагов на задачу × задач в день
Входные токены на запрос: 30 000 · Выходные токены на запрос: 2 000 · Запросов в день: 40
| Модель | В день | В месяц |
|---|---|---|
| GLM-5.3-Flash$0.02$0.04 | $0.0272 | $0.816 |
| DeepSeek-V4-Flash$0.015$0.10 | $0.026 | $0.78 |
| MiniMax-M2.7$0.05$0.15 | $0.072 | $2.16 |
А месяц при 1 200 шагах, вход и выход вместе:
| Модель | Вход $/1M | Выход $/1M | За запрос | 1 200 запросов | OpenRouter, 1 200 запросов |
|---|---|---|---|---|---|
| GLM-5.3-Flash | $0.02 | $0.04 | $0.00068 | $0.816 | $1.41 |
| DeepSeek-V4-Flash | $0.015 | $0.10 | $0.00065 | $0.78 | $2.86 |
| MiniMax-M2.7 | $0.05 | $0.15 | $0.0018 | $2.16 | $25.92 |
30 000 входных и 2 000 выходных токенов на запрос. Цены за 1M токенов актуальны и меняются вместе с нашими тарифами; столбец OpenRouter показывает справочные цены для той же нагрузки.
В сумме, с учётом входа и выхода, обе модели в этой таблице выходят дешевле референсных цен OpenRouter — с одной ценой в любое время суток и без пиковых надбавок. У DeepSeek-V4-Flash ещё и до 32 768 токенов в ответе и контекст 380K; у GLM-5.3-Flash контекст 390K. Когда отчёт покажет, какой проект тяжёлый, перейти только для него — это замена одной строки model, а баланс один на все модели. Сколько токенов уходит на один запрос, разобрано в статье сколько токенов использует запрос к API; как остановить один запуск агента посреди работы — в статье учёт токенов агента. Цены и модели — на странице моделей CheapTokens.
Где это ломается#
- Модели нет в
PRICES. Скрипт остановится сKeyError, где названа модель. Добавьте её; не подставляйте ноль, иначе проект будет выглядеть бесплатным. - Стриминг. Usage приходит в последнем чанке, только если его попросить (
stream_в OpenAI SDK). Иначе в логе будут дыры.options={"include_ usage": True} - Неудавшиеся запросы. Логируйте только ответы с объектом
usage: у запроса, завершившегося ошибкой, считать нечего. - Отчёты по людям. Держите метку на уровне проекта или агента. Отчёт про людей провоцирует неверные выводы.



