pik.li pik.li
Версия 1 · стабильная

API pik.li

Создавайте и редактируйте короткие ссылки и следите за их статистикой прямо из своего кода. REST API с JSON на входе и на выходе, отдельный ключ для каждой интеграции, а каждый эндпоинт описан здесь с примером запроса и реальным ответом.

Получить ключ API OpenAPI 3.1
Базовый URLhttps://pik.li/api/v1

Введение

API pik.li позволяет вашим программам делать то же, что и панель управления: создавать и редактировать короткие ссылки, получать их статистику, просматривать ваши теги и домены и управлять вебхуками. API работает по HTTPS, и все пути начинаются с базового URL, указанного выше.

  • Тела запросов отправляйте в формате JSON с заголовком Content-Type: application/json; параметры запросов GET передаются в строке запроса. Ответы всегда приходят в JSON в кодировке UTF-8 — кроме QR-кода, который возвращается изображением.
  • Версия входит в путь (/api/v1), и каждый ответ содержит заголовок X-Api-Version: 1. Со временем в ответах v1 могут появляться новые поля: пусть ваш код просто игнорирует незнакомые.
  • Дата и время передаются строками ISO 8601 со смещением часового пояса (2026-09-23T10:15:42.118+02:00); даты без времени выглядят так: 2026-09-23.
  • Идентификаторы — целые числа. Ссылку можно также найти по её короткому URL через GET /links/lookup.

Быстрый старт

  1. Создайте ключ в панели управления (см. Создание ключа) и сохраните его в переменной окружения.
  2. Проверьте, что всё работает: GET /me возвращает ваш аккаунт и план.
  3. Создайте свою первую короткую ссылку с помощью POST /links.
Терминал
export PIKLI_KEY="pk_live_…"

curl https://pik.li/api/v1/me \
  -H "Authorization: Bearer $PIKLI_KEY"

curl -X POST https://pik.li/api/v1/links \
  -H "Authorization: Bearer $PIKLI_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url":"https://example.com/a/very/long/page"}'

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

Для каждого вызова, кроме GET /ping, нужен ключ API. Он передаётся в заголовке Authorization как bearer-токен.

Заголовок
Authorization: Bearer pk_live_3kZ9vQeX1mT0bR7yNw2aLp4sUc8dHf6J

Подойдёт и заголовок X-API-Key. Параметр запроса api_key всё ещё принимается ради старых клиентов, но лучше его не использовать: адреса запросов попадают в логи и в историю браузера.

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

Обращайтесь с ключом как с паролем. Храните его на своём сервере и никогда не помещайте в код, который выполняется в браузере, или в приложение, которое вы распространяете. Если ключ утёк, отзовите его и создайте новый: это займёт десять секунд.

Создание ключа

  1. Войдите в аккаунт и откройте раздел API и ключи в панели управления.
  2. Дайте ключу название, по которому будет понятно, где он используется (например, CRM — продакшен), и нажмите Сгенерировать ключ.
  3. Сразу скопируйте ключ: панель показывает его только один раз.
  4. Одновременно можно иметь до 10 активных ключей. Для каждого страница показывает, когда он использовался в последний раз и сколько запросов сделал, и позволяет его отозвать: начиная со следующего вызова отозванный ключ получает 401 unauthorized.

Кто может использовать API

API входит в платные планы (Premium и Business). В план Base он не входит, если только команда pik.li не включит его для бесплатного плана или для вашего аккаунта. В любом случае у аккаунта должен быть подтверждённый e-mail, и он не должен быть заблокирован или приостановлен.

Если какое-то из этих условий не выполнено, API отвечает api_disabled, email_unconfirmed или account_blocked.

Base

Только если включит команда
Запросов в минуту
60
Новые ссылки
3 в день
Ссылок в пакетном вызове
10
Срок хранения статистики
90 дн.
Вебхуки
Нет

Premium

API включён
Запросов в минуту
600
Новые ссылки
2 500 в месяц
Ссылок в пакетном вызове
100
Срок хранения статистики
730 дн.
Вебхуки
Да

Business

API включён
Запросов в минуту
3 000
Новые ссылки
10 000 в месяц
Ссылок в пакетном вызове
100
Срок хранения статистики
1 095 дн.
Вебхуки
Да

Это стандартные значения каждого плана, взятые напрямую из текущих настроек цен. Ваши собственные значения — с учётом изменений, которые команда внесла для вашего аккаунта, — возвращают GET /limits и GET /me.

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

Каждый ключ может выполнить определённое число запросов в минуту, которое зависит от плана (см. таблицу выше). Счётчик обнуляется в начале каждой минуты.

X-RateLimit-Limit
сколько запросов в минуту разрешено этому ключу
X-RateLimit-Remaining
сколько запросов осталось в текущей минуте
X-RateLimit-Reset
когда счётчик обнулится, в виде времени Unix в секундах
Retry-After
только в ответе 429: сколько секунд нужно подождать

При превышении лимита API отвечает 429 с ошибкой rate_limited и заголовком Retry-After. Подождите указанное время и продолжайте: немедленные повторы только съедают лимит следующей минуты.

Ответ
HTTP/1.1 429 Too Many Requests
Content-Type: application/json; charset=utf-8
Retry-After: 18
X-RateLimit-Limit: 600
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1790151360

