Skip to content
Язык: Русский

API для разработчиков

Лимиты запросов: все лимиты и заголовки

Обновлено 1 ноября 2026 г. 5 мин чтения

Сколько запросов в минуту допускает LinkHarbor API, каким может быть размер запроса, как читать заголовки и как правильно обработать 429.

Чтобы API оставался быстрым для всех, LinkHarbor ограничивает число запросов в минуту. Лимиты не зависят от вашего тарифа — месячной квоты на вызовы API нет. В этой статье перечислены все лимиты и показано, как ваш код может аккуратно их обрабатывать.

Как считаются запросы

  • Каждый запрос попадает ровно в один бакет, например user для аутентифицированных запросов или token для обмена токена
  • Счёт идёт по полным минутам (UTC); с каждой новой минутой счётчик начинается с нуля
  • В зависимости от бакета API считает по рабочему пространству, по человеку, по подключению или по IP-адресу
  • Запросы, которые отклоняются уже на проверке токена, области доступа или схемы (401, 403 insufficient_scope, 400, 422 validation_failed из проверки схемы), не учитываются; более поздние ошибки, например 403 feature_not_available, учитываются

Все лимиты

Бакет (X-RateLimit-Bucket)ЛимитСчитается поПрименяется к
user600 в минутуРабочему пространству для токенов от API-ключей (все ключи и AI-инструменты рабочего пространства вместе); человеку при входе в панель управленияВсем аутентифицированным запросам к /v1, у которых нет собственного бакета
token30 в минутуIP-адресуPOST /v1/token
public120 в минутуIP-адресуЗапросам к API без аутентификации, например GET /v1/plans и GET /v1/pages/available
callback120 в минутуIP-адресуPOST /v1/callbacks/sale
internal120 в минутуIP-адресуIngest-URL интеграции «Custom (API)» (POST /v1/ingest/{id})
mcp120 в минутуAI-подключению (токену доступа)MCP-серверу по адресу /mcp
oauth300 в минутуIP-адресуВходу через OAuth и discovery для AI-ассистентов
auth30 в минутуIP-адресуВходу, регистрации, сбросу пароля (панель управления и приложение)
refresh120 в минутуIP-адресуПродлению сессии в панели управления
discover60 в минутуЧеловеку, без входа — IP-адресуРазделу «Обзор» в приложении
subs_read120 в минутуЧеловеку, без входа — IP-адресуЧтению ваших подписок (приложение)
subs_write20 в минутуЧеловеку, без входа — IP-адресуИзменению ваших подписок (приложение)

Один бюджет на рабочее пространство

Все токены от API-ключей одного рабочего пространства делят 600 запросов в минуту — сколько бы ключей вы ни создали. Сюда же засчитываются инструменты подключённых AI-ассистентов, потому что они вызывают API внутри. Поэтому больше ключей не означает больший лимит. Если большая нагрузка распределена по нескольким программам, рассчитывайте на общий бюджет.

Заголовки

X-RateLimit-Limit
Лимит бакета в минуту, например 600.
X-RateLimit-Remaining
Сколько запросов осталось в текущей минуте.
X-RateLimit-Bucket
Бакет, в который попал запрос, например user.
Retry-After
Только при 429 rate_limited: число секунд до начала следующей минуты. При api_key_locked время ожидания указано в details.retry_after_sec.

Когда лимит достигнут

Когда лимит достигнут, API отвечает HTTP 429 с таким телом. Код rate_limited неизменен, а текст в message приходит на языке аккаунта (см. Формат ошибок и коды ошибок), здесь на английском:

Ответ при 429
HTTP/1.1 429 Too Many Requests
Retry-After: 37
X-RateLimit-Limit: 600
X-RateLimit-Remaining: 0
X-RateLimit-Bucket: user

{ "error": "rate_limited", "message": "Too many requests. Please try again in a moment.", "retry_after_sec": 37 }
  1. Прочитайте Retry-After или retry_after_sec
  2. Подождите как минимум столько секунд плюс немного случайного времени, чтобы не все процессы стартовали одновременно
  3. Повторите запрос, но не больше нескольких раз
  4. Если 429 приходит снова и снова, растяните работу во времени
Node.js: кеширование токена и обработка 429
const BASE = 'https://dock.at-harbor.com';
let cached = null; // { token, expiresAt }

async function getToken() {
  if (cached && Date.now() < cached.expiresAt - 60_000) return cached.token;
  const res = await fetch(BASE + '/v1/token', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      key_id: process.env.LH_KEY_ID,
      key_secret: process.env.LH_KEY_SECRET,
    }),
  });
  if (!res.ok) throw new Error('Token exchange failed: HTTP ' + res.status);
  const data = await res.json();
  cached = { token: data.token, expiresAt: Date.now() + data.expires_in * 1000 };
  return cached.token;
}

