TL;DR. Я собрал self‑service REST API, который принимает фото документа РФ и возвращает структурированный JSON. Паспорт, водительское удостоверение, СТС. Регистрация не нужна, чтобы попробовать — есть демо без ключа: reqdoc.ru. Ниже — контракт, примеры кода и то, что оказалось неочевидно при работе именно с российскими документами.

Пост — про запуск и валидацию спроса. Буду благодарен за критику по делу: и по контракту API, и по тому, каких типов документов вам реально не хватает.

Зачем это

Если вы когда‑нибудь встраивали распознавание паспорта или СТС в свой продукт (страхование, каршеринг, финтех, логистика, любой онбординг с проверкой документов) — вы знаете, что вариантов немного и все так себе:

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

  • Крупные облака — часто это enterprise‑продажи, договоры, интеграция «через менеджера», а не «взял ключ и через 10 минут работает».

  • Готовые SDK — обычно про верификацию личности целиком (KYC), тяжёлые и недешёвые, если тебе нужно просто «фото СТС → поля».

Мне не хватало простого промежуточного слоя: берёшь ключ → шлёшь картинку → получаешь JSON. По модели, знакомой любому, кто дёргал DaData: self‑service, документация, честный контракт, тарифы на сайте. Только не адреса/ФИО из строки, а документы.

И вот тут — главное, ради чего всё затевалось. Я не претендую обогнать большие облака по качеству модели. Ставка в другом: самый короткий путь от «увидел» до «работает в проде». Открыл сайт → потрогал демо без регистрации → взял ключ в боте → сделал первый боевой запрос. Реально за пару минут, без договора, менеджера, КП и «подключения через отдел продаж». Чтобы сделать то же самое у большого провайдера, обычно уходят часы, а то и дни переписки. Если сравнивать по developer experience — вот здесь reqdoc и должен выигрывать.

Так появился reqdoc.

Контракт: один документ → один JSON

Один эндпоинт, файл отправляется как обычная веб‑форма с вложниемmultipart/form-data:

POST https://api.reqdoc.ru/v1/recognize

Два поля: file — сам файл документа в самых распространённых форматах (jpg/png/pdf, до 10 МБ) и type (passport | driver_license | vehicle_registration | vehicle_registration_back).

Попробовать можно прямо curl‑ом, без ключа (демо‑режим, лимиты по IP):

curl -X POST https://api.reqdoc.ru/v1/recognize \
  -F "type=passport" \
  -F "file=@passport.jpg"

Ответ ‑в едином формате. Даты — как в документе (DD.MM.YYYY), ключи snake_case, нераспознанное поле приходит как null (а не выдумывается):

{
  "status": "ok",
  "type": "passport",
  "data": {
    "surname": "Иванов",
    "first_name": "Иван",
    "patronymic": "Иванович",
    "gender": "male",
    "birth_date": "03.08.1978",
    "birth_place": "с. Леоново, Иркутская обл.",
    "series": "9223",
    "number": "376525",
    "issue_date": "08.09.2023",
    "issuing_authority": "МВД по Республике Татарстан",
    "division_code": "160-008",
    "registration_address": "г. Москва, ..."
  },
  "request_id": "e8468676-c5d0-4e84-8625-4b2eb7396241"
}

С ключом — добавляется Authorization: Bearer reqdoc_live_<...>, и вызовы считаются по месячной квоте тарифа. Ключ выдаёт Telegram‑бот (/key), это самый быстрый способ начать.

Пример на Python:

import requests

with open("sts.jpg", "rb") as f:
    r = requests.post(
        "https://api.reqdoc.ru/v1/recognize",
        headers={"Authorization": "Bearer reqdoc_live_..."},
        data={"type": "vehicle_registration"},
        files={"file": f},
    )
print(r.json()["data"])

Полный контракт с полями по каждому типу и кодами ошибок — в документации: docs.reqdoc.ru.

Что оказалось неочевидно с РФ‑документами

Самое интересное — не «прогнать через OCR», а привести хаос к предсказуемому контракту. Несколько вещей, на которые ушло непропорционально много внимания:

1. Серия и номер живут одной строкой. OCR отдаёт паспорт как 9223 376525, ВУ как 99 04 218375, а разработчику удобнее получить series и number отдельно. Причём правило разбивки разное: у паспорта серия — первые 4 цифры, у ВУ — тоже 4, но исходная строка отформатирована иначе. Поэтому поверх OCR лежит слой нормализации, который знает про каждый тип: где резать, что тримить, как раскладывать.