{
  "error": {
    "code": "rate_limited",
    "message": "Too many requests. Check the X-RateLimit-* headers and retry after the window resets.",
    "details": { "limit": 600, "period_seconds": 60, "resets_at": "2026-09-23T08:16:00Z" },
    "docs_url": "https://pik.li/help#error-rate_limited"
  },
  "request_id": "2b8e51d0-7a4c-4f1e-9d3b-5c6a0e8f7d21"
}
  • Вызовы без ключа ограничены: не более 30 в минуту с каждого IP-адреса (GET /ping не учитывается).
  • Кроме того, с каждого IP-адреса можно отправить на pik.li в целом не более 600 запросов в минуту, независимо от плана.

Ошибки

На неудачный запрос всегда приходит ответ одинаковой структуры, для любого эндпоинта, с HTTP-статусом, соответствующим проблеме.

Ответ
{
  "error": {
    "code": "quota_exceeded",
    "message": "Link quota reached: 2500 links per month on your plan.",
    "details": { "limit": 2500, "used": 2500, "period": "month", "resets_at": "2026-09-30T22:00:00Z" },
    "docs_url": "https://pik.li/help#error-quota_exceeded"
  },
  "request_id": "6f1c2a9e-4b0d-4c55-9a51-8f2f0d7c1e3b"
}
code
Стабильный идентификатор для вашего кода. Никогда не меняется и не переводится.
message
Фраза для людей на языке запроса (см. «Языки»).
details
Структурированные данные, когда они полезны: неверные поля, допустимые значения, параметры квоты. Присутствует не всегда.
docs_url
Ссылка на описание этого кода на этой странице.
request_id
Идентификатор запроса; он же приходит в заголовке X-Request-Id. Указывайте его, когда пишете в поддержку.

Любой эндпоинт, которому нужен ключ, может также ответить unauthorized account_blocked email_unconfirmed api_disabled rate_limited: в описании каждого эндпоинта они не повторяются.

unauthorized401
Ключ отсутствует, неверен или отозван. Проверьте заголовок Authorization и убедитесь, что ключ по-прежнему значится активным в панели управления.
account_blocked403
Аккаунт заблокирован, приостановлен или деактивирован. API остаётся закрытым, пока аккаунт снова не станет активным.
email_unconfirmed403
E-mail аккаунта ещё не подтверждён. Перейдите по ссылке из письма с подтверждением и повторите попытку.
api_disabled403
API не входит в план этого аккаунта или отключён для него. См. «Кто может использовать API».
forbidden403
Этот ключ не может выполнить это действие. зарезервирован
feature_required402
План не включает функцию, которая нужна этому эндпоинту. details.feature называет её, а details.plan — первый план, в котором она есть.
quota_exceeded402
Дневная или месячная квота ссылок исчерпана. В details есть limit, used, period и resets_at — момент, когда снова можно будет создавать ссылки.
not_found404
Ресурс не существует, был удалён или принадлежит другому аккаунту.
conflict409
Запрос конфликтует с текущим состоянием ресурса. зарезервирован
invalid422
Часть данных не прошла проверку: details.fields перечисляет проблемы по полям, details.messages — в виде готовых фраз.
domain_not_allowed422
Ваш аккаунт не может использовать этот домен. Список доступных возвращает GET /domains.
bad_request400
Не хватает обязательного параметра; его название указано в details.parameter.
invalid_json400
Тело запроса не является корректным JSON. Проверьте кавычки и заголовок Content-Type.
invalid_parameter400
Параметр имеет значение, которое эндпоинт не знает. Сообщение называет параметр, а details.allowed, если он есть, перечисляет допустимые значения.
bulk_empty400
Массив links в пакетном вызове пуст или отсутствует.
bulk_too_many400
Слишком много ссылок в одном пакетном вызове. details.max — лимит вашего плана: разбейте список на несколько вызовов.
idempotency_key_invalid400
Заголовок Idempotency-Key длиннее 128 символов.
rate_limited429
Слишком много запросов за эту минуту. Подождите столько секунд, сколько указано в Retry-After, и продолжайте.

Ответ 500 означает, что что-то сломалось на нашей стороне и исправлять в вашем запросе нечего. Повторите попытку чуть позже; если ошибка не исчезает, напишите в поддержку и укажите значение заголовка X-Request-Id.

Пагинация

Списки, которые могут быть длинными, разбиты на страницы: GET /links, GET /links/active и GET /links/:id/clicks.

  • page: нужная страница, начиная с 1.
  • per_page: сколько элементов на странице, от 1 до 100 (по умолчанию 50). limit принимается как синоним.

Ответ содержит объект pagination, а общее количество также приходит в заголовке X-Total-Count. Запрашивайте next_page, пока он не станет null.

Ответ
{
  "links": [],
  "pagination": { "page": 2, "per_page": 50, "total": 137, "total_pages": 3, "next_page": 3, "prev_page": 1 }
}

Идемпотентность

POST /links, POST /links/bulk и POST /links/generate принимают заголовок Idempotency-Key: произвольный текст длиной до 128 символов, который вы выбираете сами, — свой для каждой операции, например номер заказа или UUID.

Если тот же Idempotency-Key снова приходит с того же ключа API в течение 24 часов, pik.li ничего не создаёт, а возвращает первый ответ с заголовком Idempotent-Replayed: true. Так запрос, повторённый после тайм-аута, никогда не создаёт дубликат.

Тот же запрос с тем же ключом в течение 24 часов
HTTP/1.1 201 Created
Idempotent-Replayed: true
Content-Type: application/json; charset=utf-8

