Справочник API

REST API
Стройте на основе своих ссылок.

Аутентифицируйтесь с помощью API-токена, получайте список ссылок и создавайте их, читайте переходы и подписывайтесь на вебхуки.

Базовый URL https://letl.ink/api/v1 Только HTTPS

Тарифы Growth Ultimate
На этой странице

Начало работы

REST API доступен на тарифах Growth и Ultimate. Запросы отправляются на https://letl.ink/api/v1 по HTTPS. Рабочее пространство на Free или Solo получает 403 plan_required от /api/v1; эндпоинт MCP отдельно ограничен тарифом Ultimate.

  1. Откройте Интеграции → REST API (только для владельцев и администраторов рабочего пространства).
  2. Создайте токен. У него нет срока действия.
  3. Храните его как пароль. Один токен обслуживает REST API, MCP-сервер и все платформы автоматизации, подключённые к этому рабочему пространству, поэтому его пересоздание сразу делает недействительными их все.

Участники могут скопировать существующий токен, если он создан; менять его могут только владельцы и администраторы.

Аутентификация

Передавайте токен как X-API-KEY или как Authorization: Bearer. Оба варианта принимаются в /api/v1 и /mcp. Запрос совсем без токена возвращает 401 unauthenticated.

GET Кто я?
curl -H "X-API-KEY: YOUR_TOKEN" \
  https://letl.ink/api/v1/auth/me

Эквивалентный заголовок: Authorization: Bearer YOUR_TOKEN

Соглашения

Это действует для всех эндпоинтов ниже.

Формат ответа

Каждый успешный ответ — это JSON-объект с ключом data и ключом meta, если о результате есть что сказать: пагинация, итоговый период дат, включена ли география. Каждая ошибка — объект с ключом error, содержащим постоянный code, понятное человеку message и иногда details. Ориентируйтесь на code, а не на текст сообщения.

Формы
// success
{ "data": { … }, "meta": { … } }

// failure
{ "error": { "code": "invalid_parameter",
             "message": "sort must be one of: created_at, clicks.",
             "details": { "parameter": "sort" } } }

Пагинация

Эндпоинты списков принимают limit (1–200, по умолчанию 50) и offset. Блок meta сообщает total, has_more и next_offset, так что листать можно без вычислений: вызывайте с offset = meta.next_offset, пока has_more не станет false.

Перебор всех ссылок
offset=0
while [ "$offset" != "null" ]; do
  page=$(curl -s -H "X-API-KEY: $TOKEN" \
    "https://letl.ink/api/v1/links?limit=200&offset=$offset")
  echo "$page" | jq -c '.data[]'
  offset=$(echo "$page" | jq -r '.meta.next_offset')
done

Сортировка, фильтрация и поиск

GET /links принимает sort (created_at, clicks, short_code, title, updated_at), direction (asc/desc), search (ищет по короткому коду, названию и целевому URL), type, archived, favorite и domain. Неизвестные значения не игнорируются молча, а отклоняются с ошибкой invalid_parameter и списком допустимых.

Периоды дат

start_date и end_date задаются в формате YYYY-MM-DD и включают оба дня. Период обрезается до срока хранения данных на вашем тарифе: если запросить год истории на тарифе с хранением 30 дней, вернутся имеющиеся 30 дней, а meta.date_range покажет фактически использованный период.

Ограничения частоты запросов

600 запросов в минуту на аккаунт в скользящем окне. Каждый ответ содержит X-RateLimit-Limit, X-RateLimit-Remaining и X-RateLimit-Reset (Unix-метка времени). При превышении лимита возвращается 429 rate_limited с заголовком Retry-After в секундах. Выжидайте указанное в Retry-After время, а не повторяйте запрос сразу.

Заголовки в каждом ответе
X-RateLimit-Limit: 600
X-RateLimit-Remaining: 586
X-RateLimit-Reset: 1789000816

Версионирование

Версия указывается в пути. Добавление поля в ответ или нового необязательного параметра не считается критическим изменением и может произойти в любой момент, поэтому разбирайте ответы с запасом и игнорируйте незнакомые ключи. Всё, что изменило бы или удалило существующее поле, выходит под новым префиксом версии.

Эндпоинты

Аккаунт

GET /me Проверка подключения

Пользователь, рабочее пространство, тариф и все права, которые он даёт. Самый простой способ проверить токен и узнать, что разрешено аккаунту, прежде чем пытаться это сделать.

GET cURL
curl -H "X-API-KEY: YOUR_TOKEN" \
  https://letl.ink/api/v1/me
