Public API
Справочники ОКПД-2 и КТРУ, генерация технических заданий и импорт товаров из PDF, Word и Excel — через REST API с аутентификацией по API-ключу
Быстрый старт
Зарегистрируйтесь
Создайте аккаунт на moy-zakupki.ru
Получите API-ключ
Перейдите в Профиль → API-ключи и создайте новый ключ. Ключ показывается только один раз — сохраните его.
Сделайте первый запрос
curl -H "Authorization: Api-Key mz_live_ваш_ключ" \
https://moy-zakupki.ru/api/public/v2/ktru/?per_page=2Аутентификация
Все запросы к Public API требуют заголовок Authorization с вашим API-ключом.
Формат заголовка:
Authorization: Api-Key mz_live_ваш_ключBase URL
https://moy-zakupki.ru/api/public/v2 Формат ответов
JSON (REST API) Важно: API-ключ показывается только при создании. Если ключ утерян, деактивируйте его и создайте новый в настройках профиля.
КТРУ (Каталог товаров, работ, услуг)
/api/public/v2/ktru/Получить список позиций КТРУ с пагинацией и фильтрацией
Query параметры:
statusСтатус КТРУ (например, "Включено в КТРУ")is_enlargedУкрупненные позиции (true / false)okpd2_codeФильтр по коду ОКПД-2 (например, "26.20.11")pageНомер страницы (по умолчанию: 1)per_pageЗаписей на странице (по умолчанию: 15, макс: 50)Пример ответа:
{
"count": 77000,
"next": "https://moy-zakupki.ru/api/public/v2/ktru/?page=2",
"previous": null,
"results": [
{
"code": "01.11.11.110-00000001",
"name": "Пшеница продовольственная",
"shortname": "Пшеница",
"status": "Включено в КТРУ",
"is_enlarged": false,
"okpd2_code": "01.11.11",
"okpd2_name": "Выращивание зерновых культур",
"unit_name": "тонн",
"characteristics_count": 3
}
]
}/api/public/v2/ktru/{code}/Получить детальную информацию о позиции КТРУ, включая характеристики и ограничения
Пример ответа:
{
"code": "26.20.11.110-00000165",
"name": "Компьютеры портативные массой не более 10 кг",
"shortname": "Ноутбук",
"description": "...",
"status": "Включено в КТРУ",
"is_enlarged": false,
"okpd2_code": "26.20.11",
"okpd2_name": "Компьютеры портативные",
"unit_name": "шт",
"characteristics": [
{
"key": "Диагональ экрана",
"values": ["13.3", "15.6", "17.3"]
}
],
"limitations": [
{
"name": "Ограничение по цене",
"group": "price"
}
],
"last_version": {
"version": 3,
"publish_date": "2024-10-15",
"application_date_start": "2024-11-01",
"inclusion_date": "2024-10-15"
}
}/api/public/v2/ktru/search/Поиск КТРУ по характеристикам
Request body:
{
"characteristics": ["мощность", "размер диагонали:27"],
"status": "Включено в КТРУ",
"is_enlarged": false
} Поля status и is_enlarged необязательные. Характеристики можно передавать как ключевые слова или пары "ключ:значение".
ОКПД-2 (Классификатор)
/api/public/v2/okpd2/Получить список кодов ОКПД-2 с пагинацией и фильтрацией
Query параметры:
levelУровень иерархии (2, 3, 4, 5...)is_actualТолько актуальные (true по умолчанию)pageНомер страницы (по умолчанию: 1)per_pageЗаписей на странице (по умолчанию: 15, макс: 50)Пример ответа:
{
"count": 20372,
"next": "https://moy-zakupki.ru/api/public/v2/okpd2/?page=2",
"previous": null,
"results": [
{
"code": "01.11.11",
"name": "Выращивание зерновых культур",
"level": 5,
"parent_code": "01.11",
"is_actual": true
}
]
}/api/public/v2/okpd2/{code}/Получить детальную информацию о коде ОКПД-2, включая дочерние коды и ограничения
Пример ответа:
{
"code": "01.11",
"name": "Выращивание зерновых и зернобобовых культур",
"level": 4,
"parent_code": "01",
"is_actual": true,
"children": [
{
"code": "01.11.11",
"name": "Выращивание зерновых культур",
"level": 5
}
],
"limitations": [
{
"name": "Ограничение",
"group": "regulatory"
}
],
"ktru_count": 42
}Мои технические задания
Выгрузка ТЗ, созданных в вашем аккаунте. Ключ видит только ваши документы. Эти запросы не расходуют месячный лимит — он отведён под справочники.
/api/public/v2/tech-specs/Список ваших ТЗ без позиций — быстрый, подходит для регулярной синхронизации.
Query параметры:
statusready / draft / generating / errorupdated_afterISO-8601. Для инкрементальной выгрузки: забирайте только изменённоеcreated_afterISO-8601, дата или дата-времяcreated_beforeISO-8601, дата или дата-время/api/public/v2/tech-specs/{id}/Полная структура: позиции, коды ОКПД-2/КТРУ и характеристики.
Пример ответа:
{
"id": "3f1c…",
"name": "Закупка лестниц",
"status": "ready",
"products_count": 2,
"products": [
{
"position": 1,
"name": "Лестница алюминиевая трёхсекционная",
"quantity": "5.00",
"unit": "шт",
"is_included": true,
"ktru_code": "25.99.29.190-00000123",
"okpd2_code": "25.99.29.190",
"characteristics": [
{
"name": "Объем встроенного накопителя",
"unit": "Гигабайт",
"source": "ktru",
"is_included": true,
"values": [
{
"value": "512",
"display": "≥ 512",
"requirement_type": "range",
"range_type": "greater_or_equal",
"extra_value": null,
"extra_range_type": null
}
]
}
]
}
]
}О значениях характеристик. Требование описывается всей связкой requirement_type / range_type / extra_value, а не одним value: например «не менее 512» — это range с greater_or_equal. Ориентируйтесь на них, если строите документ программно.
Коды могут быть null. Часть позиций не сопоставлена ни с КТРУ, ни с ОКПД-2 — это штатная ситуация, а не ошибка выгрузки. Отключённые позиции и характеристики возвращаются с is_included: false, а не пропускаются.
Генерация ТЗ
Запуск генерации технического задания — та же операция, что кнопка в личном кабинете, с тем же телом запроса. Работает асинхронно: запрос ставит задачу в очередь и сразу возвращает её идентификатор.
Генерации не расходуют лимит в 1 000 запросов/мес. Тот лимит отведён под справочники КТРУ и ОКПД-2. Генерация списывается из квоты генераций вашего тарифа — той же, что тратит интерфейс. Запуск, опрос статуса и выгрузка готового ТЗ месячный лимит запросов не расходуют вовсе: работа со своими ТЗ ничем не ограничена, кроме общего лимита запросов в минуту.
/api/public/v2/tech-spec-generations/ Ставит генерацию в очередь. Списывает одну генерацию за каждую позицию в items.
Тело запроса:
{
"items": [
{
"product_name": "Ноутбук офисный",
"product_code": "26.20.13.000-00000002",
"quantity": 5,
"unit": "шт"
}
]
} Обязательно только product_name. Остальное необязательно: product_code — код КТРУ или ОКПД2 (классификатор определяется самим кодом: у позиции КТРУ есть номер через дефис), max_resources (1–20, по умолчанию 5) и max_sites (1–10, по умолчанию 3) управляют широтой поиска источников. Блок settings с header и footer задаёт шапку и подвал документа.
Ответ 201:
{
"id": "8c2e…",
"status": "processing",
"progress": 0,
"is_finished": false,
"product_name": "Ноутбук офисный",
"tech_spec_id": "3f1c…",
"error": null,
"generation_quota_remaining": 47
}/api/public/v2/tech-spec-generations/{id}/ Статус запуска. Опрашивайте раз в 10 секунд, пока is_finished не станет true: бесплатный ключ даёт 10 запросов в минуту на всё, и частый опрос упрётся в 429. Генерация занимает около полутора минут, в тяжёлых случаях — до шести.
pendingв очередиprocessingгенерируется, progress растёт от 0 до 100completedготово — забирайте документ по tech_spec_idfailedпричина в поле error, квота возвращена Список всех ваших запусков — GET /api/public/v2/tech-spec-generations/.
Полный цикл
import time, requests
H = {"Authorization": "Api-Key mz_live_…"}
BASE = "https://moy-zakupki.ru/api/public/v2"
def is_quota_refusal(r):
"""429 с полем error — кончилась квота: ждать бесполезно."""
try:
return "error" in r.json()
except ValueError: # 429 от прокси приходит HTML-страницей
return False
def call(method, path, **kwargs):
"""Запрос с повтором, пока действует лимит запросов в минуту."""
while True:
r = requests.request(method, f"{BASE}{path}", headers=H, timeout=60, **kwargs)
if r.status_code != 429 or is_quota_refusal(r):
return r
time.sleep(int(r.headers.get("Retry-After", 10)))
# 1. Запускаем генерацию
r = call("POST", "/tech-spec-generations/", json={
"items": [{"product_name": "Ноутбук офисный", "quantity": 5, "unit": "шт"}]
})
if r.status_code == 429: # insufficient_generation_quota
raise SystemExit(r.json()["detail"])
r.raise_for_status() # 400, 401 и прочие отказы
task = r.json()
# 2. Ждём завершения — не чаще раза в 10 секунд
while not task["is_finished"]:
time.sleep(10)
r = call("GET", f"/tech-spec-generations/{task['id']}/")
r.raise_for_status()
task = r.json()
if task["status"] == "failed":
raise RuntimeError(task["error"])
# 3. Забираем готовое ТЗ
r = call("GET", f"/tech-specs/{task['tech_spec_id']}/")
r.raise_for_status()
spec = r.json()
print(spec["name"], spec["products_count"])Кончились генерации — придёт 429 с полем error: "insufficient_generation_quota". Этим он отличается от 429 при превышении лимита запросов в минуту: там такого поля нет. В первом случае нужно пополнить квоту, во втором — просто подождать.
Завершение не гарантирует полноту. Иногда пайплайн отрабатывает, но по части позиций характеристики найти не удаётся. Если результат важен для дальнейшей автоматики, проверяйте содержимое ТЗ, а не только status.
Импорт товаров
Загрузите перечень товаров — PDF, Word, Excel или просто текст, — и платформа распознает товары: наименование, код КТРУ или ОКПД2 (если он указан в документе, он проверяется и привязывается к справочнику), единицу измерения, количество и характеристики. Товары сохраняются в вашем аккаунте, а вся загрузка — отдельным ТЗ «Импорт: <имя файла>» или, если передать tech_spec_id, — в конец вашего ТЗ. Это та же операция, что кнопка «Загрузить товары из файла» в личном кабинете. Работает асинхронно: запрос сразу возвращает идентификатор загрузки, результат забирается опросом.
У импорта своя квота — загрузки. Без подписки — 5 загрузок в месяц и до 10 страниц в одной загрузке, с подпиской — 50 загрузок и до 50 страниц. Файл — до 20 МБ. Страница у PDF — настоящая страница, у Word, Excel и текста — 1 800 символов. Квота общая с личным кабинетом и считается на аккаунт, а не на ключ; обновляется 1-го числа по Москве. Лимит в 1 000 запросов к справочникам импорт не расходует — ни загрузка, ни опрос. Сбой, отказ и документ без товаров возвращают загрузку в квоту.
/api/public/v2/product-imports/Принимает документ и ставит распознавание в очередь. Передайте что-то одно: файл или текст.
filemultipart/form-data: PDF, Word (.docx) или Excel (.xlsx), до 20 МБ. Формат определяется по содержимому, а не по расширениюtextapplication/json: перечень товаров текстом. Содержимое .txt-файла передавайте здесьtech_spec_idНеобязательно, поле формы или ключ JSON: id вашего ТЗ. Товары допишутся в его конец, а не в новое ТЗ «Импорт: …»; статус ТЗ не меняется. Чужое или удалённое ТЗ — 404, ТЗ, которое ещё генерируется, — 409, загрузка не списывается. Если ТЗ удалят, пока идёт разбор, товары сохранятся в новом ТЗ: tech_spec_id ответа будет отличаться от target_tech_spec_id, причина — в warningsIdempotency-KeyЗаголовок, необязательный, до 64 символов — см. «Идемпотентность» ниже# Файл
curl -X POST "https://moy-zakupki.ru/api/public/v2/product-imports/" \
-H "Authorization: Api-Key mz_live_ваш_ключ" \
-H "Idempotency-Key: spec-2026-09-27-01" \
-F file=@spec.pdf
# Текст
curl -X POST "https://moy-zakupki.ru/api/public/v2/product-imports/" \
-H "Authorization: Api-Key mz_live_ваш_ключ" \
-H "Content-Type: application/json" \
-d '{"text": "1. Ноутбук — 5 шт. Диагональ не менее 15,6 дюйма, ОЗУ не менее 16 ГБ\n2. Мышь беспроводная — 5 шт."}'
# Дописать в конец своего ТЗ
curl -X POST "https://moy-zakupki.ru/api/public/v2/product-imports/" \
-H "Authorization: Api-Key mz_live_ваш_ключ" \
-F file=@spec.xlsx \
-F tech_spec_id=3f1c2b7a-5d4e-4f60-9a8b-7c6d5e4f3a21 Не принимаются .doc и .xls (пересохраните в .docx, .xlsx или PDF), файлы с макросами и защищённые паролем. Сканы в PDF распознаются; Word-файл, где вместо текста картинки, — нет: загрузите его как PDF.
Ответ 201 с заголовком Location:
{
"id": "5b0e2c7a-…",
"status": "pending",
"is_finished": false,
"stage": "",
"progress": 0,
"source": {
"type": "pdf",
"filename": "spec.pdf",
"size_bytes": 184320,
"pages": 4,
"chars": null,
"is_scan": false
},
"products_count": 0,
"products_extracted": 0,
"codes": {"linked": 0, "not_found": 0, "not_in_document": 0},
"tech_spec_id": null,
"target_tech_spec_id": null,
"error_code": null,
"error": null,
"warnings": [],
"skipped_count": 0,
"quota_refunded": false,
"resumed_after_auth": false,
"created_at": "2026-09-27T10:15:02.114000+00:00",
"finished_at": null,
"poll_after_seconds": 10,
"import_quota": {
"used": 1,
"limit": 5,
"available": 4,
"resets_at": "2026-10-01T00:00:00+03:00",
"pages_per_import": 10,
"plan": "free"
}
}source.pages — сколько страниц засчитано. У PDF и текста они известны сразу, у Word и Excel — после разбора, поэтому до завершения там null.
/api/public/v2/product-imports/{id}/ Статус загрузки, после завершения — товары. Опрашивайте не чаще раза в poll_after_seconds секунд (10), пока is_finished не станет true: бесплатный ключ даёт 10 запросов в минуту на всё.
pendingв очередиprocessingидёт разбор; stage: reading → extracting → linking → saving, progress — от 0 до 100completedготово, товары — в products. Если товаров не нашлось, products пуст, error_code: "no_products_found" и загрузка возвращенаpartialчасть документа не разобрана — что именно, сказано в warnings; сохранённые товары в products, загрузка засчитанаfailedсбой обработки, причина — в error, загрузка возвращенаrejectedдокумент не подошёл: страниц больше лимита, файл повреждён, в Word только картинки. Код — в error_code, загрузка возвращенаПример ответа после завершения:
{
"id": "5b0e2c7a-…",
"status": "completed",
"is_finished": true,
"stage": "done",
"progress": 100,
"source": {"type": "pdf", "filename": "spec.pdf", "size_bytes": 184320,
"pages": 4, "chars": 6953, "is_scan": false},
"products_count": 2,
"products_extracted": 2,
"codes": {"linked": 2, "not_found": 0, "not_in_document": 0},
"tech_spec_id": "3f1c…",
"error_code": null,
"error": null,
"warnings": [],
"skipped_count": 0,
"quota_refunded": false,
"resumed_after_auth": false,
"created_at": "2026-09-27T10:15:02.114000+00:00",
"finished_at": "2026-09-27T10:16:31.402000+00:00",
"poll_after_seconds": null,
"products": [
{
"id": "a91d…",
"position": 1,
"name": "Ноутбук",
"unit": "шт",
"quantity": 5.0,
"code": {
"as_written": "26.20.11.110-00000165",
"ktru_code": "26.20.11.110-00000165",
"okpd2_code": "26.20.11.110",
"match": "ktru_exact"
},
"characteristics": [
{"name": "Диагональ экрана", "value": "≥ 15.6", "unit": "дюйм", "by_ktru": true},
{"name": "Объем оперативной памяти", "value": "не менее 16", "unit": "Гигабайт", "by_ktru": true},
{"name": "Тип накопителя", "value": "SSD", "unit": null, "by_ktru": false}
],
"notes": ["Характеристик сопоставлено с КТРУ: 2 из 3."]
},
{
"id": "c07b…",
"position": 2,
"name": "Мышь компьютерная беспроводная",
"unit": "шт",
"quantity": 5.0,
"code": {
"as_written": "26.20.16.170-00000999",
"ktru_code": null,
"okpd2_code": "26.20.16.170",
"match": "ktru_not_found_okpd2_from_prefix"
},
"characteristics": [
{"name": "Тип подключения", "value": "беспроводное", "unit": null, "by_ktru": false}
],
"notes": ["Код КТРУ «26.20.16.170-00000999» не найден; привязан ОКПД2 26.20.16.170."]
}
]
}Как привязан код — поле code.match: ktru_exact и okpd2_exact — код из документа найден в справочнике; okpd2_ancestor — ОКПД2 не найден, привязан ближайший вышестоящий; ktru_not_found_okpd2_from_prefix — КТРУ не найден, привязан ОКПД2 из его начала; drug_registry_code — это код лекарства (ЕСКЛП), а не КТРУ; not_found и invalid_format — код не найден или не похож на код; code_not_in_document — кода нет в тексте документа, и он не привязан; no_code — в документе кода не было. Код по названию товара не подбирается. code.as_written — код дословно из документа.
Характеристики по КТРУ — by_ktru: true, если справочник КТРУ знает и имя характеристики, и её значение; в документе Word они идут как «Характеристика по КТРУ». Остальные — дополнительные.
Значения — как в документе. Характеристика — строка value с требованием в том виде, как оно записано: "≥ 27", "от 10 до 20", "белый"; unit и коды могут быть null. Под позицией в notes — пометки о том, что стоит проверить: код не найден, единица не распознана, количество заменено на 1.
/api/public/v2/product-imports/ Ваши загрузки, новые сверху, без товаров. Параметры page и per_page (до 50), фильтры status (например, completed) и created_after (2026-09-01 или ISO 8601 с временем); ответ — в обычной пагинации. Загрузки из личного кабинета здесь тоже видны.
/api/public/v2/product-imports/limits/Форматы, лимиты тарифов и расход загрузок этого месяца. Удобно проверить до отправки большого документа.
{
"formats": [".pdf", ".docx", ".xlsx", ".txt", "text"],
"max_file_bytes": 20971520,
"chars_per_page": 1800,
"free": {"monthly_imports": 5, "pages_per_import": 10},
"paid": {"monthly_imports": 50, "pages_per_import": 50},
"import_quota": {
"used": 1,
"limit": 5,
"available": 4,
"resets_at": "2026-10-01T00:00:00+03:00",
"pages_per_import": 10,
"plan": "free"
}
}Ошибки загрузки
Отказ приходит в одной форме: {"error": "<код>", "detail": "<пояснение>"}. Ни один отказ не списывает загрузку.
| HTTP | error | Когда |
|---|---|---|
| 400 | invalid_request, empty_document | Не передан ни файл, ни текст (или переданы оба); файл или текст пустой |
| 404 | tech_spec_not_found | ТЗ из tech_spec_id не найдено среди ваших: чужое, удалено или id некорректен |
| 409 | import_in_progress | Предыдущая загрузка аккаунта ещё обрабатывается: одновременно идёт только одна |
| 409 | idempotency_key_reused | Тот же Idempotency-Key уже использован для другого документа или другого tech_spec_id |
| 409 | tech_spec_generating | ТЗ из tech_spec_id ещё генерируется — дождитесь окончания и повторите |
| 413 | file_too_large | Файл больше 20 МБ |
| 415 | unsupported_format, legacy_format, macro_enabled, encrypted, corrupted | Не PDF, DOCX или XLSX; .doc / .xls; файл с макросами; защищён паролем; повреждён |
| 422 | too_many_pages | Страниц больше лимита тарифа (в теле — pages и limit_pages). Так же — повреждённый или запароленный PDF (corrupted, encrypted) |
| 429 | import_quota_exceeded | Кончились загрузки месяца (reason: "monthly_limit") или сработал суточный предохранитель попыток ("daily_attempts"). В теле — import_quota и payment_url. Ждать и повторять бесполезно |
| 500 | storage_failed, dispatch_failed | Не удалось сохранить файл или поставить его в очередь. Загрузка не списана — повторите |
429 без поля error — это лимит запросов в минуту: подождите Retry-After секунд и повторите. У Word и Excel страницы считаются уже при разборе, поэтому лишние страницы в них приходят не 422, а статусом rejected с error_code: "too_many_pages".
Идемпотентность. Передайте заголовок Idempotency-Key — любую строку до 64 символов, одну на документ, — и повтор запроса после обрыва сети безопасен: тот же ключ с тем же телом вернёт ту же загрузку со статусом 200 и заголовком Idempotent-Replayed: true, вторая загрузка не спишется. Тот же ключ с другим документом — 409 idempotency_key_reused. Без ключа повторная отправка того же файла создаст товары ещё раз — в warnings придёт предупреждение.
Полный цикл
import time, uuid, requests
H = {"Authorization": "Api-Key mz_live_…"}
BASE = "https://moy-zakupki.ru/api/public/v2"
def is_quota_refusal(r):
"""429 с полем error — кончилась квота: ждать бесполезно."""
try:
return "error" in r.json()
except ValueError: # 429 от прокси приходит HTML-страницей
return False
def call(method, path, headers=None, **kwargs):
"""Запрос с повтором, пока действует лимит запросов в минуту."""
while True:
r = requests.request(method, f"{BASE}{path}", headers={**H, **(headers or {})},
timeout=120, **kwargs)
if r.status_code != 429 or is_quota_refusal(r):
return r
time.sleep(int(r.headers.get("Retry-After", 10)))
# 1. Загружаем файл. Ключ идемпотентности — один на документ:
# повтор с ним после обрыва сети не спишет вторую загрузку.
with open("spec.pdf", "rb") as fh:
data = fh.read()
key = str(uuid.uuid4())
r = call("POST", "/product-imports/", headers={"Idempotency-Key": key},
files={"file": ("spec.pdf", data)})
if r.status_code == 429: # import_quota_exceeded
body = r.json()
raise SystemExit(f"{body['detail']} Тарифы: https://moy-zakupki.ru{body['payment_url']}")
if r.status_code not in (200, 201): # 400, 409, 413, 415, 422, 500 — не списано
raise SystemExit(f"{r.status_code}: {r.text[:500]}")
job = r.json()
# 2. Ждём завершения — не чаще, чем просит сервер
while not job["is_finished"]:
time.sleep(job["poll_after_seconds"] or 10)
r = call("GET", f"/product-imports/{job['id']}/")
r.raise_for_status()
job = r.json()
# 3. Результат
if job["status"] in ("failed", "rejected"): # загрузка возвращена в квоту
raise SystemExit(f"{job['error_code']}: {job['error']}")
for warning in job["warnings"]:
print("Внимание:", warning)
if not job["products"]:
print(job["error"]) # «В документе не найдено товаров» — тоже не списано
for p in job["products"]:
code = p["code"]["ktru_code"] or p["code"]["okpd2_code"] or "без кода"
print(f"{p['position']}. {p['name']} — {p['quantity']:g} {p['unit']} [{code}]")
for c in p["characteristics"]:
print(f" {c['name']}: {c['value']} {c['unit'] or ''}")
for note in p["notes"]:
print(f" ! {note}")Тарифы и лимиты
Каждый API-ключ привязан к тарифу, определяющему месячную квоту и ограничение по частоте запросов. Месячная квота расходуется только справочниками (КТРУ и ОКПД-2); работа со своими ТЗ — генерация, опрос статуса и выгрузка — и импорт товаров из неё не списываются.
| Тариф | Запросов / месяц | Запросов / мин |
|---|---|---|
| Free | 1 000 | 10 |
| Starter | 10 000 | 30 |
| Business | 100 000 | 60 |
| Enterprise | 1 000 000 | 120 |
При превышении лимита API вернет 429 Too Many Requests с заголовком Retry-After. Месячная квота сбрасывается 1-го числа каждого месяца.
Оба лимита действуют на аккаунт целиком. Ключей можно завести сколько угодно — например, по одному на приложение, — но счётчик у них общий, и выпуск нового ключа его не обнуляет. Текущие значения всегда приходят в заголовках X-RateLimit-* и X-Monthly-Quota-*.
Эта квота — про справочники. Генерация ТЗ тратит отдельную квоту генераций вашего тарифа, импорт товаров — свою квоту загрузок, а не эти запросы; опрос статуса не расходует ни одну из квот. Лимит запросов в минуту действует на всё: при 10 запросах в минуту опрашивайте статус не чаще раза в 10 секунд.
Коды ошибок
401Unauthorized
Отсутствует или невалидный API-ключ, ключ деактивирован или истёк. Исчерпанная квота даёт не 401, а 429
400Bad Request
Некорректные параметры запроса
404Not Found
Не найден код КТРУ или ОКПД-2 либо ваше ТЗ, генерация или загрузка с таким идентификатором
429Too Many Requests
Превышен лимит запросов в минуту или исчерпана месячная квота справочников — повторите через Retry-After секунд. Если в теле есть поле error, кончилась квота генераций (insufficient_generation_quota) или загрузок (import_quota_exceeded) — повтор не поможет: пополните квоту по ссылке из payment_url или дождитесь её обновления
500Internal Server Error
Внутренняя ошибка сервера. Повторите запрос позже
Пагинация
Все списочные эндпоинты возвращают результаты с пагинацией. Для навигации по страницам используйте параметры page и per_page.
pageНомер страницы (по умолчанию: 1)per_pageЗаписей на странице (по умолчанию: 15, максимум: 50)GET /api/public/v2/ktru/?page=3&per_page=25
{
"count": 77000,
"next": "https://moy-zakupki.ru/api/public/v2/ktru/?page=4&per_page=25",
"previous": "https://moy-zakupki.ru/api/public/v2/ktru/?page=2&per_page=25",
"results": [...]
}Примеры кода
Python (requests)
import requests
API_KEY = "mz_live_ваш_ключ"
BASE_URL = "https://moy-zakupki.ru/api/public/v2"
HEADERS = {"Authorization": f"Api-Key {API_KEY}"}
# Получить список КТРУ
response = requests.get(
f"{BASE_URL}/ktru/",
headers=HEADERS,
params={"status": "Включено в КТРУ", "per_page": 10}
)
data = response.json()
print(f"Всего записей: {data['count']}")
for item in data["results"]:
print(f" {item['code']} — {item['name']}")
# Получить детали по коду
response = requests.get(
f"{BASE_URL}/ktru/26.20.11.110-00000165/",
headers=HEADERS
)
detail = response.json()
print(f"Характеристик: {len(detail['characteristics'])}")JavaScript (Fetch API)
const API_KEY = "mz_live_ваш_ключ";
const BASE_URL = "https://moy-zakupki.ru/api/public/v2";
// Получить список КТРУ
const response = await fetch(
`${BASE_URL}/ktru/?status=Включено+в+КТРУ&per_page=10`,
{ headers: { "Authorization": `Api-Key ${API_KEY}` } }
);
const data = await response.json();
console.log(`Всего записей: ${data.count}`);
data.results.forEach(item =>
console.log(` ${item.code} — ${item.name}`)
);
// Получить детали ОКПД-2
const okpdResponse = await fetch(
`${BASE_URL}/okpd2/26.20.11/`,
{ headers: { "Authorization": `Api-Key ${API_KEY}` } }
);
const okpd = await okpdResponse.json();
console.log(`Дочерних кодов: ${okpd.children.length}`);cURL
# Список КТРУ
curl "https://moy-zakupki.ru/api/public/v2/ktru/?per_page=5" \
-H "Authorization: Api-Key mz_live_ваш_ключ"
# Детали КТРУ по коду
curl "https://moy-zakupki.ru/api/public/v2/ktru/26.20.11.110-00000165/" \
-H "Authorization: Api-Key mz_live_ваш_ключ"
# Список ОКПД-2 (уровень 3)
curl "https://moy-zakupki.ru/api/public/v2/okpd2/?level=3&per_page=10" \
-H "Authorization: Api-Key mz_live_ваш_ключ"
# Поиск КТРУ по характеристикам
curl -X POST "https://moy-zakupki.ru/api/public/v2/ktru/search/" \
-H "Authorization: Api-Key mz_live_ваш_ключ" \
-H "Content-Type: application/json" \
-d '{"characteristics": ["мощность", "напряжение:220"]}'Интерактивная документация
Полная интерактивная документация доступна через Swagger UI. Можно выполнять запросы прямо из браузера.
Открыть Swagger UI