Дизайн публичного API для SaaS с нуля: версии, лимиты, SDK
Дизайн публичного API: как новичку в SaaS продумать версии, пагинацию, лимиты, документацию и простой SDK без лишней сложности.

Зачем SaaS вообще нужен публичный API
Публичный API - это стабильный вход в ваш сервис для чужих систем: партнерских продуктов, интеграторов, внутренней автоматизации клиента. В отличие от внутреннего API, он живет дольше, им пользуются люди вне вашей команды, и любая поломка становится их проблемой, а значит и вашей поддержкой.
С публичным API вы растете там, где UI не помогает. Например, бухгалтерия клиента хочет, чтобы счета и статусы оплат автоматически попадали в их систему. Им не нужен ваш интерфейс, им нужен надежный способ забрать данные и отправить команду.
Самые дорогие ошибки в первых интеграциях обычно не в «красоте» эндпоинтов, а в непредсказуемости. Когда сегодня ответ такой, завтра другой, а после релиза мобильного приложения внезапно меняются поля или статусы. Интегратор тратит часы на отладку, вы тратите дни на разбор тикетов, и доверие падает.
Есть решения, которые потом менять особенно больно, потому что они просачиваются в код клиентов и живут там годами: формат идентификаторов и ключевых полей (строка vs число), правила версионирования и политика изменений, способ аутентификации и модель доступов, структура ошибок (коды, сообщения, поля), базовые договоренности про пагинацию и лимиты.
Цели публичного API обычно простые: интеграции с внешними сервисами и автоматизация у клиентов, партнерские сценарии (когда ваш продукт становится частью чужого решения) и ваши же приложения (мобильное, виджеты, расширения, фоновые задачи). Часто именно мобильное приложение первым заставляет навести порядок в контракте.
Когда вы проектируете публичный API с нуля, думайте не про «идеально», а про контракт, который понятен, предсказуем и расширяем без сюрпризов для тех, кто подключится первым.
Определяем пользователей и границы первого релиза
Хороший дизайн публичного API начинается не с эндпоинтов, а с людей. Один и тот же метод может быть удобным для вашей команды, но мучительным для партнера, который пишет интеграцию в одиночку и хочет предсказуемость.
Обычно у API три группы пользователей: внешние партнеры (маркетплейсы, бухгалтерии, интеграторы), ваши клиенты (чтобы тянуть свои данные в BI или CRM) и внутренняя команда (поддержка, аналитики, автоматизация). Для первого релиза выберите одну главную группу. Остальные получат пользу позже, но не должны диктовать архитектуру сейчас.
Дальше выберите 2-3 сценария, которые реально будут использоваться каждую неделю. Частая ошибка - сразу пытаться покрыть и чтение, и запись, и массовые операции, и вебхуки. Если вы не уверены, начните с чтения данных: оно проще, безопаснее и быстрее для проверки гипотез.
Чтобы не расползтись, зафиксируйте границы. В первом релизе держите 5-10 ключевых операций и пообещайте себе не добавлять «а еще один маленький метод» без явного запроса. Практичный минимум обычно сводится к следующему: список объектов с пагинацией, получение одного объекта по id, создание (только если без этого нельзя), обновление 1-2 самых частых полей и отдельный способ получить статус или события по объекту.
Параллельно задайте планку надежности. Не обязательно обещать SLA на старте, но клиентам нужны стабильные коды ошибок, понятные лимиты и предсказуемые изменения. Если API падает, партнер не «подождет», он просто отключит интеграцию.
Заранее решите, какие данные нельзя отдавать вообще (персональные, внутренние заметки, технические токены) и где нужен аудит. Минимум - логировать, кто и когда запрашивал чувствительные операции, и уметь быстро отозвать доступ. Это особенно важно, если вы выдаете ключи партнерам и даете экспорт наружу, например когда приложение собрано на TakProsto, а клиент просит интеграцию с внешней системой.
Базовые правила: ресурсы, методы и форматы ответов
Если вы делаете дизайн публичного API впервые, проще всего стартовать с REST. Он понятен большинству интеграторов, хорошо ложится на HTTP и не требует сложных решений на старте.
Начните с ресурсов (существительных), а не с действий. Хорошее правило: имя в множественном числе и стабильный идентификатор, который не меняется со временем. Например: /users, /projects, /invoices, а конкретный объект - /projects/{id}. Идентификатор лучше делать непрозрачным (например, UUID), чтобы вы могли менять внутреннюю структуру данных без риска.
Методы HTTP держите стандартными и предсказуемыми:
GET- получить список или один объект.POST- создать объект или запустить операцию.PATCH- частично обновить (например, сменить статус).DELETE- удалить или пометить как удаленный.
PUT лучше избегать, если вы не готовы поддерживать полную замену объекта без потери полей.
С форматом ответов есть два популярных варианта. Первый - отдавать сразу объект: при GET /projects/{id} вернуть JSON проекта. Это проще для клиентов.
Второй - использовать единый «конверт», например { "data": ..., "meta": ... }. Плюс в том, что вы без боли добавите метаданные (пагинацию, лимиты, предупреждения) и сохраните единый стиль. Минус - клиентам нужно писать чуть больше кода. Компромисс для первого релиза: для списков используйте конверт (там мета чаще нужна), а для одиночных объектов можно отдавать прямой объект.
Отдельно продумайте идемпотентность для создания. Реальный сценарий: партнер отправляет POST /invoices, сеть моргнула, он повторил запрос и получил два счета. Решение простое: поддержите заголовок Idempotency-Key (или поле в теле) и храните результат по ключу какое-то время.
Тогда повтор одного и того же запроса вернет тот же ответ, а не создаст дубль. Это резко снижает число спорных ситуаций и ручных разборов в поддержке.
Версионирование без боли: как не сломать клиентов
Версия нужна не «на всякий случай», а когда вы делаете ломающее изменение. Если вы расширяете API (добавили новое поле, новый эндпоинт, новый параметр с безопасным значением по умолчанию), это обычно можно выпускать без новой версии. Хороший дизайн публичного API как раз про то, чтобы большинство изменений были расширениями, а не поломками.
Ломающее изменение - это когда старый клиент перестает работать или начинает получать другой смысл данных. Примеры: вы переименовали поле, изменили тип (строка стала числом), поменяли формат дат, сделали обязательным то, что раньше было необязательным.
Самый простой вариант версионирования, который легче всего поддерживать и объяснять, - версия в пути: /v1/.... Заголовки и параметры тоже работают, но чаще дают больше путаницы: сложнее отлаживать, труднее быстро увидеть версию в логах и примерах. Если вы только начинаете, берите /v1 и не усложняйте.
Главное правило совместимости: новые поля добавлять можно, удалять нельзя. Точнее, клиент должен быть готов к тому, что в ответе появятся дополнительные поля, которые он игнорирует. Например, было:
{ "id": "123", "status": "paid" }
Стало:
{ "id": "123", "status": "paid", "paid_at": "2026-01-09T10:20:00Z" }
Такое изменение не ломает аккуратных клиентов.
Планируя v2, заранее решите, сколько времени вы будете поддерживать v1 и как будете предупреждать. Практичный минимум выглядит так: заранее объявить дату окончания поддержки и причины, добавить предупреждение в ответах (заголовок или поле в метаданных), дать короткую инструкцию миграции с примерами и обеспечить период, когда v1 и v2 работают параллельно. После дедлайна оставьте понятную ошибку и контакт для экстренных случаев.
Changelog и заметки о релизах сильно снижают боль у интеграторов. Пишите человеческим языком: что изменилось, кого касается, есть ли действия со стороны клиента. Даже если вы собираете API в TakProsto, дисциплина релиз-заметок и сроков поддержки важнее, чем выбор конкретного инструмента.
Пагинация: простые решения, которые не стыдно поддерживать
Пагинация нужна не для «красоты», а чтобы API не падал от одного запроса. Если отдавать все записи сразу, вы получите длинные ответы, рост времени ответа, высокую нагрузку на базу и неожиданные счета за трафик. Для клиента тоже плохо: он не может быстро показать первые результаты.
Для старта почти всегда достаточно одного выбора: offset или cursor. Offset (page, offset) проще объяснить, но он плохо переживает изменения данных: пока клиент листает, новые записи могут «сдвинуть» страницы, и часть элементов повторится или пропадет. Cursor-пагинация обычно надежнее для SaaS, потому что вы двигаетесь от известной точки вперед и сохраняете предсказуемость.
Сделайте единый формат, чтобы дизайн публичного API выглядел цельно во всех списках. Хороший минимум:
- В запросе: limit и cursor (опционально)
- В ответе: items, next_cursor, has_more
Важно зафиксировать порядок сортировки. Выберите один базовый ключ, например created_at по убыванию, и добавьте вторичный стабильный ключ (id), чтобы порядок был строгим. Тогда страницы не будут «прыгать» даже при одинаковых датах. Фильтры тоже должны быть частью контракта: если клиент меняет фильтр, курсор из старого запроса применять нельзя.
Пример для списка событий или транзакций (в духе «последние операции»):
// GET /v1/transactions?limit=50&cursor=eyJjcmVhdGVkX2F0IjoiMjAyNi0wMS0wOVQxMjoyMzowMFoiLCJpZCI6IjEwMjM0NSJ9
{
"items": [
{"id":"102345","created_at":"2026-01-09T12:23:00Z","amount":1200,"status":"paid"}
],
"next_cursor": "eyJjcmVhdGVkX2F0IjoiMjAyNi0wMS0wOVQxMjoyMjowMFoiLCJpZCI6IjEwMjM0NCJ9",
"has_more": true
}
Практичные детали, которые экономят время позже: задайте разумный default limit (например 20-50), поставьте верхнюю границу (например 100-200), и всегда возвращайте одинаковую структуру ответа, даже если items пустой.
Rate limits: защита сервиса и предсказуемость для клиентов
Rate limits нужны не только против злоупотреблений. Они делают интеграции предсказуемыми: клиент понимает, сколько запросов можно отправить, а вы защищаете базу и фоновые задачи от всплесков.
Сначала решите, что именно ограничивать. Обычно хватает 2-3 измерений: запросы в минуту (или в секунду) на API-ключ или пользователя, одновременные запросы (чтобы один клиент не забрал все воркеры), и отдельные лимиты для тяжелых эндпоинтов (экспорт, отчеты, поиск по большим наборам).
Дальше важно правильно сообщать об ограничении. При превышении возвращайте HTTP 429 Too Many Requests и понятный текст ошибки: что именно превышено и когда можно повторить. Добавьте заголовки, чтобы клиент мог заранее планировать нагрузку: например, RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset (или X-RateLimit-*, если так проще в вашей экосистеме). И обязательно используйте Retry-After, чтобы клиент не гадал.
Вместо «кирпича» лучше подсказать корректное поведение. Если запросы идут волнами, клиент делает экспоненциальный backoff (1с, 2с, 4с) и повторяет запрос после Retry-After. Для операций, которые можно отложить, можно принять задачу в очередь и вернуть быстрый ответ (например, статус «принято»), а результат отдать позже через отдельный эндпоинт.
Лимиты почти всегда отличаются по планам: free, pro, business, enterprise. Не обязательно выносить все цифры в маркетинг, но в документации API лучше быть точным: «в этом плане N запросов в минуту и M параллельных».
Чтобы не уйти в паранойю, начните с простого лимита на ключ плюс базовая защита от очевидных атак (подозрительные пики, слишком частые ошибки, запросы без auth). Если видите проблему, добавляйте точечные правила на конкретные эндпоинты, а не усложняйте весь API сразу.
Аутентификация и доступы: достаточно безопасно, но просто
Самая частая ошибка в дизайне публичного API на старте - сделать вход слишком сложным и отпугнуть первых интеграторов. Вторая ошибка - оставить все «на одном ключе» без ограничений и потом разгребать утечки и случайные удаления данных. Нужен простой минимум, который можно расширить.
Что выбрать на первом релизе
Для большинства молодого SaaS достаточно API key в заголовке запроса (например, Authorization: Bearer <key>). Это быстро объяснить, легко внедрить и удобно тестировать.
Выбор обычно такой:
- API key - лучший старт для сервер-сервер интеграций и внутренних партнеров.
- JWT - удобно, если у вас уже есть свои сессии и вы хотите единый формат токена, но не путайте это с полноценным OAuth2.
- OAuth2 - имеет смысл, когда вы отдаете доступ к данным «от имени пользователя» и нужен экран согласия, отзыв доступа и гранулярные разрешения.
Если сомневаетесь, начните с API key + скоупы. OAuth2 добавите позже, когда появятся реальные кейсы.
Как выдавать и хранить ключи
Показывайте ключ пользователю полностью только один раз при создании. Дальше отображайте только «хвост» (например, последние 4 символа) и дату создания. Храните на сервере не сам ключ, а его хэш, чтобы даже при утечке базы ключи не уехали «как есть». Дайте ротацию, чтобы можно было быстро заменить ключ без переписки с поддержкой.
Сразу разделите ключи по назначению: «серверный ключ для продакшена» и «тестовый ключ». Это снижает шанс, что кто-то случайно удалит реальные данные, пока отлаживает интеграцию.
Скоупы, аудит и тестовые окружения
Не усложняйте разрешения, но и не оставляйте все как admin. На старте обычно хватает трех уровней: read, write, admin (для управления ключами и доступами).
Для предсказуемости интеграций ведите аудит. Минимум, который стоит логировать: кто вызвал API (идентификатор ключа или клиента), какой эндпоинт и метод, результат (код ответа) и время, важные изменения (что создали, обновили, удалили), откуда пришел запрос (IP или идентификатор приложения).
Если вы работаете в режиме «быстро собрать и проверить гипотезу», как это часто бывает на takprosto.ai, такая простая модель безопасности особенно спасает: понятные ключи, скоупы и тестовый режим экономят недели поддержки.
Ошибки, статусы и совместимость: чтобы интеграции не ломались
Хороший дизайн публичного API заметен не тогда, когда все работает, а когда что-то пошло не так. Клиенту важно быстро понять: это ошибка данных, прав, лимитов или временный сбой. И еще важнее - можно ли безопасно повторить запрос.
Единый формат ошибок
Договоритесь об одном JSON-формате для всех ошибок, чтобы клиент не угадывал, где искать причину. Минимальный набор: стабильный code, понятное message, опциональные details (например, по полям) и request_id для поддержки.
{
"error": {
"code": "validation_error",
"message": "Некорректные данные",
"details": {
"email": "Неверный формат",
"age": "Должно быть >= 18"
},
"request_id": "req_01HXYZ..."
}
}
request_id стоит возвращать и в заголовке, и в теле. Тогда партнер присылает один идентификатор, а вы быстро находите запрос в логах.
HTTP-статусы, которые не путают клиентов
Лучше использовать небольшой, предсказуемый набор статусов:
200/201/204для успеха (204 когда нет тела)400для неверного запроса,401нет авторизации,403нет прав404ресурс не найден,409конфликт (например, дубль уникального значения)422удобно для ошибок валидации по полям (если вы готовы его поддерживать)429лимиты,500/503ошибки сервиса
Если сомневаетесь между 400 и 422, выберите одно правило и держитесь его везде.
Таймауты, ретраи и что можно повторять
Клиенты будут делать ретраи автоматически. Помогите им не создать дубли:
- безопасно повторять
GETиDELETE(если удаление идемпотентно) POSTповторять только с идемпотентным ключом или если вы явно это поддерживаете429и503можно повторять с задержкой,400/422повторять бессмысленно- возвращайте понятный текст и, по возможности, подсказку по паузе (например, через заголовки)
Совместимость: добавлять можно, ломать нельзя
Золотое правило: новые поля в ответах добавлять можно, менять смысл старых нельзя. Клиент должен игнорировать неизвестные поля, а сервер не должен падать, если клиент прислал лишнее. Обычно лучше тихо игнорировать незнакомые поля и строго валидировать только то, что вы реально поддерживаете.
Простой сценарий: партнер парсит ответ и сохраняет только id и status. Через месяц вы добавили status_reason. Если клиент написан аккуратно, интеграция не ломается, а новые данные он сможет начать использовать позже.
Документация и простой SDK: как выпустить без оверинжиниринга
Хорошая документация делает больше, чем половина «идеального» дизайн публичного API. Если человек может сделать первый запрос за 10 минут и понять ошибки без переписки с вами, интеграции начинают появляться сами.
Минимальный набор документации, который реально читают
Начните с короткого «быстрого старта». Не пишите длинные вводные, лучше покажите один рабочий пример запроса и ответа, а рядом объясните, где взять токен и какой базовый URL использовать.
Дальше описывайте каждый метод одинаковым шаблоном: что делает, какие параметры принимает, что вернет, какие ошибки возможны, и какие лимиты действуют. Примеры должны быть копируемыми и одинаковыми по стилю.
Обычно достаточно следующего набора: пример запроса (curl или один выбранный формат), пример успешного ответа, параметры и валидация (обязательный, тип, ограничения), список ошибок с понятными сообщениями, указание rate limit и правил пагинации.
Сценарии важнее справочника
Один справочник по ручкам не отвечает на вопрос «как решить мою задачу». Добавьте 2-3 коротких сценария, которые повторяют реальную интеграцию. Например: «создать объект, проверить статус, получить список и обновить». Это снижает число вопросов сильнее, чем дополнительные детали по каждому параметру.
Пример сценария: партнер получает заказы. Он создает доступ, делает запрос на список заказов с пагинацией, сохраняет cursor, затем раз в минуту догружает новые. Если получает 429, ждет время из заголовка и повторяет.
Простой SDK без магии
SDK на первом релизе должен быть тонкой оберткой над HTTP, а не отдельным «мини-продуктом». Достаточно одного клиента, конфига и единых ошибок.
Минимальная структура SDK:
- Client(baseUrl, token, timeout)
- методы-обертки: getOrders(params), createOrder(data) и т.п.
- единая обработка ошибок (включая тело ответа и requestId)
- встроенный ретрай только для 429 и сетевых сбоев (с лимитом попыток)
Не спешите с генераторами, сложными типами, авто-рефрешем токенов и «умными» моделями. Пока у вас 10-20 методов, ручной SDK проще поддерживать и легче объяснить. Если вы выпускаете API для партнеров и хотите быстрый старт, простота важнее «умности».
Пример из жизни: первый партнер просит интеграцию
У вас маленький SaaS для подписок. Первый партнер говорит: «Нам нужно раз в день выгружать платежи и сверять их со своей бухгалтерией». Он не просит «идеальный API на все случаи». Ему нужно надежно и предсказуемо забрать данные, не сломав ваш сервис.
Для v1 достаточно набора простых ресурсов вокруг платежей и клиентов. Например:
GET /v1/transactions(список)GET /v1/transactions/{id}(детали платежа)GET /v1/customers/{id}(кто платил, если нужно)GET /v1/balanceилиGET /v1/summary(итог за период, опционально)
Ключевой эндпоинт - список транзакций. Сделайте пагинацию и фильтр по дате сразу, иначе партнер упрется в лимиты или начнет тянуть «все за год».
Хороший минимум: GET /v1/transactions?created_from=2026-01-01&created_to=2026-01-31&limit=100&cursor=.... В ответе верните items, next_cursor (или has_more) и зафиксируйте сортировку, например по created_at от новых к старым. Тогда интегратор понимает, как безопасно догружать следующую страницу.
Лимиты нужны, чтобы защититься от массовых выгрузок, но не заблокировать бизнес клиента. Дайте понятный коридор, например 60 запросов в минуту на API-ключ и отдельный лимит на тяжелый список транзакций. Если лимит превышен, отвечайте 429 и добавляйте Retry-After, чтобы партнер мог корректно повторить.
Документировать сразу стоит: формат дат, правила пагинации, примеры ответа, ошибки (включая 429), и как делать «ежедневную сверку» (рекомендуемый запрос по диапазону дат). На потом можно оставить расширенные фильтры, вебхуки, экспорты в файлы и сложные отчеты.
Быстрый чеклист и следующие шаги
Перед релизом публичного API полезно пройтись по короткому списку и убедиться, что вы выпускаете не набор разрозненных ручек, а понятный контракт. Это экономит недели переписок с первым партнером и снижает риск поломок.
Чеклист перед v1
Проверьте эти пункты на реальных запросах, а не только «на бумаге»:
- Версия: выбран один способ (например, /v1) и описано, что считается breaking change.
- Пагинация: есть единый формат (limit + cursor или page + page_size) и понятные значения по умолчанию.
- Лимиты: возвращаются явные заголовки или поля с остатком и временем сброса, а сообщения об ошибке читаемые.
- Ошибки: единый JSON-формат (code, message, details) и стабильные статусы, плюс request_id в каждом ответе.
- Auth: понятная схема (API key или OAuth), описаны права и как безопасно хранить ключ.
Сделайте 10-15 тестовых запросов руками: успешный кейс, неверный токен, отсутствие обязательного поля, конфликт (409), и самый «тяжелый» список. Отдельно проверьте большие выборки: 10, 100, 1000 элементов, а также ситуацию, когда данных нет. Если где-то поведение «плавает», лучше зафиксировать и упростить сейчас.
Типичные ошибки, которые быстро портят впечатление: менять названия полей без версии, скрывать лимиты (клиент не понимает, что происходит), не возвращать request_id (сложно искать проблему по логам), смешивать разные форматы пагинации в разных методах.
Следующие шаги
Сначала зафиксируйте контракт: схемы запросов и ответов, примеры, список кодов ошибок. Затем соберите минимальный набор эндпоинтов v1 и черновик SDK (например, один клиент с 3-5 методами и обработкой ошибок).
Если нужно выпустить быстрее, в TakProsto можно начать с planning mode: описать ресурсы и сценарии, получить каркас API и документации, а потом стабилизировать контракт. Когда все совпало с ожиданиями партнеров, разверните и хостите сервис, при необходимости экспортируйте исходники и подключите кастомный домен, сохраняя возможность отката через снапшоты.
FAQ
Зачем SaaS вообще публичный API, если есть интерфейс?
Публичный API нужен, чтобы чужие системы могли стабильно работать с вашим сервисом без UI: интеграции с бухгалтерией, CRM/BI, партнёрские сценарии, ваши мобильные приложения и фоновые задачи.
Он ценен тем, что становится контрактом: предсказуемые ответы, ошибки и правила обновлений сильно уменьшают поддержку и повышают доверие интеграторов.
С чего начать v1: какие методы включать в первый релиз?
Для v1 выберите одну главную аудиторию (партнёры или клиенты, или внутренняя команда) и 2–3 сценария, которые реально будут использоваться каждую неделю.
Практичный старт — чтение данных:
- список объектов с пагинацией
- получение объекта по
id - фильтр по дате/статусу (самый нужный)
Запись (create/update) добавляйте только если без неё сценарий не работает.
Как не расползтись и не сделать слишком много эндпоинтов?
Держите границы жёстко: 5–10 ключевых операций и никаких «ещё одной маленькой ручки» без реального запроса.
Хороший ориентир:
- один ресурс = один набор эндпоинтов
- один формат пагинации во всех списках
- один формат ошибок
Лучше выпустить маленький, но стабильный контракт, чем большой, который меняется каждую неделю.
REST или что-то другое — что выбрать для первого публичного API?
Самый понятный старт — REST: ресурсы как существительные во множественном числе (/projects, /invoices) и объект как /projects/{id}.
По методам:
GET— чтениеPOST— создание или запуск операцииPATCH— частичное обновлениеDELETE— удаление/soft-delete
PUT лучше не трогать в начале, если вы не готовы к «полной замене объекта» без сюрпризов.
Нужен ли «конверт» вида {data, meta} или можно отдавать объект напрямую?
Дефолт — JSON. Для ответов проще всего:
- одиночный объект: вернуть сам объект
- списки: вернуть «конверт» с метаданными
Например, для списков удобно стандартизировать:
- в ответе:
items,next_cursor,has_more - в запросе:
limit,cursor
Так вы сможете добавлять метаданные (лимиты, предупреждения) без ломки клиентов.
Какую пагинацию выбрать, чтобы данные не дублировались и не пропадали?
Сделайте cursor-пагинацию как основной вариант для SaaS: она обычно стабильнее, чем offset, когда данные меняются во время листания.
Минимальный контракт:
- запрос:
limit(с дефолтом) и опциональноcursor - ответ:
items,next_cursor,has_more
Обязательно зафиксируйте порядок сортировки (например, created_at desc + вторичный id), иначе страницы будут «прыгать».
Когда нужна версия API и как не сломать клиентов при изменениях?
Версионируйте только ломающее. Самый простой и понятный способ — версия в пути: /v1/....
Правила совместимости:
- добавлять новые поля можно
- удалять/переименовывать поля нельзя без новой версии
- нельзя менять типы и смысл полей (строка → число, другой формат дат)
Планируя v2, заранее обозначьте срок поддержки v1 и период параллельной работы.
Как правильно сделать rate limits и что возвращать при превышении?
Дефолт — лимитировать по API-ключу: запросы в минуту/секунду + (опционально) лимит параллельных запросов и отдельные ограничения для тяжёлых эндпоинтов.
При превышении:
- возвращайте
429 Too Many Requests - добавляйте
Retry-After - в тексте ошибки пишите, что именно превышено и когда можно повторить
Клиентам проще жить, когда лимиты не скрыты и поведение предсказуемо.
Что выбрать для авторизации в публичном API: API key, JWT или OAuth2?
Для первого релиза обычно достаточно API key в заголовке Authorization: Bearer <key>.
Минимальные практики:
- показывайте ключ полностью только один раз
- храните на сервере хэш ключа
- дайте ротацию ключей
- разделяйте тестовый и продакшен доступ
- добавьте простые скоупы (
read,write,admin)
OAuth2 имеет смысл позже, когда реально нужен доступ «от имени пользователя» и отзыв разрешений.
Как оформить ошибки и статусы, чтобы интеграторам было легко отлаживать?
Сделайте единый JSON-формат ошибок и ограниченный, понятный набор HTTP-статусов.
Рекомендуемый минимум в ошибке:
code(стабильный)message(человеческий)details(ошибки по полям, если есть)request_id(для поддержки)
По ретраям: безопасно повторять GET, а POST — только с идемпотентностью (например, Idempotency-Key), чтобы не создавать дубли.