Ответ 200
{
  "data": {
    "user": { "id": 1, "email": "[email protected]", "name": "You" },
    "workspace": { "id": 12, "name": "My Workspace", "block_bots": true },
    "plan": {
      "id": "growth",
      "name": "Growth",
      "limits": {
        "links": 200,
        "monthly_clicks": 100000,
        "custom_domains": 3,
        "qr_codes": 30,
        "retention_days": 365
      },
      "features": {
        "api": true,
        "webhooks": true,
        "integrations": true,
        "ai_access": false,
        "geo_analytics": false,
        "smart_link_types": true
      }
    },
    "base_domain": "letl.ink"
  }
}
GET /usage

Какая часть каждого лимита израсходована в этом расчётном месяце. Используйте это, чтобы предупредить клиента до того, как он упрётся в потолок отслеживаемых кликов: после него перенаправление продолжает работать, но клики перестают записываться.

Ответ 200
{
  "data": {
    "links": { "used": 42, "limit": 200, "remaining": 158, "percent_used": 21.0 },
    "clicks_this_month": { "used": 8140, "limit": 100000, "remaining": 91860, "percent_used": 8.1 },
    "custom_domains": { "used": 1, "limit": 3, "remaining": 2, "percent_used": 33.3 },
    "qr_codes": { "used": 4, "limit": 30, "remaining": 26, "percent_used": 13.3 }
  },
  "meta": { "plan": "growth", "period": "2026-09" }
}
GET /domains

Собственные домены рабочего пространства и статус DNS-проверки каждого из них. В поле domain при создании ссылки можно указывать только подтверждённые домены.

GET /integrations

Все интеграции продукта: входит ли каждая в ваш тариф, подключена ли она в этом рабочем пространстве и как идёт доставка — последний статус, последняя ошибка и число неудач подряд. Удобно для мониторинга множества рабочих пространств.

Ссылки

Аналитика

GET /analytics/summary

Ключевые показатели всего рабочего пространства: клики за всё время и за период, уникальные посетители, сканирования QR, пиковый день, а также распределения по часам суток и дням недели. Принимает days (по умолчанию 30, ограничено сроком хранения данных).

Ответ 200 (сокращённо)
{
  "data": {
    "window_days": 30,
    "clicks": {
      "total": 24592, "window": 8140, "last_7_days": 2103,
      "daily_average": 271,
      "peak_day": { "date": "Sep 02", "clicks": 612 }
    },
    "visitors": { "unique_total": 18204, "unique_window": 6021 },
    "qr": { "scans_total": 940, "scans_window": 310 },
    "timing": {
      "by_hour": [12, 4, 2, …],
      "by_weekday": [1420, 1610, 1585, 1490, 1310, 480, 245],
      "peak_hour": { "peakHour": 14, "peakValue": 812, "label": "14:00–14:59" },
      "peak_weekday": { "peakDay": 1, "peakValue": 1610, "label": "Tuesday" }
    },
    "links": { "active": 42, "limit": 200 }
  },
  "meta": { "plan": "growth", "geo_included": true, "retention_days": 365 }
}
GET /analytics/timeseries

Число кликов по дням, готовое для графика. Передайте link_id, чтобы ограничиться одной ссылкой, и days, чтобы задать период. Дни без трафика возвращаются с нулём, а не пропускаются, поэтому ряд всегда непрерывен.

Ответ 200
{
  "data": [
    { "date": "Sep 04", "clicks": 22 },
    { "date": "Sep 05", "clicks": 11 }
  ],
  "meta": { "window_days": 7, "total": 33, "link_id": null }
}
GET /analytics/breakdown

Клики, сгруппированные по одному измерению, с долей каждой строки от общего числа.

Параметры запроса

  • dimension string

    channel, referrer, device, browser, os, brand, variation или country. Для browser, os, brand и variation нужен link_id.

    Обязательно
  • link_id integer

    Ограничить одной ссылкой. Не указывайте для всего рабочего пространства.

    Необязательно
  • limit integer

    1–100. По умолчанию 10.

    Необязательно
Ответ 200
{
  "data": [
    { "label": "Search", "value": 215, "share": 33.1 },
    { "label": "Social", "value": 201, "share": 30.9 },
    { "label": "Direct / none", "value": 142, "share": 21.8 }
  ],
  "meta": { "dimension": "channel", "link_id": 49, "total": 650 }
}
GET /events С пагинацией

