MAATRIX / Блог / Стоимость одного API-запроса: методика расчёта от счёта до строки кода

Стоимость одного API-запроса: методика расчёта от счёта до строки кода

MAATRIX

Себестоимость пользователя и себестоимость заказа — метрики, которые уже дают бизнесу ориентир, сколько стоит инфраструктура в пересчёте на что-то осмысленное. Но обе они усредняют десятки и сотни операций внутри одной сессии или одного заказа. Если хочется понять, где именно в коде уходят инфраструктурные деньги, нужна единица измерения на порядок мельче — один API-запрос. Это последний, самый детальный уровень unit-экономики инфраструктуры: дальше только строка кода.

Зачем спускаться на уровень одного запроса

Себестоимость пользователя в месяц отвечает на вопрос «сколько нам стоит клиентская база». Себестоимость заказа — «сколько нам стоит один акт продажи». Оба показателя полезны для финансового планирования и ценообразования, но бесполезны для разработчика, который открывает профайлер и не понимает, с какого эндпоинта начинать оптимизацию.

Стоимость одного API-запроса — это мостик между бухгалтерией и кодом. Она переводит абстрактный счёт за облако или аренду серверов в конкретную величину, которую можно приписать конечной точке API: GET /orders/{id} стоит X, POST /search стоит Y. Как только у вас есть эти X и Y, разговор с командой разработки меняется — вместо «нам нужно оптимизировать бэкенд» появляется «нам нужно оптимизировать вот этот метод поиска, он в пересчёте на запрос обходится в несколько раз дороже соседних».

Здесь же встаёт естественный вопрос: а что было до профилирования кода? Обычно — растущий безадресно счёт, который вырос вдвое без роста нагрузки, и никто не может объяснить почему. Методика ниже как раз даёт способ найти ответ, а не гадать.

Шаг первый: суммарный счёт и суммарное число запросов

Базовая формула предельно простая:

Средняя стоимость запроса = Суммарный счёт за инфраструктуру за период / Суммарное число API-запросов за тот же период

Практические нюансы делают её не такой тривиальной, как кажется на первый взгляд.

Что входит в «суммарный счёт». Берите весь набор счетов, который обслуживает API-слой за расчётный период (обычно месяц): аренду серверов приложений, базы данных, кеш (Redis/Memcached), балансировщик нагрузки, исходящий трафик (egress), логирование и мониторинг, если они выставляются отдельной строкой, лицензии на API-шлюз (тот же APISIX или Kong, если это управляемый тариф). Не включайте расходы, которые с API не связаны напрямую — фронтенд-хостинг, статику на CDN, почтовую рассылку. Если инфраструктура общая для нескольких продуктов, разделите счёт пропорционально доле ресурсов, которую потребляет именно API-слой — это можно оценить по CPU-time или по объёму трафика через API-шлюз.

Что считать «одним запросом». Здесь нужна дисциплина. Один HTTP-вызов к вашему API — это один запрос, вне зависимости от того, сколько внутренних микросервисов он затронул. Если у вас API-шлюз (nginx, APISIX, Kong) — берите число запросов из его логов, это самый надёжный источник, потому что шлюз видит абсолютно весь внешний трафик и не зависит от того, залогировал ли что-то конкретный сервис.

# Пример: подсчёт запросов из access-логов nginx за месяц
awk '{print $4}' /var/log/nginx/access.log* | \
  grep "01/Aug/2026\|02/Aug/2026" | wc -l

Для APISIX точнее взять агрегированную метрику apisix_http_requests_total из Prometheus за период — она уже учитывает ретраи и балансировку между upstream'ами:

sum(increase(apisix_http_requests_total[30d]))

Пример расчёта. Допустим, суммарный счёт за август — сервер приложений, база данных, кеш и трафик — составил условную сумму, а шлюз за тот же месяц пропустил условное число запросов. Средняя стоимость одного запроса получается делением первого на второе. Порядок величины для типичного веб-API — доли цента до нескольких центов за запрос, но это очень грубый ориентир: у вас может отличаться на порядок в любую сторону в зависимости от того, насколько «тяжёлые» у вас операции и сколько внешних API вы дергаете внутри собственного запроса.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать сервер

