Читайте заголовки кредитов: аудит каждого списания из ответа
Что вы построите: тонкую обёртку клиента, которая записывает стоимость каждого вызова и остаток, чтобы ваши собственные логи стали аудиторским следом тарификации.
| Раздел | Основы API |
|---|---|
| Используемые действия | POST /v1/data/contacts/verify-email |
| Кредиты | 1 (худший случай) |
| Работает с тестовым ключом | Да — работает на qk_test_ за ноль кредитов |
| Время на выполнение | ~5 мин |
| Требования | Ключ и любой HTTP-клиент, позволяющий читать заголовки ответа. |
Три заголовка в каждом вызове данных
X-Credits-Charged — сколько стоил этот вызов. X-Credits-Balance — сколько осталось после него. X-Price-Book указывает прайс-лист, по которому рассчитано списание, поэтому оценку и списание всегда можно свести к одним и тем же правилам.
curl -i -X POST https://api.qannasapi.com/v1/data/contacts/verify-email \
-H "Authorization: Bearer $QANNAS_API_KEY" \
-H "Content-Type: application/json" \
-d '{"query": "jane@acme.example"}'
# < HTTP/1.1 200 OK
# < X-Credits-Charged: 1 # what this call cost
# < X-Credits-Balance: 94 # what is left afterwards
# < X-Price-Book: public # which rules were appliedЛогируйте списание, а не оценку
Один раз оберните HTTP-клиент и записывайте три заголовка рядом с request_id. Это даёт распределение затрат по вызовам без опроса эндпоинта использования, а request_id — именно то, что запросит поддержка, если списание покажется неверным.
export async function call(action: string, body: unknown) {
const res = await fetch(`${API}/v1/data/${action}`, {
method: "POST",
headers: { Authorization: `Bearer ${key}`, "Content-Type": "application/json" },
body: JSON.stringify(body),
});
const json = await res.json();
// Your logs become the billing audit trail.
logger.info("qannas.call", {
action,
request_id: json.request_id,
charged: Number(res.headers.get("X-Credits-Charged")),
balance: Number(res.headers.get("X-Credits-Balance")),
});
return json;
}Следите за балансом, а не за часами
Поскольку баланс возвращается с каждым вызовом, ограничивать нагрузку или поднимать тревогу можно по уже полученному ответу. Нет отдельного эндпоинта квот для опроса и нет разрыва между тратой и знанием о ней.
// The balance arrives on the call you already made —
// there is no quota endpoint to poll and no lag.
const balance = Number(res.headers.get("X-Credits-Balance"));
if (balance < LOW_WATER_MARK) {
pauseNonUrgentJobs();
alertOps(`credits low: ${balance}`);
}Итоги
| Приём | Как |
|---|---|
| Сколько стоил вызов | X-Credits-Charged |
| Сколько осталось | X-Credits-Balance |
| Какие правила применялись | X-Price-Book |
| Отследить списание | request_id в теле ответа |
| Ограничение нагрузки | Тревога по X-Credits-Balance из только что сделанного вызова |
Чек-лист перед продакшеном
- Логируйте X-Credits-Charged, X-Credits-Balance и request_id при каждом вызове.
- Поднимайте тревогу по заголовку баланса, а не опрашивайте эндпоинт использования.
- Храните request_id — по нему отслеживается конкретное списание.
- Ожидайте X-Credits-Charged: 0 при пустом результате и не считайте это сбоем.
Дальше
Сначала запустите оценку. Она ничего не стоит и использует ровно ту же арифметику, что и счётчик.