Поток сырых переходов по всем ссылкам рабочего пространства, сначала новые. Это современная замена GET /visits: те же данные, стандартная обёртка, поддержка limit и offset.

Ответ 200
{
  "data": [
    {
      "id": 2691,
      "link_id": 49,
      "short_code": "spring",
      "occurred_at": "2026-09-10T22:45:33+00:00",
      "ip_address": "203.0.113.7",
      "referrer": "https://twitter.com/",
      "browser": "Chrome",
      "os": "iOS",
      "device_type": "mobile",
      "device_brand": "Apple",
      "country": "US",
      "variation": "control"
    }
  ],
  "meta": { "limit": 50, "returned": 50, "geo_included": true }
}

Вебхуки

GET /hooks

Эндпоинты вебхуков, зарегистрированные в этом аккаунте.

POST /hooks

Зарегистрируйте HTTPS-эндпоинт, чтобы получать JSON при каждом записанном клике. До 25 на аккаунт. Неудачные доставки повторяются автоматически.

JSON-тело

  • target_url

    Должен начинаться с https://.

    Обязательно
  • event

    По умолчанию link_visited — сейчас это единственное событие.

    Необязательно
POST cURL
curl -X POST https://letl.ink/api/v1/hooks \
  -H "X-API-KEY: YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"target_url": "https://example.com/hooks/clicks"}'
DELETE /hooks/{id}

Удаляет эндпоинт вебхука. POST /hooks/subscribe и DELETE /hooks/unsubscribe/{id} остаются доступными для REST Hook-потока Zapier.

Устаревший

GET /auth/me Устаревший

Изначальная проверка подключения: возвращает ваш ID пользователя, email и ID/название рабочего пространства в виде плоского объекта. GET /me возвращает те же данные плюс лимиты вашего тарифа и флаги функций — для нового кода это лучший выбор.

GET cURL
curl -H "X-API-KEY: YOUR_TOKEN" \
  https://letl.ink/api/v1/auth/me
Ответ 200
{
  "id": 1,
  "email": "[email protected]",
  "workspace_id": 12,
  "workspace_name": "My Workspace"
}
GET /visits Устаревший

50 последних переходов по всему рабочему пространству в виде голого JSON-массива — без обёртки — с полями visit_id, visit_time, link_shortcode и visitor_*. Именно его читает шаг Zapier REST Hook "perform list", поэтому его формат заморожен. Для новых задач используйте GET /events.

POST /hooks/subscribe Zapier

Подписка REST Hook. Принимает target_url и необязательный event (по умолчанию link_visited) и возвращает созданную подписку в плоском виде, без обёртки. POST /hooks — та же операция в стандартном формате.

DELETE /hooks/unsubscribe/{id} Zapier

Отписка REST Hook, возвращает {"success": true}. DELETE /hooks/{id} — эквивалент в стандартном формате.

Ошибки

  • 400 invalid_request

    Тело не является JSON-объектом, или отсутствует обязательное поле.

  • 400 invalid_parameter

    Параметр запроса имеет неверный формат или недопустимое значение. details.parameter указывает, какой именно.

  • 401 unauthenticated

    Токен не передан. Добавьте X-API-KEY или Authorization: Bearer.

  • 401 invalid_token

    Токен не соответствует ни одному аккаунту. Возможно, он был перевыпущен.

  • 403 plan_required

    Тариф аккаунта не включает этот эндпоинт или функцию. details.upgrade_url указывает на страницу оплаты.

  • 403 forbidden

    Аутентификация пройдена, но действие не разрешено — например, участник пытается выполнить действие, доступное только владельцу.

  • 404 not_found

    В этом рабочем пространстве нет такого ресурса. Id из другого рабочего пространства возвращаются как «не найдено», а не «запрещено».

  • 409 conflict

    Запрос конфликтует с текущим состоянием — достигнут лимит вебхуков или у аккаунта ещё нет рабочего пространства.

  • 422

    Возвращается POST /links/bulk, если все строки пакета завершились ошибкой. Смотрите meta.errors.

  • 429 rate_limited

    Более 600 запросов за последнюю минуту. Подождите Retry-After секунд.

Устаревшие эндпоинты GET /visits и GET /links/{id}/visits возвращают голый JSON-массив со своими названиями полей и заморожены ради уже построенных на них интеграций Zapier. В новом коде используйте GET /events и GET /links/{id}/events — они используют стандартную обёртку и поддерживают пагинацию.

Остались вопросы?

Мы готовы помочь. Напишите нам, и мы ответим как можно скорее.