Шаг второй: почему средняя цифра — это только начало

Средняя стоимость запроса — полезная метрика для трендов («в этом месяце запрос подорожал на 15% при том же трафике — надо разбираться»), но она скрывает главное: реальный API почти никогда не состоит из однородных операций.

Возьмите типичный REST-бэкенд интернет-магазина или SaaS-продукта. В нём соседствуют:

  • GET /products/{id} — получение одной записи по первичному ключу, один индексный lookup в базе, миллисекунды CPU;
  • GET /search?q=... — полнотекстовый или фасетный поиск, может задействовать Elasticsearch, агрегации, сортировку по нескольким полям;
  • POST /reports/export — генерация отчёта, который сканирует тысячи строк и держит соединение с базой секундами;
  • POST /webhooks/notify — рассылка уведомлений через внешний API, с ретраями и таймаутами;
  • GET /health — заглушка, которая почти ничего не стоит, но может дёргаться раз в несколько секунд мониторингом.

Если просто усреднить счёт на общее число запросов, /health и /reports/export получат одинаковую «стоимость», хотя по факту первый почти бесплатен, а второй может быть в сотни раз дороже. Это ровно та же проблема, что с себестоимостью пользователя: усреднение по грубой единице маскирует то, что действительно важно.

Шаг третий: разбивка по типам эндпоинтов

Чтобы увидеть картину честно, нужно распределить инфраструктурные расходы не поровну, а пропорционально тому, сколько ресурсов реально потребляет каждый тип запроса. Практический алгоритм:

  1. Сгруппируйте запросы по маршруту (route pattern, а не по конкретному URL с параметрами — /orders/{id}, а не /orders/4521).
  2. Соберите метрику потребления ресурсов на группу. Минимально нужны: среднее время обработки (response time) и число вызовов за период. Если инфраструктура это позволяет — добавьте CPU-time на запрос и число обращений к базе/внешним API на запрос.
  3. Переведите время обработки в долю ресурса. Грубая, но рабочая эвристика: доля инфраструктурных расходов эндпоинта пропорциональна (среднее время обработки × число вызовов) этого эндпоинта относительно суммы той же величины по всем эндпоинтам. Это работает, если сервис в основном CPU-bound или держит соединение с базой на всё время обработки — то есть для подавляющего большинства обычных бэкендов.
  4. Умножьте долю на суммарный счёт — получите расходы, приписанные группе эндпоинтов, а разделив на число вызовов этой группы — стоимость одного запроса именно этого типа.

Источник данных для шага 2, если у вас уже настроен мониторинг — APM-инструмент (Sentry Performance, Grafana + Prometheus с гистограммами http_request_duration_seconds с лейблом route) или access-логи со временем ответа:

# Пример: время ответа nginx с $request_time, агрегация по маршруту (упрощённо)
log_format timing '$request_method $uri $request_time';
# Группировка по маршруту и подсчёт среднего времени (awk, упрощённый пример)
awk '{route=$2; sub(/[0-9]+$/, "{id}", route); \
      sum[route]+=$3; cnt[route]++} \
     END {for (r in sum) print r, sum[r]/cnt[r], cnt[r]}' access_timing.log

Если у вас Prometheus с гистограммами по маршрутам, честнее взять сумму реального времени, а не среднее:

sum by (route) (increase(http_request_duration_seconds_sum[30d]))

Это уже прямая пропорция для распределения счёта — без промежуточного умножения среднего на количество.

Результат обычно выглядит примерно так:

МаршрутДоля от общего времени обработкиДоля от счёта (оценка)Вызовов за месяц
GET /products/{id}12%условно низкаяочень много
GET /search41%условно высокаяумеренно
POST /reports/export23%условно высокаянемного
POST /orders15%средняяумеренно
GET /health, служебные2%пренебрежимо малаочень много
прочее7%