Ответы со статусом 5xx не запоминаются, поэтому повтор запроса с тем же ключом выполнит его заново. На слишком длинный ключ приходит idempotency_key_invalid.

Языки

Сообщения об ошибках доступны на девяти языках: en it zh ar ru fr de es pt-BR. API выбирает язык по параметру locale, затем по заголовку Accept-Language, затем по языку вашего аккаунта, а если ничего не подошло — использует английский. Меняется только сообщение: коды и имена полей остаются прежними, как и docs_url, который открывает эту страницу на том же языке.

Терминал
curl "https://pik.li/api/v1/links?status=nope" \
  -H "Authorization: Bearer $PIKLI_KEY" \
  -H "Accept-Language: it"

{
  "error": {
    "code": "invalid_parameter",
    "message": "Valore non valido per il parametro status.",
    "details": { "allowed": ["all", "active", "disabled", "review", "expired"] },
    "docs_url": "https://pik.li/help?locale=it#error-invalid_parameter"
  },
  "request_id": "0d5c9b7e-1f2a-4e3b-8c6d-7a9e0f1b2c3d"
}

Окна статистики

Эндпоинты статистики берут временное окно и сравнивают его с предыдущим окном той же длины: отсюда и берутся previous и delta_pct.

period
24h, 7d, 30d, 90d, 1y или all (вся история, которую хранит ваш план). Подойдёт и число дней: period=45 или устаревший вариант days=45. По умолчанию — 7d для аккаунта, 30d для отдельной ссылки и all для списка кликов.
from, to
Вместо period: дата (2026-09-01) или время в формате ISO 8601. По умолчанию to — текущий момент, from — за 30 дней до to; дата в to учитывается до конца этого дня.
interval
hour или day — шаг временного ряда. Окна до 48 часов по умолчанию разбиваются по часам, более длинные — по дням; почасовой ряд охватывает не больше 7 дней.
window.clamped
Окно никогда не уходит в прошлое дальше истории, которую хранит ваш план (retention_days). Если окно пришлось обрезать, window.clamped равен true.
bots
Клики ботов не учитываются. Добавьте bots=1, чтобы считать и их; статистика отдельной ссылки к тому же показывает их отдельно в period.bots.

Эндпоинты

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

Сервис

Работает ли API и сколько осталось от лимитов вашего плана.

GET /ping Без ключа

Проверить, что API работает

Ключ не нужен: эндпоинт предназначен для проверок доступности. Возвращает время сервера и адреса этой документации и файла OpenAPI.

Запрос
curl https://pik.li/api/v1/ping
Ответ · 200
{
  "ok": true,
  "service": "pik.li",
  "version": "v1",
  "time": "2026-09-23T08:15:42Z",
  "docs": "https://pik.li/help",
  "openapi": "https://pik.li/openapi.json"
}
Ошибки Нет: этому эндпоинту не нужен ключ.
GET /limits

Что осталось от ваших квот

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

Запрос
curl https://pik.li/api/v1/limits \
  -H "Authorization: Bearer $PIKLI_KEY"
Ответ · 200
{
  "plan": "premium",
  "plan_name": "Premium",
  "links": {
    "period": "month", "limit": 2500, "used": 412, "remaining": 2088,
    "resets_at": "2026-09-30T22:00:00Z",
    "today": 9, "this_month": 412, "per_day": 0, "per_month": 2500
  },
  "clicks": { "limit": 150000, "used": 38120, "remaining": 111880, "resets_at": "2026-09-30T22:00:00Z" },
  "rate_limit": { "limit": 600, "remaining": 597, "period_seconds": 60, "resets_at": "2026-09-23T08:16:00Z" },
  "api_keys": { "used": 2, "limit": 10 },
  "custom_domains": { "used": 1, "limit": 3 },
  "bulk": { "max_items": 100 },
  "retention_days": 730,
  "link_ttl_days": 0,
  "features": ["qr", "analytics_full", "tags", "export", "custom_slug", "password", "expiry", "utm", "bulk", "api", "webhooks"]
}
Ошибки Только те, что может вернуть любой эндпоинт.

Аккаунт

Кто выполняет вызов: аккаунт, которому принадлежит ключ.

GET /me

Ваш аккаунт и текущий ключ

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

Запрос
curl https://pik.li/api/v1/me \
  -H "Authorization: Bearer $PIKLI_KEY"
Ответ · 200
{
  "id": 1042,
  "username": "anna",
  "email": "[email protected]",
  "name": "Anna Rossi",
  "status": "active",
  "confirmed": true,
  "locale": "it",
  "plan": "premium",
  "plan_name": "Premium",
  "interval": "monthly",
  "plan_expires_at": "2026-10-14T23:59:59.000+02:00",
  "api_enabled": true,
  "features": ["qr", "analytics_full", "tags", "export", "custom_slug", "password", "expiry", "utm", "bulk", "api", "webhooks"],
  "limits": {
    "links_per_day": 0, "links_per_month": 2500, "clicks_per_month": 150000,
    "retention_days": 730, "link_ttl_days": 0, "custom_domains": 3, "api_rpm": 600
  },
  "usage": {
    "links_today": 9, "links_this_month": 412, "clicks_this_month": 38120,
    "links_quota_used": 412, "links_quota_limit": 2500, "links_total": 3187
  },
  "api_key": {
    "id": 55, "name": "CRM production", "prefix": "pk_live_Xq3v",
    "created_at": "2026-09-02T16:40:10.311+02:00", "last_used_at": "2026-09-23T10:15:42.020+02:00", "requests_count": 18230
  },
  "created_at": "2026-03-11T09:12:47.664+01:00"
}
Ошибки Только те, что может вернуть любой эндпоинт.