2. Марка и модель — тоже одна строка. В СТС приходит KIA RIO → раскладываем в make (первое слово) и model (остаток). Мелочь, но без неё каждый интегратор пишет один и тот же парсер у себя.

3. Лицевая и оборот СТС — это два разных снимка. Владелец и адрес регистрации физически на обороте. Поэтому два типа (vehicle_registration и vehicle_registration_back): вы загружаете фото нужной стороны — API не притворяется, что видит то, чего на кадре нет.

4. Даты — как в документе, без «умной» нормализации. Соблазн привести всё к ISO велик, но для документов важнее точное соответствие тому, что напечатано (03.08.1978). Нормализацию под свой формат делает потребитель — так меньше сюрпризов.

5. null вместо галлюцинации. Если поля на кадре нет (снял разворот с фото — регистрации там не будет) — приходит null. Никаких «додумок».

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

Защита API и мониторинг

Публичный API без защиты живёт недолго, поэтому на входе — слой, который делает скучные, но обязательные вещи:

  • Ключи (reqdoc_live_...), в базе — только SHA-256 хеш, не сам токен.

  • Rate‑limit фиксированным окном (по ключу и по IP для демо) — атомарный инкремент счётчика в Postgres, дешёвый и предсказуемый.

  • Месячная квота на аккаунт: тариф задаёт включённый объём, дальше — overage или жёсткий стоп (для бесплатных).

  • request_id в теле ответа и в заголовке X-Request-Id — чтобы можно было сослаться на конкретный вызов в поддержке.

  • Лог использования без ПДн: пишутся метаданные вызова (тип документа, статус, латентность, биллинг‑флаг), но не содержимое документа.

Ошибки — не «500 и разбирайся», а типизированный код: invalid_key (401), invalid_type (400), rate_limited (429), recognition_empty (422), ocr_failed (502).

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

  • Health‑эндпоинт GET /v1/health прогоняет цепочку насквозь (веб‑слой → оркестратор → БД) и отдаёт 200/500 — то есть проверяет не «отвечает ли порт», а живой ли путь до базы.

  • Внутренняя проверка раз в пару минут следит за API, OCR, БД и свободным местом на диске (однажды именно забитый диск уронил сервис — теперь это отдельная метрика). Алерт приходит мгновенно и только на смене состояния — чтобы не было шума из одинаковых сообщений.

  • Внешний dead‑man‑мониторинг пингует health со стороны, независимо от нашего сервера: если упадёт вся машина целиком (и внутренний монитор вместе с ней), сигнал всё равно придёт.

Смысл — узнавать о проблеме первым, а не из тикета. Для API, который кто‑то встроил в свой онбординг, это не опция, а часть контракта надёжности.

Про 152-ФЗ

Документы — это персданные, поэтому инфраструктура на РФ‑хостинге, а в логах вызовов не хранится содержимое документа — только техническая телеметрия. Это осознанное ограничение: меньше данных о пользователе на нашей стороне — меньше рисков у всех.

Честные ограничения

Чтобы не создавать ложных ожиданий:

  • Нет confidence. OCR не отдаёт вероятность по полям, поэтому и API её не выдумывает. Валидацию критичных полей закладывайте у себя.

  • Пока только 3 типа (паспорт, ВУ, СТС + оборот). Европротокол, справка ГАИ, СНИЛС, ИНН, ПТС — в планах, но эндпоинтов ещё нет.

  • OCR не идеален. Кривое фото, блики, наклон — влияют. Демо специально даёт потрогать на своих файлах, чтобы вы оценили качество до интеграции.

Тарифы

Модель freemium: бесплатный trial‑ключ на 50 распознаваний/мес выдаёт бот — этого хватает, чтобы проверить в бою. Дальше — платные тарифы (от 990 ₽/мес), детали на сайте. Специально не расписываю прайс здесь — пост не про это.

Куда дальше — и вопрос к вам

Сейчас стадия — валидация спроса. Инфраструктура, контракт, демо, документация, выдача ключей — уже живые. Дальше зависит от обратной связи.

Поэтому вопрос к тем, кто такое встраивал: каких типов документов вам не хватает больше всего? Европротокол? ПТС? СНИЛС/ИНН? Что‑то ещё? И что в контракте вы бы сделали иначе — ISO‑даты, другой формат ответа, вебхуки вместо синхронного ответа?

Потрогать: reqdoc.ru · документация: docs.reqdoc.ru · ключ: бот @req_doc_bot.

Спасибо, что дочитали. Критика приветствуется — за тем и пришёл.