Конкретные проценты и суммы у вас будут свои — не переносите цифры из таблицы, они здесь только чтобы показать форму результата. Смысл в другом: /search при умеренном числе вызовов может съедать почти половину инфраструктурного бюджета API, а /products/{id}, несмотря на огромное число вызовов, обходится в разы дешевле в пересчёте на единицу — потому что каждый отдельный вызов простой.

Практическая ценность: приоритизация вместо оптимизации вслепую

Главная польза от такой детализации — не красивая табличка для отчёта, а ответ на вопрос «что оптимизировать в первую очередь». Без разбивки по эндпоинтам разработчики часто оптимизируют то, что легко оптимизировать, или то, что попалось на глаза в профайлере в моменте — а не то, что реально сжигает бюджет.

Разбивка по типам запросов переворачивает порядок работы:

  • Если GET /search даёт 40% инфраструктурных расходов при 10% трафика — это кандидат номер один: индекс, кеширование частых запросов, ограничение глубины пагинации, отдельный поисковый движок вместо LIKE '%...%' по основной базе.
  • Если POST /reports/export дорогой, но вызывается редко — возможно, дешевле не оптимизировать код, а вынести генерацию в фоновую очередь с ограничением параллельности, чтобы пиковые вызовы не тянули ресурсы у остального API.
  • Если дорогой оказывается эндпоинт, который дёргает внешний API синхронно — здесь оптимизация инфраструктуры бессмысленна, проблема в архитектуре ожидания ответа, и это прямая связь с профилированием на уровне кода, а не только счетов.

Это тот момент, где unit-экономика инфраструктуры напрямую встречается с профилированием приложения на проде: счета говорят «где дорого», профайлер — «почему дорого». Одно без другого работает хуже. Стоимость по эндпоинтам показывает, куда смотреть профайлером в первую очередь, а не заставляет прогонять его по всему коду в надежде найти проблему.

Второй практический эффект — это язык для разговора с продуктовой командой. «Функция поиска стоит нам в разы дороже, чем всё остальное API вместе взятое» — аргумент, который понятен и продакт-менеджеру, и финансам, в отличие от абстрактного «CPU load вырос». Это превращает оптимизацию кода из технической инициативы в решение с понятной окупаемостью: если известно, сколько стоит эндпоинт сейчас и насколько реалистично снизить его стоимость, можно прикинуть отдачу от спринта на оптимизацию ещё до того, как он начался.

Где эта методика ломается

Честно говоря о её ограничениях: пропорциональное распределение по времени ответа — эвристика, а не точный учёт. Она искажается минимум в трёх случаях.

Асинхронные и фоновые операции. Если эндпоинт быстро отвечает клиенту, но ставит тяжёлую задачу в очередь (например, генерацию отчёта в фоне), время ответа самого HTTP-запроса не отражает реальную стоимость. Нужно отдельно учитывать расходы воркеров очереди и приписывать их эндпоинту, который поставил задачу, а не эндпоинту, который её забрал.

Разная стоимость операций записи и чтения. Запись в базу обычно дороже по I/O, чем чтение из кеша, даже при одинаковом времени ответа — время ответа может быть похожим, а нагрузка на диск отличаться в разы. Если у вас есть метрики I/O по типам запросов — используйте их вместо или вместе со временем ответа.

Холодный старт и разогрев. Serverless-функции или контейнеры с автоскейлингом искажают картину: первый запрос после простоя может стоить в разы дороже последующих из-за холодного старта. Если у вас такая архитектура, разумнее считать стоимость отдельно для «тёплых» и «холодных» вызовов, иначе редкие эндпоинты будут казаться неоправданно дорогими просто потому, что чаще попадают на холодный старт.

Ни одно из этих ограничений не отменяет метод — они просто означают, что точность зависит от того, сколько метрик вы готовы собирать. Начните с грубой оценки по времени ответа, она уже отсекает 80% неопределённости; углубляйтесь в I/O и холодные старты только для тех эндпоинтов, которые метод уже выделил как подозрительно дорогие.