Статистика аккаунта

Показатели всех ваших ссылок вместе, с теми же фильтрами, что и в панели управления: окно, домен, тег, боты. Общие параметры описаны в разделе «Окна статистики».

GET /stats

Статистика аккаунта одним вызовом

Итоги по аккаунту, окно в сравнении с предыдущим, временной ряд, первые 10 источников и стран, первые 6 устройств, браузеров и операционных систем и 10 ссылок с наибольшим числом кликов. Остальные эндпоинты /stats возвращают каждую часть отдельно и с более длинными списками.

Параметры
  • period string в строке запроса

    Окно: 24h, 7d (по умолчанию), 30d, 90d, 1y, all. См. «Окна статистики».

  • from string в строке запроса

    Начало окна вместо period: дата или время в формате ISO 8601.

  • to string в строке запроса

    Конец окна (по умолчанию — текущий момент).

  • interval string в строке запроса

    hour или day — шаг временного ряда.

  • domain string в строке запроса

    Только этот домен, по имени хоста (например, lnkz.li). Домен должен быть одним из ваших: см. GET /domains.

  • tag string в строке запроса

    Только ссылки с этим тегом.

  • bots string в строке запроса

    1, чтобы учитывать и клики ботов.

Запрос
curl "https://pik.li/api/v1/stats?period=7d" \
  -H "Authorization: Bearer $PIKLI_KEY"
Ответ · 200
{
  "window": {
    "from": "2026-09-17T00:00:00+02:00",
    "to": "2026-09-23T10:15:42+02:00",
    "days": 7,
    "interval": "day",
    "period": "7d",
    "clamped": false,
    "retention_days": 730,
    "previous": { "from": "2026-09-10T00:00:00+02:00", "to": "2026-09-17T00:00:00+02:00" }
  },
  "filters": { "bots": false },
  "totals": {
    "links": 3187, "active_links": 2954, "clicks": 402118, "unique_clicks": 288930,
    "clicks_today": 1204, "links_active_24h": 214, "links_created_in_window": 61
  },
  "period": { "clicks": 21480, "uniques": 15522, "links_clicked": 690, "clicks_per_link": 31.1 },
  "previous": { "clicks": 19870, "uniques": 14410, "links_clicked": 655, "clicks_per_link": 30.3, "delta_pct": 8.1, "uniques_delta_pct": 7.7 },
  "timeseries": {
    "interval": "day",
    "points": [
      { "at": "2026-09-17", "clicks": 2890, "uniques": 2104 },
      { "at": "2026-09-18", "clicks": 3120, "uniques": 2240 }
    ]
  },
  "referrers": [{ "referrer": "instagram.com", "clicks": 5210, "share": 24.3 }],
  "direct": 8740,
  "countries": [{ "country": "IT", "clicks": 12877, "share": 59.9 }],
  "devices": [{ "device": "mobile", "clicks": 14002, "share": 65.2 }],
  "browsers": [{ "browser": "Chrome", "clicks": 9406, "share": 43.8 }],
  "os": [{ "os": "iOS", "clicks": 7340, "share": 34.2 }],
  "top_links": [{
    "id": 4821,
    "short_url": "https://lnkz.li/spring-sale",
    "slug": "spring-sale",
    "domain": "lnkz.li",
    "target_url": "https://example.com/shop/spring-sale",
    "target_host": "example.com",
    "title": "Spring sale",
    "description": null,
    "tags": ["promo", "newsletter"],
    "status": "active",
    "moderation": "allowed",
    "active": true,
    "expired": false,
    "password_protected": false,
    "clicks": 1284,
    "unique_clicks": 902,
    "last_clicked_at": "2026-09-23T09:58:12.431+02:00",
    "expires_at": null,
    "max_clicks": null,
    "disabled_reason": null,
    "utm": { "source": "newsletter", "medium": "email", "campaign": "spring" },
    "preview_url": "https://lnkz.li/spring-sale+",
    "qr_url": "https://pik.li/api/v1/links/4821/qr",
    "created_at": "2026-09-01T08:30:05.117+02:00",
    "updated_at": "2026-09-01T08:30:05.117+02:00",
    "clicks_in_window": 311,
    "uniques_in_window": 240
  }]
}
GET /stats/timeseries

Клики по времени

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

Параметры
  • period string в строке запроса

    Окно: 24h, 7d (по умолчанию), 30d, 90d, 1y, all. См. «Окна статистики».

  • from string в строке запроса

    Начало окна вместо period: дата или время в формате ISO 8601.

  • to string в строке запроса

    Конец окна (по умолчанию — текущий момент).

  • interval string в строке запроса

    hour или day — шаг временного ряда.

  • domain string в строке запроса

    Только этот домен, по имени хоста (например, lnkz.li). Домен должен быть одним из ваших: см. GET /domains.

  • tag string в строке запроса

    Только ссылки с этим тегом.

  • bots string в строке запроса

    1, чтобы учитывать и клики ботов.

Запрос
curl "https://pik.li/api/v1/stats/timeseries?period=24h" \
  -H "Authorization: Bearer $PIKLI_KEY"
