Anthropic SDK для Python: base_url, ключ и первый запрос через шлюз
Официальный пакет anthropic для Python нужно только направить на другой адрес. Разбираем base_url, ключ, стриминг, подсчёт токенов, кэш и ошибки.
5 мин · · разработка · API-ключи
Пакет anthropic — официальный клиент для Claude на Python, и чтобы работать с ним через шлюз, не нужно ничего переписывать: достаточно сменить адрес и ключ. Главная ловушка здесь одна: у Anthropic SDK base_url пишется без /v1, а у OpenAI SDK с /v1. На ней спотыкаются чаще всего. Ниже — рабочий код для Zukko: клиент, обычный и потоковый запрос, подсчёт токенов, кэш, обработка ошибок и таблица отличий от OpenAI SDK. Сам пакет и его возможности описаны в репозитории anthropics/anthropic-sdk-python и в документации Anthropic, а этот текст про подключение.
Anthropic python base url: установка и клиент
Установка стандартная. Документация SDK указывает, что нужен Python 3.10 или новее:
pip install anthropicЕсли вы обновляетесь с версии 0.x на 1.x, в репозитории есть руководство по миграции с перечнем несовместимых изменений. Сверьтесь с ним, прежде чем править старый код.
Создайте клиент и задайте адрес шлюза:
import os
from anthropic import Anthropic
client = Anthropic(
base_url="https://api.zukko.pro",
api_key=os.environ["ZUKKO_API_KEY"],
)
message = client.messages.create(
model="claude-sonnet-5-5",
max_tokens=1024,
messages=[{"role": "user", "content": "Объясни, что делает декоратор в Python"}],
)
for block in message.content:
if block.type == "text":
print(block.text)Ключ fetch_... берётся в кабинете и кладётся в переменную ZUKKO_API_KEY. Передавайте его в api_key явно: тогда SDK не подставит случайно переменную окружения со старым официальным ключом. SDK шлёт ключ в заголовке x-api-key, шлюз принимает его наравне с Authorization: Bearer.
Почему без /v1
SDK сам достраивает путь /v1/messages. Если написать base_url="https://api.zukko.pro/v1", запрос уйдёт на /v1/v1/messages и вернётся 404. Это зеркальная ошибка к OpenAI SDK, где адрес должен заканчиваться на /v1. Общее правило для клиентов описано в справке по Python.
Адрес через переменные окружения
В документации SDK указано, что адрес можно задать переменной ANTHROPIC_BASE_URL. Удобно, когда один и тот же код работает с разными окружениями:
export ANTHROPIC_BASE_URL=https://api.zukko.pro
export ZUKKO_API_KEY=fetch_ваш_ключЕсли вы вместо api_key предпочитаете схему с заголовком Authorization: Bearer, у SDK есть параметр auth_token и одноимённая переменная окружения. Точные имена проверьте в README вашей версии пакета.
Чем Anthropic SDK отличается от OpenAI SDK
anthropic | openai | |
|---|---|---|
base_url | https://api.zukko.pro | https://api.zukko.pro/v1 |
| Вызов | client.messages.create(...) | client.chat.completions.create(...) |
max_tokens | обязательный | необязательный |
| Системная инструкция | параметр system | реплика с ролью system |
| Текст ответа | message.content[0].text | response.choices[0].message.content |
| Токены | usage.input_tokens, usage.output_tokens | usage.prompt_tokens, usage.completion_tokens |
| Кэш Claude | виден, есть скидка | весь вход по цене входа |
Последняя строка таблицы существенна для бюджета: скидку за кэш у Claude шлюз применяет только в Anthropic-формате, потому что именно там видны чтение и запись кэша. Если ваш код ходит к Claude через openai, перенос на anthropic может заметно снизить счёт на повторяющихся контекстах. Подробнее в статье о кэше.
Стриминг
Для потока в Python удобнее помощник messages.stream, он собирает текст и итоговое сообщение:
with client.messages.stream(
model="claude-sonnet-5-5",
max_tokens=1024,
messages=[{"role": "user", "content": "Напиши функцию слияния двух отсортированных списков"}],
) as stream:
for text in stream.text_stream:
print(text, end="", flush=True)
final = stream.get_final_message()
print()
print(final.usage)Есть и вариант messages.create(..., stream=True): он отдаёт сырые события, а сборкой текста вам придётся заниматься самим.
Подсчёт токенов до запроса
count = client.messages.count_tokens(
model="claude-sonnet-5-5",
messages=[{"role": "user", "content": "Привет, мир"}],
)
print(count.input_tokens)Метод ходит на POST /v1/messages/count_tokens, подробности есть в справке по count tokens в документации Zukko. Он полезен, чтобы оценить стоимость длинного промпта заранее.
Кэш промптов с проверкой по usage
Если в каждом запросе повторяется большой префикс (инструкция, документация, правила), пометьте его cache_control:
LONG_TEXT = open("rules.md", encoding="utf-8").read()
resp = client.messages.create(
model="claude-sonnet-5-5",
max_tokens=500,
system=[
{"type": "text", "text": LONG_TEXT, "cache_control": {"type": "ephemeral"}},
],
messages=[{"role": "user", "content": "Проверь, не нарушает ли этот абзац правила"}],
)
u = resp.usage
print(u.input_tokens, u.cache_creation_input_tokens, u.cache_read_input_tokens, u.output_tokens)В первом запросе заполнится cache_creation_input_tokens, в последующих cache_read_input_tokens. Расчёт по формуле из справки: свежий вход × цена входа + чтение кэша × цена чтения + запись кэша × цена записи + выход × цена выхода. Пример на claude-sonnet-5-5 (вход 0.4, чтение 0.05, запись 0.5, $ за 1M токенов, на 18 октября 2026) для префикса в 20 000 токенов и 50 запросов, если пауза между ними короче срока жизни кэша:
- без кэша: 50 × 20 000 × 0.4 / 1 000 000 = $0.40;
- с кэшем: запись 20 000 × 0.5 / 1 000 000 = $0.01, плюс 49 чтений 49 × 20 000 × 0.05 / 1 000 000 = $0.049, итого $0.059.
Учтены только токены префикса, реплики пользователя и ответы считаются отдельно. У Anthropic есть минимальный размер префикса, короче которого кэш не включается, и срок жизни кэша: уточняйте их в документации Anthropic, а фактический результат проверяйте по полям usage и списаниям в кабинете.
Ошибки и повторы
Исключения в SDK соответствуют статус-кодам:
import anthropic
try:
client.messages.create(
model="claude-sonnet-5-5",
max_tokens=100,
messages=[{"role": "user", "content": "Привет"}],
)
except anthropic.AuthenticationError:
print("401: проверьте ключ")
except anthropic.NotFoundError:
print("404: неизвестная модель или лишний /v1 в base_url")
except anthropic.RateLimitError:
print("429: лимит ключа или ограничение провайдера")
except anthropic.APIStatusError as e:
print(e.status_code, e.response.text)В SDK 400 даёт BadRequestError, 401 AuthenticationError, 403 PermissionDeniedError, 404 NotFoundError, 413 RequestTooLargeError, 429 RateLimitError, 500 и выше InternalServerError, а ошибка соединения APIConnectionError. Код 402 (не хватает баланса) отдельного класса не имеет и ловится через общий APIStatusError. Ответ 503 с model_disabled (модель выключена) придёт как InternalServerError, и SDK по умолчанию повторит его: повторы тут не помогут, смените модель на одну из списка GET /v1/models.
По умолчанию SDK повторяет запрос дважды на ошибках соединения, 408, 409, 429 и 5xx. Параметр max_retries меняет это. Для 429 из-за дневного лимита ключа повторы не помогут: лимит нужно поднять в кабинете. Для отладки у каждого ответа есть _request_id, а заголовки доступны через client.messages.with_raw_response.create(...).
Асинхронный клиент
Для пакетной обработки есть AsyncAnthropic с тем же интерфейсом:
import asyncio
from anthropic import AsyncAnthropic
client = AsyncAnthropic(base_url="https://api.zukko.pro", api_key="fetch_ваш_ключ")
async def main():
msg = await client.messages.create(
model="claude-sonnet-5-5",
max_tokens=200,
messages=[{"role": "user", "content": "Скажи привет"}],
)
print(msg.content[0].text)
asyncio.run(main())Если запросов тысячи, посмотрите разбор асинхронной обработки. Создайте ключ в кабинете, пополните баланс от $1 (СБП с комиссией платёжной страницы 8,5% сверху, MasterCard или криптовалюта) и запустите первый пример.
Частые вопросы
Почему я получаю 404 от шлюза?
Чаще всего в base_url указан /v1. Для пакета anthropic адрес должен быть https://api.zukko.pro. Вторая причина — опечатка в имени модели; список имён даёт GET /v1/models.
Можно ли вызывать через anthropic не только Claude?
Шлюз позволяет звать любую модель из своего списка в любом формате, но поведение конкретных полей запроса (например, инструментов или рассуждений) у других поставщиков может отличаться. Сначала проверьте на простом запросе.
Нужен ли заголовок anthropic-version?
SDK ставит его сам. Если вы пишете запросы вручную через requests, добавьте anthropic-version: 2023-06-01.
Как переключиться с OpenAI SDK, не меняя весь код?
Перенесите вызов на messages.create, вынесите системную реплику в system, добавьте max_tokens и поправьте разбор ответа. Остальное в приложении можно оставить как есть.
zukko
Платите в 25×
меньше
за Claude Code & ChatGPT
Один ключ fetch_* и base URL api.zukko.pro — в Cursor, Claude Code, Codex и любом SDK.
Похожие статьи
- Свой прокси к нейросети на Node.js: не показывать API-ключ во фронтендеНебольшой сервер на Express между вашим фронтендом и API нейросети: ключ остаётся на сервере, а модели, длина запроса и частота ограничены.
- DeepSeek API на Python: запросы, стриминг, JSON и обработка ошибокГотовый код для DeepSeek V4.1 Flash и V4 Pro на OpenAI SDK: запрос, стриминг, разбор JSON и аккуратная обработка ошибок.
- Gemini API на Python: запросы через OpenAI SDK и OpenAI-форматРабочий пример Gemini API на Python без отдельной библиотеки: меняем base_url в OpenAI SDK, добавляем стриминг и JSON и сравниваем с google-genai.
- Безопасность Claude Code: чек-лист перед запуском агента в проектеАгент читает файлы и запускает команды, поэтому настройки безопасности нужны до первого запуска. Чек-лист: разрешения, секреты, песочница, отдельный ключ с лимитом.