Двадцать один десять. Я вытряхнула на стол жестянку из-под чая, где лежат чеки за неделю, — привычка из той жизни, где деньги считают по бумажкам, а не по строке в кабинете. Обычно я разбираю их в воскресенье и почти все выбрасываю.

Сегодня они понадобились не для этого. Мне нужна была вещь, на которой написана одна цифра. Весь вечер я читала документацию, где одна и та же просьба стоит то девятнадцать центов, то девяносто долларов, и хотелось подержать в руках хоть что-нибудь, что не меняется задним числом.

Один чек лежал отдельно, с временем 00:12. Я переложила его лицом вниз и не стала вспоминать, за чем ходила.

Вторая модель в списке — не аварийная копия, а условие

У OpenRouter подмена моделей — не костыль, приделанный сбоку, а параметр запроса. Вместо одного идентификатора передаётся массив в порядке приоритета: «if the first model returns an error, OpenRouter will automatically try the next model in the list». Ошибкой считается не только недоступность провайдера. В том же абзаце перечислены четыре повода: контекст не прошёл проверку длины, модерация отклонила ответ, лимит запросов, простой.

Два повода из четырёх — не про аварию. Слишком длинный контекст и модерационный отказ принадлежат самому запросу: они повторятся на следующем вызове с тем же текстом и на следующем после него. Значит, основная модель может не участвовать в работе вовсе — не потому, что упала, а потому, что её вход ей не подходит, — и ни одной красной строки в логах не появится. Красная строка появляется, когда сломались все. А когда отвечает вторая, это выглядит как успех.

Счёт выставляют по ответившей, и это единственная квитанция

В разделе о цене одна фраза: «Requests are priced using the model that was ultimately used, which will be returned in the model attribute of the response body». То есть в теле ответа есть поле, где написано, кто на самом деле ответил, и по нему же выставляют счёт. Дальше — уточнение для случая, который делают почти все: если в начале списка стояла бесплатная модель, а переход случился на платную, платят по цене платной. «To keep a request free, list only :free models».

Здесь я признаюсь в ожидании, которое не подтвердилось. Я думала, что неудачная попытка тоже стоит денег: модель начала отвечать и отказала на середине — токены уже улетели. В описании этого нет, там сказано только про модель, которая ответила. Своего счёта у меня нет, проверить не могу, поэтому оставляю это как прочитанное, а не как проверенное.

Девяносто четыре и семь десятых раза

Дальше арифметика, и она моя. Я взяла цены из публичного списка моделей OpenRouter — того, что отдаётся по `api/v1/models`, — на второе ноября. Единица у них одна: доллары за миллион токенов, вход и выход считаются отдельно. Беру миллион токенов входа и миллион выхода. Claude Sonnet 4.5 — 3,00 плюс 15,00, итого 18,00. Claude Opus 4.1 — 15,00 плюс 75,00, итого 90,00. Модель из примера в той же документации, gryphe/mythomax-l2-13b, — 0,08 плюс 0,11, итого 0,19.

В примере список устроен так: сначала Claude Sonnet, вторым номером mythomax. Если переход случился, вы получили ответ за девятнадцать центов вместо восемнадцати долларов — в девяносто четыре и семь десятых раза дешевле. Это не «резерв подешевле», это другая модель: у неё 8 192 токена контекста против миллиона и, очень вероятно, другой ответ на тот же вопрос.

Обратный случай тоже в той же таблице. У Sonnet 4.5 есть надбавка за вход длиннее 200 тысяч токенов: 6,00 за миллион входа и 22,50 за выход, то есть 28,50 за тот же объём вместо 18,00. Подмена вверх по цене — в пять раз, и заметить её можно ровно тем же способом, что и подмену вниз.

Инфографика: стоимость миллиона токенов входа и миллиона токенов выхода в долларах — gryphe/mythomax 0,19; OpenAI GPT-5 mini 2,25; Gemini 2.5 Flash 2,80; Claude Sonnet 4.5 18,00; Sonnet 4.5 при входе длиннее 200 тысяч токенов 28,50; Claude Opus 4.1 90,00
Цены — из публичного списка моделей OpenRouter, прочитанного 2 ноября 2026. Суммы за миллион токенов входа и миллион выхода посчитаны мной: цена входа плюс цена выхода.

Три записи, и ни одной своей

У Anthropic-совместимой ручки `/api/v1/messages` та же подмена называется `fallbacks`, и там ограничений больше. Каждая запись принимает только поле `model`: свои `max_tokens`, `thinking`, `speed` или `output_config` на попытку отклоняются с ошибкой 400. Список `fallbacks` нельзя совмещать с массивом `models` — тоже 400. И записей не больше трёх; четвёртая — снова 400.

Вывод скучный и важный: потолок цены нельзя поставить на попытку. Потолок — это сам список. Если вторым номером стоит модель в пять раз дороже, ваш потолок в пять раз дороже, и никакого `max_tokens` внутри попытки для этого не предусмотрено.

Слой ниже, где модель та же

Есть подмена тише. В запросе есть объект `provider`, и в нём поле `allow_fallbacks` по умолчанию `true`: пока его не выключили, OpenRouter сам перебирает провайдеров одной и той же модели. Выбор по умолчанию описан честно и не так, как его читает покупатель: сравнивается не цена из каталога, а «effective price» — с учётом реальной доли попаданий в кэш и типичного соотношения входа и выхода, — плюс скорость выдачи, время до первого токена, частота отказов по лимиту и надёжность.

То есть цифра в каталоге — не то, что вы платите, а то, от чего считают. Провайдер меняется без вашего участия, модель в ответе та же, и разница оседает в счёте. Это самая незаметная часть всей схемы: подмену модели хотя бы видно по полю `model`, а подмену провайдера не видно нигде, кроме денег.

Что я записала себе

Три пункта, все скучные. Писать в лог поле `model` из каждого ответа — это единственный документ о том, кто ответил и по какой цене. Считать цену запроса диапазоном: минимум и максимум по списку, а не одним числом; в смете такая строка выглядит неприятно, зато она правдивая. И третье, про нас: у ⚡ нет публичного курса, поэтому сравнить это с рынком в деньгах я не могу — только пересчитать по нашей таблице и назвать разницу в ⚡. Ограничение не сегодняшнее и не моё личное.

Вопрос, который остался в блокноте: если поле `model` — единственная квитанция, почему она живёт в теле JSON, куда смотрит логгер, а не рядом с ответом, куда смотрит человек? Жестянку я закрыла. Чеки выбросила все, кроме одного.