crm.item.list и 429 в Битрикс24: как подсчёт total съедает лимит и как ускорить выгрузку в
PrimeCoder · 2026 · формат: tech
Разбор новинки простыми словами: что это, кому нужно и брать ли.
Разработчикам интеграций с Битрикс24: лимит OPERATION_TIME_LIMIT считается в секундах серверного времени и убивает постраничную выгрузку — переход на start=-1 с фильтром по id снимает блокировку без роста пауз.
Связанные услуги: каталог, AI Boost Team, стать клиентом.
Выгрузка сделок из Битрикс24 годами ходит по расписанию и укладывается в минуты. Потом начинает идти часами. Потом отдаёт HTTP 429. Паузу между запросами увеличили вдвое — не помогло. При этом crm.item.get, задачи и контакты в ту же минуту отвечают 200, и портал со стороны выглядит здоровым.
Причина — второй лимит Битрикс24. Он считает не запросы, а секунды серверного времени. И постраничная выборка, которая по умолчанию тратит это время на подсчёт строк, о котором её никто не просил.
Два лимита с разными кодами
Обычно интеграцию пишут под один лимит: два запроса в секунду, пауза полсекунды, редкий повтор. Для маленького портала этого хватает годами. Дальше появляется второй счётчик, и ведут они себя по-разному.
- 503 QUERY_LIMIT_EXCEEDED — частота HTTP-запросов, схема дырявого ведра. Для Enterprise X = 250 и Y = 5, для остальных тарифов X = 50 и Y = 2. Ведро своё у каждого портала, приложения с одного сервера делят его между собой. Отказ прилетает всем методам сразу и живёт секунды.
- 429 OPERATION_TIME_LIMIT — operating. Портал суммирует секунды выполнения каждого метода отдельно для каждого приложения или вебхука, в 10 одноминутных корзинах. Порог в облаке — 480 секунд за последние 10 минут. Отказ получает один метод одного приложения, остальные вызовы того же приложения проходят.
В коробке ограничение по умолчанию выключено, а если включить — 420 секунд, правится в настройках модуля rest.
Отсюда и симптом. Повтор через секунду бесполезен: корзины освобождаются поминутно, самая старая выпадет из суммы только через 10 минут после своего начала. А crm.item.get отвечает 200, пока crm.item.list уже заблокирован, потому что счётчики разные.
Куда уходят секунды
При start >= 0 метод списка на каждой странице заново считает общее число записей для поля total. Не один раз за выгрузку. На каждой странице.
Документация даёт замер на выборке из 2 387 743 элементов: с подсчётом crm.item.list отвечал 49,9 секунды, с start = -1 — 0,097 секунды.
Разница в 500 раз, и это одна страница из 50 записей.
2,4 млн сделок превращаются в 47 755 страниц, и каждая пересчитывает все 2,4 млн записей. Хотя за время выгрузки число сделок меняется в лучшем случае на десятки. Пока сделок 20 000, подсчёт стоил доли секунды и терялся в паузе между запросами. Дальше он растёт вместе с порталом, а порог operating остаётся тем же.
Что меняет start = -1
Привычный шаблон — start от нуля, next из ответа, usleep между вызовами. Он описан на странице самого метода, и там же сказано: размер страницы всегда 50 записей, для N-й страницы передавайте start = (N-1) * 50. Про start = -1 на этой странице нет ни слова — он живёт на отдельной странице про большие объёмы.
Схема замены: сортировка по id, фильтр по последнему полученному id, start = -1. Метод перестаёт считать total, выгрузка упирается только в частоту запросов, и operating даже не приближается к порогу. Разница на модели выгрузки:
| Сделок | С подсчётом total | 429 | Фильтр по id, start = -1 | 429 |
|---|---|---|---|---|
| 20 000 | 6,8 мин | 0 | 4,0 мин | 0 |
| 100 000 | 1,5 ч | 9 | 19,9 мин | 0 |
| 500 000 | 36,2 ч | 217 | 1,7 ч | 0 |
| 2 387 743 | 33,2 сут | 4775 | 7,9 ч | 0 |
На 20 тысячах разницы почти нет — именно в этой точке интеграцию обычно и пишут, а потом удивляются, когда портал вырастает.
Диагностика за один вечер
Каждый ответ REST содержит блок time. Для дела нужны два поля: operating — накопленное время этого метода для этого приложения, оно и сравнивается с порогом, и operating_reset_at — момент, когда из суммы выпадет самая старая корзина. В примере из документации он на 600 секунд позже старта.
Записывайте оба поля в лог рядом с именем метода. Если operating у crm.item.list растёт на секунды с каждым вызовом и подбирается к сотням, блокировка — вопрос минут. Если растёт на десятые доли, лимит упрётся в частоту запросов намного раньше.
Где подвох
- С
start = -1вы не получитеtotal. Если общее число записей нужно в отчёте, считайте его отдельно и редко, а не 47 755 раз. - Цифры в таблице — модель, а не замер вашего портала. Пропорциональность подсчёта числу сделок в ней допущение; в реальности стоимость страницы зависит от фильтра и прав.
- Ошибок в логах не будет. Выгрузка замедляется месяц за месяцем и однажды перестаёт успевать до утра.
Порога в коде нет, выгрузка замедляется месяц за месяцем вместе с числом сделок и однажды перестаёт успевать до утра. Ошибок до этого момента в логах нет.
Ещё один момент: порог в облаке задаётся конфигурацией Битрикс24, отключить operating из кода интеграции нельзя. Можно только не жечь бюджет на подсчёт, который вам не нужен.
Вердикт за 30 секунд
- Брать. Переписать цикл на
orderпоid, фильтр по последнемуidиstart = -1. Работа на полдня, снимает 429 без роста пауз. - Сначала замер. Один прогон с логом
time.operating. Если там сотые доли секунды на вызов, ваша проблема в частоте запросов, и переписывать нечего. - Подождать нельзя. Отложенный фикс не деградирует плавно: выгрузка просто однажды не закроется до утра, а в логах будет пусто.
FAQ
Я увеличил паузу до двух секунд, 429 остался. Почему?
Потому что operating считает не запросы, а секунды выполнения. Пауза между вызовами не уменьшает время, которое сервер тратит на подсчёт total. Повтор через секунду тоже бесполезен: корзина живёт 10 минут.
Можно ли просто отключить подсчёт total параметром?
Отдельного флага нет. Эквивалент — start = -1, при нём метод не считает общее число записей.
Почему остальные методы работают?
Operating ведётся отдельно для каждого метода каждого приложения или вебхука. Блокируется только тот, что съел бюджет.
Это проблема только облака?
В коробке лимит по умолчанию выключен, но если его включили — порог 420 секунд и он меняется в настройках модуля rest. Логика та же.
Что сделать на этой неделе
Откройте свою ночную выгрузку и найдите место, где формируется start. Если там next из ответа — вы платите секундами за число, которое никуда не сохраняете. Добавьте в лог time.operating и time.operating_reset_at до правок, прогоните один раз на боевом портале, потом переписывайте цикл. Так у вас будет свой замер, а не чужая таблица.