Как встроить расчёт в регулярный процесс

Разовый расчёт полезен, но реальную ценность метод даёт при регулярном пересчёте — раз в месяц вместе с закрытием счетов за инфраструктуру. Минимальный процесс:

  1. В конце месяца выгрузите сумму счетов инфраструктуры, обслуживающей API.
  2. Из логов шлюза или Prometheus получите число вызовов и суммарное/среднее время обработки по каждому маршруту.
  3. Посчитайте долю каждого маршрута и стоимость на один запрос.
  4. Сравните с прошлым месяцем — рост стоимости конкретного эндпоинта без роста числа его вызовов сигнализирует о деградации кода, миграции без индекса или утечке в кеше раньше, чем это станет заметно на графике общей нагрузки.
  5. Заведите порог: если стоимость эндпоинта выросла больше, чем на условную величину (например, 30%) месяц к месяцу, это повод завести задачу на профилирование, а не ждать, пока накопится.

Такой процесс дешевле держать на предсказуемой инфраструктуре — собственных серверах или выделенных VPS с фиксированной ежемесячной стоимостью, где счёт заранее известен и не «плавает» вместе с трафиком, как это бывает на чисто облачных pay-as-you-go тарифах. Тогда основная переменная в формуле — число и структура запросов, а не сама стоимость инфраструктуры, что упрощает интерпретацию месячной динамики.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать сервер

Нужны сами нейросети для контента?

Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.

Частые вопросы

Нужен ли для этой методики специальный APM-инструмент, или достаточно логов nginx?

Логов шлюза с временем ответа достаточно для базовой версии расчёта — группировки по маршруту и оценки доли расходов. APM (Sentry Performance, Datadog, self-hosted Grafana с трейсингом) даёт более точную картину, особенно по асинхронным вызовам и запросам к внешним API внутри обработки, но это уже уточнение, а не обязательное условие для старта.

Как быть, если один и тот же сервер обслуживает несколько разных API или продуктов?

Разделяйте по CPU-time или по доле трафика через шлюз, если шлюз различает продукты по домену или префиксу пути. Если разделить физически невозможно — считайте общую стоимость запроса для всех продуктов вместе, а уже внутри неё делайте разбивку по маршрутам, как описано в статье; абсолютная точность здесь менее важна, чем относительное сравнение маршрутов между собой.

Стоит ли учитывать в счёте зарплату разработчиков и DevOps?

Нет, эта методика — про инфраструктурную часть себестоимости (серверы, база, трафик, лицензии на компоненты вроде API-шлюза), а не про полную себестоимость разработки. Смешивать их не стоит: инфраструктурная стоимость запроса нужна именно для решений про архитектуру и оптимизацию кода, а не для расчёта общей рентабельности продукта.

С какой периодичностью пересчитывать стоимость запроса — раз в месяц мало?

Для большинства продуктов месячного цикла достаточно, потому что он совпадает с циклом выставления счетов за инфраструктуру. Если у вас резкие сезонные пики нагрузки (распродажи, пиковые часы) — имеет смысл считать отдельно стоимость запроса в пиковый и внепиковый период, потому что автоскейлинг может делать эти цифры сильно разными.

Что делать, если после разбивки самым дорогим оказался эндпоинт, который трогать страшно — legacy-код без тестов?

Это нормальная и частая ситуация. Методика не обязывает оптимизировать код немедленно — она даёт приоритет. Если рефакторинг рискован, можно сначала снизить стоимость эксплуатационными средствами: кеширование результата на уровне шлюза или CDN, ограничение частоты вызовов (rate limiting), вынос тяжёлой части в фон. Это снижает инфраструктурные расходы без переписывания логики и даёт время подготовить безопасный рефакторинг отдельно.

Обсудить статью, задать вопрос или начать новую тему

Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.

Перейти в сообщество →