async function lh(method, path, body, attempt = 0) {
  const headers = { Authorization: 'Bearer ' + (await getToken()) };
  if (body !== undefined) headers['Content-Type'] = 'application/json';

  const res = await fetch(BASE + path, {
    method,
    headers,
    body: body === undefined ? undefined : JSON.stringify(body),
  });

  if (res.status === 429 && attempt < 3) {
    const wait = Number(res.headers.get('Retry-After') ?? 5);
    await new Promise((r) => setTimeout(r, (wait + Math.random() * 2) * 1000));
    return lh(method, path, body, attempt + 1);
  }
  if (res.status === 401 && attempt === 0) {
    cached = null; // токен истёк — ещё одна попытка с новым токеном
    return lh(method, path, body, attempt + 1);
  }
  const data = await res.json().catch(() => null);
  if (!res.ok) throw new Error((data && data.error) + ' (HTTP ' + res.status + ')');
  return data;
}

// Пример: const { items } = await lh('GET', '/v1/pages');

Советы для больших объёмов данных

  • Кешируйте токен и обновляйте его только незадолго до истечения 15 минут
  • Следите за X-RateLimit-Remaining и замедляйтесь, когда значение становится маленьким
  • Отправляйте запросы друг за другом, а не через десятки параллельных соединений
  • Загружайте списки один раз и фильтруйте их локально, вместо того чтобы запрашивать каждую страницу отдельно
  • Растягивайте большие синхронизации на несколько минут
  • Повторяйте запросы при ошибках 5xx с растущими паузами, но не при ошибках 4xx

Другие лимиты

ЧтоЛимитОтвет
Неудачные входыБлокировка после 5, 10 и 20 неудачных попыток на адрес электронной почты и IP: 60 секунд, 5 минут, 30 минут429 account_locked
Отправка форм10 в час с одного IP-адреса, по всем формам вместе429 form_rate_limited

Неудачные попытки обмена токена временно блокируют ключ и IP, см. API-ключи и токены. Push-уведомления вы отправляете только в панели управления; их лимиты описаны в статье Подписчики и push-уведомления.

Лимиты контента вашего тарифа — страницы, ссылки, блоки, короткие ссылки и так далее — это не лимиты запросов, а фиксированные ограничения. Они перечислены в статье Сравнение тарифов.

Размер запроса

Каждое тело измеряется до того, как API его прочитает. Допустимый размер зависит от эндпоинта:

ЭндпоинтыМаксимальное тело
Вход, регистрация и другие вызовы без аутентификации под /v1/auth, POST /v1/token, POST /v1/oauth/exchange, OAuth-эндпоинты для AI-ассистентов под /oauth и согласие для них, POST /v1/ingest/{id}, оформление платежа в блоке «Поддержать», вызовы подписок в приложении (подписки, коллекции, push-устройства), переключение рабочих пространств, POST /v1/auth/logout-all, удаление аккаунта, настройка двухфакторной аутентификации, принятие приглашения, активация реферальной программы и шаг возврата при подключении интеграции64 КБ
Все остальные JSON-эндпоинты, включая отправку формы на странице1 МБ
POST /v1/webhooks/{id}/verify-signature и MCP-сервер по адресу /mcp2 МБ
Загрузка файлов (POST /v1/assets и POST /v1/pages/{id}/uploads, multipart/form-data)11 МБ на весь запрос, сам файл не больше 10 МБ

Если тело больше, API отвечает 413 request_too_large; лимит указан в details.max_bytes и details.max_kb. Слишком большой файл при загрузке приводит к 413 payload_too_large. Эндпоинты без тела, например вызовы DELETE, не принимают тело или принимают только {}.

Ответ при 413
HTTP/1.1 413 Payload Too Large

{ "error": "request_too_large", "message": "…", "details": { "max_bytes": 65536, "max_kb": 64 }, "request_id": "…" }

Ограничения есть и внутри тела: у каждого текста есть максимальная длина, у каждого списка — максимальное число элементов, а неизвестные поля отклоняются. Значения для каждого эндпоинта перечислены в справочнике API. Нарушение приводит к 422 validation_failed, а details называет поле, правило и лимит. Путь и строка запроса вместе могут занимать не больше 16 КБ.

Ничего не нашли?

Напишите, что вам нужно, и наша служба поддержки поможет вам лично.

Написать в поддержку