Ответ · 200
{
  "window": {
    "from": "2026-09-22T10:15:42+02:00",
    "to": "2026-09-23T10:15:42+02:00",
    "days": 1,
    "interval": "hour",
    "period": "24h",
    "clamped": false,
    "retention_days": 730,
    "previous": { "from": "2026-09-21T10:15:42+02:00", "to": "2026-09-22T10:15:42+02:00" }
  },
  "filters": { "bots": false },
  "interval": "hour",
  "clicks": 3308,
  "previous_clicks": 2977,
  "delta_pct": 11.1,
  "points": [
    { "at": "2026-09-22T08:00:00Z", "clicks": 131, "uniques": 97 },
    { "at": "2026-09-22T09:00:00Z", "clicks": 158, "uniques": 120 }
  ]
}
GET /stats/referrers

Откуда приходят клики

Сайты, которые привели больше всего кликов, с долей каждого от общего числа, а также клики без источника (direct).

Параметры
  • period string в строке запроса

    Окно: 24h, 7d (по умолчанию), 30d, 90d, 1y, all. См. «Окна статистики».

  • from string в строке запроса

    Начало окна вместо period: дата или время в формате ISO 8601.

  • to string в строке запроса

    Конец окна (по умолчанию — текущий момент).

  • domain string в строке запроса

    Только этот домен, по имени хоста (например, lnkz.li). Домен должен быть одним из ваших: см. GET /domains.

  • tag string в строке запроса

    Только ссылки с этим тегом.

  • bots string в строке запроса

    1, чтобы учитывать и клики ботов.

  • limit integer в строке запроса

    Сколько строк в каждом списке, от 1 до 50 (по умолчанию 10).

Запрос
curl "https://pik.li/api/v1/stats/referrers?period=30d&limit=5" \
  -H "Authorization: Bearer $PIKLI_KEY"
Ответ · 200
{
  "window": {
    "from": "2026-09-17T00:00:00+02:00",
    "to": "2026-09-23T10:15:42+02:00",
    "days": 7,
    "interval": "day",
    "period": "7d",
    "clamped": false,
    "retention_days": 730,
    "previous": { "from": "2026-09-10T00:00:00+02:00", "to": "2026-09-17T00:00:00+02:00" }
  },
  "filters": { "bots": false },
  "clicks": 21480,
  "direct": 8740,
  "referrers": [
    { "referrer": "instagram.com", "clicks": 5210, "share": 24.3 },
    { "referrer": "google.com", "clicks": 3011, "share": 14.0 }
  ]
}
GET /stats/countries

Страны и города

Страны в виде ISO-кодов с их долей; cities доступен в планах с полной статистикой.

Параметры
  • period string в строке запроса

    Окно: 24h, 7d (по умолчанию), 30d, 90d, 1y, all. См. «Окна статистики».

  • from string в строке запроса

    Начало окна вместо period: дата или время в формате ISO 8601.

  • to string в строке запроса

    Конец окна (по умолчанию — текущий момент).

  • domain string в строке запроса

    Только этот домен, по имени хоста (например, lnkz.li). Домен должен быть одним из ваших: см. GET /domains.

  • tag string в строке запроса

    Только ссылки с этим тегом.

  • bots string в строке запроса

    1, чтобы учитывать и клики ботов.

  • limit integer в строке запроса

    Сколько строк в каждом списке, от 1 до 50 (по умолчанию 10).

Запрос
curl "https://pik.li/api/v1/stats/countries?period=30d" \
  -H "Authorization: Bearer $PIKLI_KEY"
Ответ · 200
{
  "window": {
    "from": "2026-09-17T00:00:00+02:00",
    "to": "2026-09-23T10:15:42+02:00",
    "days": 7,
    "interval": "day",
    "period": "7d",
    "clamped": false,
    "retention_days": 730,
    "previous": { "from": "2026-09-10T00:00:00+02:00", "to": "2026-09-17T00:00:00+02:00" }
  },
  "filters": { "bots": false },
  "clicks": 21480,
  "countries": [
    { "country": "IT", "clicks": 12877, "share": 59.9 },
    { "country": "DE", "clicks": 2630, "share": 12.2 }
  ],
  "cities": [
    { "city": "Milano", "clicks": 3120, "share": 14.5 },
    { "city": "Roma", "clicks": 2210, "share": 10.3 }
  ]
}
GET /stats/devices

Устройства, браузеры, системы и языки

Четыре списка одним вызовом, в каждом — клики и доля от общего числа.

Параметры
  • period string в строке запроса

    Окно: 24h, 7d (по умолчанию), 30d, 90d, 1y, all. См. «Окна статистики».

  • from string в строке запроса

    Начало окна вместо period: дата или время в формате ISO 8601.

  • to string в строке запроса

    Конец окна (по умолчанию — текущий момент).

  • domain string в строке запроса

    Только этот домен, по имени хоста (например, lnkz.li). Домен должен быть одним из ваших: см. GET /domains.

  • tag string в строке запроса

    Только ссылки с этим тегом.

  • bots string в строке запроса

    1, чтобы учитывать и клики ботов.

  • limit integer в строке запроса

    Сколько строк в каждом списке, от 1 до 50 (по умолчанию 10).

Запрос
curl "https://pik.li/api/v1/stats/devices?period=30d" \
  -H "Authorization: Bearer $PIKLI_KEY"
Ответ · 200
{
  "window": {
    "from": "2026-09-17T00:00:00+02:00",
    "to": "2026-09-23T10:15:42+02:00",
    "days": 7,
    "interval": "day",
    "period": "7d",
    "clamped": false,
    "retention_days": 730,
    "previous": { "from": "2026-09-10T00:00:00+02:00", "to": "2026-09-17T00:00:00+02:00" }
  },
  "filters": { "bots": false },
  "clicks": 21480,
  "devices": [{ "device": "mobile", "clicks": 14002, "share": 65.2 }, { "device": "desktop", "clicks": 7100, "share": 33.1 }],
  "browsers": [{ "browser": "Chrome", "clicks": 9406, "share": 43.8 }],
  "os": [{ "os": "iOS", "clicks": 7340, "share": 34.2 }],
  "languages": [{ "language": "it", "clicks": 12690, "share": 59.1 }]
}
GET /stats/top

Самые популярные ссылки

Ссылки с наибольшим числом кликов за окно, у каждой есть clicks_in_window и uniques_in_window.

Параметры
  • period string в строке запроса

    Окно: 24h, 7d (по умолчанию), 30d, 90d, 1y, all. См. «Окна статистики».

  • from string в строке запроса

    Начало окна вместо period: дата или время в формате ISO 8601.

  • to string в строке запроса

    Конец окна (по умолчанию — текущий момент).

  • domain string в строке запроса

    Только этот домен, по имени хоста (например, lnkz.li). Домен должен быть одним из ваших: см. GET /domains.

  • tag string в строке запроса

    Только ссылки с этим тегом.

  • bots string в строке запроса

    1, чтобы учитывать и клики ботов.

  • limit integer в строке запроса

    Сколько строк в каждом списке, от 1 до 50 (по умолчанию 10).

Запрос
curl "https://pik.li/api/v1/stats/top?period=7d&limit=10" \
  -H "Authorization: Bearer $PIKLI_KEY"
Ответ · 200
{
  "window": {
    "from": "2026-09-17T00:00:00+02:00",
    "to": "2026-09-23T10:15:42+02:00",
    "days": 7,
    "interval": "day",
    "period": "7d",
    "clamped": false,
    "retention_days": 730,
    "previous": { "from": "2026-09-10T00:00:00+02:00", "to": "2026-09-17T00:00:00+02:00" }
  },
  "filters": { "bots": false },
  "clicks": 21480,
  "links": [{
    "id": 4821,
    "short_url": "https://lnkz.li/spring-sale",
    "slug": "spring-sale",
    "domain": "lnkz.li",
    "target_url": "https://example.com/shop/spring-sale",
    "target_host": "example.com",
    "title": "Spring sale",
    "description": null,
    "tags": ["promo", "newsletter"],
    "status": "active",
    "moderation": "allowed",
    "active": true,
    "expired": false,
    "password_protected": false,
    "clicks": 1284,
    "unique_clicks": 902,
    "last_clicked_at": "2026-09-23T09:58:12.431+02:00",
    "expires_at": null,
    "max_clicks": null,
    "disabled_reason": null,
    "utm": { "source": "newsletter", "medium": "email", "campaign": "spring" },
    "preview_url": "https://lnkz.li/spring-sale+",
    "qr_url": "https://pik.li/api/v1/links/4821/qr",
    "created_at": "2026-09-01T08:30:05.117+02:00",
    "updated_at": "2026-09-01T08:30:05.117+02:00",
    "clicks_in_window": 311,
    "uniques_in_window": 240
  }]
}

Теги

Метки, которые вы присваиваете своим ссылкам.

GET /tags

Ваши теги

Все теги ваших ссылок: у скольких ссылок есть каждый тег и сколько кликов они собрали; сначала самые используемые.

Параметры
  • q string в строке запроса

    Только теги, содержащие этот текст.

Запрос
curl https://pik.li/api/v1/tags \
  -H "Authorization: Bearer $PIKLI_KEY"
Ответ · 200
{
  "tags": [
    { "name": "promo", "links": 214, "clicks": 90211 },
    { "name": "newsletter", "links": 58, "clicks": 20473 }
  ],
  "total": 2
}
Ошибки Только те, что может вернуть любой эндпоинт.

Домены

Домены, на которых могут работать ваши короткие ссылки.

GET /domains

Доступные вам домены

Домены pik.li, разрешённые вашим планом, и ваши подтверждённые собственные домены; какой из них используется по умолчанию и сколько собственных доменов допускает ваш план. При создании ссылки передавайте hostname в поле domain.

Запрос
curl https://pik.li/api/v1/domains \
  -H "Authorization: Bearer $PIKLI_KEY"
Ответ · 200
{
  "domains": [
    { "id": 1, "hostname": "lnkz.li", "base_url": "https://lnkz.li", "kind": "system", "min_plan": "free", "status": "active", "verified": true, "default": true },
    { "id": 318, "hostname": "go.example.com", "base_url": "https://go.example.com", "kind": "custom", "min_plan": "free", "status": "active", "verified": true, "default": false }
  ],
  "default": "lnkz.li",
  "custom_domains": { "used": 1, "limit": 3 }
}
Ошибки Только те, что может вернуть любой эндпоинт.

ВебхукиПланы: Premium · Business

Управляйте из своего кода вебхуками, которые иначе настраивались бы на странице «Вебхуки» панели управления. Как работают доставки, описано ниже в разделе «Вебхуки».

GET /webhooks

Список вебхуков

Ваши вебхуки и события, на которые можно подписаться. После создания секрет больше никогда не показывается.

Запрос
curl https://pik.li/api/v1/webhooks \
  -H "Authorization: Bearer $PIKLI_KEY"
Ответ · 200
{
  "webhooks": [{
    "id": 17,
    "url": "https://example.com/hooks/pikli",
    "events": ["link.created", "link.disabled"],
    "active": true,
    "failures_count": 0,
    "last_delivered_at": "2026-09-22T18:04:11.520+02:00",
    "created_at": "2026-09-10T11:20:33.004+02:00",
    "updated_at": "2026-09-10T11:20:33.004+02:00",
    "signature_header": "X-Pikli-Signature"
  }],
  "events": ["link.created", "link.clicked", "link.disabled", "link.deleted"]
}
Ошибки feature_required
GET /webhooks/:id

Получить вебхук

Вебхук с последними 20 доставками и статусом, которым ваш сервер ответил на каждую из них.

Параметры
  • id integer в пути обязательный

    id вебхука.

Запрос
curl https://pik.li/api/v1/webhooks/17 \
  -H "Authorization: Bearer $PIKLI_KEY"
Ответ · 200
{
  "id": 17,
  "url": "https://example.com/hooks/pikli",
  "events": ["link.created", "link.disabled"],
  "active": true,
  "failures_count": 0,
  "last_delivered_at": "2026-09-22T18:04:11.520+02:00",
  "created_at": "2026-09-10T11:20:33.004+02:00",
  "updated_at": "2026-09-10T11:20:33.004+02:00",
  "signature_header": "X-Pikli-Signature",
  "deliveries": [
    { "id": 903, "event": "ping", "response_code": 200, "attempts": 1, "delivered_at": "2026-09-22T18:04:11.520+02:00", "created_at": "2026-09-22T18:04:11.520+02:00" }
  ]
}
POST /webhooks

Создать вебхук

Ответ содержит secret, которым подписываются доставки, — только в этот раз: сохраните его сразу.

Параметры
  • url string в теле JSON обязательный

    Адрес, на который приходят доставки: http:// или https://, доступный из интернета.

  • events array | string в теле JSON

    События, которые нужно получать, массивом или через запятую. Если не указано — все. Список событий см. ниже.

  • active boolean в теле JSON

    false, чтобы создать вебхук приостановленным. По умолчанию true.

Запрос
curl -X POST https://pik.li/api/v1/webhooks \
  -H "Authorization: Bearer $PIKLI_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url":"https://example.com/hooks/pikli","events":["link.created","link.disabled"]}'
Ответ · 201
{
  "id": 17,
  "url": "https://example.com/hooks/pikli",
  "events": ["link.created", "link.disabled"],
  "active": true,
  "failures_count": 0,
  "last_delivered_at": null,
  "created_at": "2026-09-10T11:20:33.004+02:00",
  "updated_at": "2026-09-10T11:20:33.004+02:00",
  "signature_header": "X-Pikli-Signature",
  "secret": "ec307f56cfac71856a10d641eee7dd58e0eb8286"
}
PATCH /webhooks/:id

Изменить вебхук

Передавайте только то, что меняется: адрес, события или active, чтобы приостановить или возобновить доставки.

Также принимается PUT с теми же параметрами.

Параметры
  • id integer в пути обязательный

    id вебхука.

  • url string в теле JSON

    Новый адрес.

  • events array | string в теле JSON

    Новый список событий; он не может быть пустым.

  • active boolean в теле JSON

    false приостанавливает доставки, true возобновляет их.

Запрос
curl -X PATCH https://pik.li/api/v1/webhooks/17 \
  -H "Authorization: Bearer $PIKLI_KEY" \
  -H "Content-Type: application/json" \
  -d '{"active":false}'
Ответ · 200
{
  "id": 17,
  "url": "https://example.com/hooks/pikli",
  "events": ["link.created", "link.disabled"],
  "active": false,
  "failures_count": 0,
  "last_delivered_at": "2026-09-22T18:04:11.520+02:00",
  "created_at": "2026-09-10T11:20:33.004+02:00",
  "updated_at": "2026-09-23T10:15:42.118+02:00",
  "signature_header": "X-Pikli-Signature"
}
DELETE /webhooks/:id

Удалить вебхук

Доставки прекращаются, а их история удаляется вместе с вебхуком.

Параметры
  • id integer в пути обязательный

    id вебхука.

Запрос
curl -X DELETE https://pik.li/api/v1/webhooks/17 \
  -H "Authorization: Bearer $PIKLI_KEY"
Ответ · 204
Ответ без тела: всё говорит код статуса.
POST /webhooks/:id/test

Отправить тестовую доставку

Ставит в очередь ping на адрес вебхука, подписанный так же, как любая доставка: самый быстрый способ проверить ваш код проверки подписи. Доставка уходит вскоре после вызова и появляется в GET /webhooks/:id.

Параметры
  • id integer в пути обязательный

    id вебхука.

Запрос
curl -X POST https://pik.li/api/v1/webhooks/17/test \
  -H "Authorization: Bearer $PIKLI_KEY"
Ответ · 202
{
  "queued": true,
  "event": "ping",
  "webhook_id": 17
}

ВебхукиПланы: Premium · Business

Вебхук — это адрес на вашем сервере, на который pik.li отправляет POST-запрос, когда в вашем аккаунте что-то происходит. Создать его можно на странице «Вебхуки» панели управления или через эндпоинты вебхуков.

События

ping
Тестовая доставка: отправляется, когда вы нажимаете «Тест» в панели управления или вызываете POST /webhooks/:id/test.
link.created
Создана ссылка — в панели управления или через API. скоро
link.clicked
Кто-то кликнул по одной из ваших ссылок. скоро
link.disabled
Ссылка отключена — вами или модерацией. скоро
link.deleted
Ссылка удалена. скоро
События ссылок уже можно выбрать в вебхуке, но их доставка ещё не запущена: пока pik.li отправляет только тестовый ping.

Как выглядит доставка

Это POST-запрос с JSON-телом из трёх полей: event — название события, created_at — время отправки, data — подробности события. Вместе с ним приходят такие заголовки:

Что получает ваш сервер
POST /hooks/pikli HTTP/1.1
Content-Type: application/json
X-Pikli-Event: ping
X-Pikli-Signature: sha256=bdaf35d42413498dab7fea8be1f292c36b690152e5ec7d66e6dd2c2cd0b2d61c

{"event":"ping","created_at":"2026-09-23T10:15:42+02:00","data":{"message":"pik.li webhook test","at":"2026-09-23T10:15:42+02:00","via":"api"}}
Content-Type
всегда application/json.
X-Pikli-Event
название события, то же, что в теле: по нему можно направить запрос нужному обработчику, ещё не читая тело.
X-Pikli-Signature
sha256=, за которым следует подпись тела, как описано ниже.

Проверка подписи

Каждая доставка подписывается секретом своего вебхука, который вы получаете один раз — при его создании. Заголовок X-Pikli-Signature содержит sha256=, за которым следует HMAC-SHA256 исходного тела в шестнадцатеричном виде. Вычислите такой же HMAC по полученным байтам — до разбора JSON, который бы их изменил, — и сравните две строки за постоянное время. Если они различаются, ответьте 401 и проигнорируйте запрос.

Чтобы проверить свой код: с секретом ec307f56cfac71856a10d641eee7dd58e0eb8286 из примера выше тело показанной здесь доставки даёт в точности подпись из её заголовка.

Node.js
import crypto from "node:crypto";
import express from "express";

const app = express();

app.post("/hooks/pikli", express.raw({ type: "application/json" }), (req, res) => {
  const expected = "sha256=" + crypto
    .createHmac("sha256", process.env.PIKLI_WEBHOOK_SECRET)
    .update(req.body)
    .digest("hex");
  const received = req.get("X-Pikli-Signature") || "";
  const valid = received.length === expected.length &&
    crypto.timingSafeEqual(Buffer.from(received), Buffer.from(expected));
  if (!valid) return res.sendStatus(401);

  const event = JSON.parse(req.body);
  res.sendStatus(204);
});
Python
import hashlib, hmac, os
from flask import Flask, abort, request

app = Flask(__name__)
SECRET = os.environ["PIKLI_WEBHOOK_SECRET"].encode()

@app.post("/hooks/pikli")
def pikli_hook():
    body = request.get_data()
    expected = "sha256=" + hmac.new(SECRET, body, hashlib.sha256).hexdigest()
    if not hmac.compare_digest(expected, request.headers.get("X-Pikli-Signature", "")):
        abort(401)
    event = request.get_json()
    return "", 204
Ruby
class PikliHooksController < ActionController::API
  def create
    body = request.raw_post
    expected = "sha256=" + OpenSSL::HMAC.hexdigest("SHA256", ENV.fetch("PIKLI_WEBHOOK_SECRET"), body)
    received = request.headers["X-Pikli-Signature"].to_s
    return head(:unauthorized) unless ActiveSupport::SecurityUtils.secure_compare(expected, received)

    event = JSON.parse(body)
    head :no_content
  end
end
PHP
<?php
$body = file_get_contents('php://input');
$expected = 'sha256=' . hash_hmac('sha256', $body, getenv('PIKLI_WEBHOOK_SECRET'));
$received = $_SERVER['HTTP_X_PIKLI_SIGNATURE'] ?? '';
if (!hash_equals($expected, $received)) {
    http_response_code(401);
    exit;
}
$event = json_decode($body, true);
http_response_code(204);

Ответы, повторы и сбои

  • Отвечайте любым статусом 2xx как можно скорее, а медленную работу выполняйте уже после ответа.
  • Если pik.li вообще не может достучаться до вашего адреса — сетевая ошибка, отказ в соединении, непубличный адрес, — доставка повторяется до 5 раз со всё более длинными паузами: последняя попытка происходит примерно через шесть минут после первой.
  • Ответ со статусом 400 и выше записывается и не повторяется. Он увеличивает на единицу failures_count вебхука; счётчик обнуляется при первой же успешной доставке.
  • Последние 20 доставок со статусом, которым ответил ваш сервер, есть в GET /webhooks/:id и на странице «Вебхуки» панели управления.
  • Адрес должен быть публичным: pik.li не обращается к частным и локальным сетям. Используйте https://, чтобы доставки передавались в зашифрованном виде.

OpenAPI

Та же справка в машиночитаемом виде опубликована по адресу /openapi.json (OpenAPI 3.1). Импортируйте её в Postman, Insomnia или Bruno либо сгенерируйте клиент с помощью openapi-generator.

Чего-то не хватает или что-то работает не так, как написано на этой странице? Напишите на [email protected].