API сокращателя ссылок — одна из тех небольших интеграций, которые обычно висят в бэклоге инженерной команды. Три эндпоинта, заголовок аутентификации, JSON-полезная нагрузка. Страница документации обещает первый успешный вызов через пять минут. Но затем в продакшен приходит трафик, логика повторных попыток создает дубликаты ссылок, панель управления заполняется вариантами /foo-1, /foo-2, /foo-3 для одного и того же адреса назначения, и кто-то заводит тикет.
В этом посте мы разберем реальный процесс интеграции. Аутентификация, первый вызов, четыре эндпоинта, которые покрывают большинство сценариев, идемпотентность, обработка ошибок, лимиты запросов (rate limits) и нюансы продакшена, которые пропускают «пятиминутные» руководства. Примеры кода на TypeScript, Python, Go, Ruby и PHP: первые три через официальные SDK (@elido/sdk, elido-python, github.com/elido/elido-go), последние два — через обычные HTTP-клиенты.
Предварительные условия#
Войдите в панель управления, перейдите в /settings/api и создайте персональный токен доступа (personal access token). Токены привязаны к области рабочего пространства (workspace) — токен, выданный в рабочем пространстве A, не может создавать ссылки в рабочем пространстве B. Токены сервисных аккаунтов (для систем CI, внутренних инструментов, интеграций machine-to-machine) создаются на том же экране в тарифах Pro и выше; они имеют явные области доступа (links:write, analytics:read, domains:write) и ротируются независимо от персональных токенов.
Базовый URL — https://api.elido.app/v1. Домены редиректа (f.elido.me, s.elido.me, b.elido.me) отделены от поверхности API. Ваши короткие ссылки работают на доменах редиректа, а API предназначен для их создания, изменения и чтения.
Спецификация OpenAPI опубликована по адресу https://api.elido.app/v1/openapi.json и соответствует стандарту OpenAPI 3.1. Официальные SDK генерируются на основе этой спецификации и обновляются с каждым релизом API; вы также можете сгенерировать собственный клиент на любом языке, поддерживающем OpenAPI.
Первый вызов#
Создадим короткую ссылку из целевого URL. Пять строк на TypeScript:
import { Elido } from "@elido/sdk";
const elido = new Elido({ token: process.env.ELIDO_TOKEN! });
const link = await elido.links.create({
destinationUrl: "https://shop.example.com/spring-sale",
});
console.log(link.shortUrl); // https://s.elido.me/abc123
Python:
from elido import Elido
client = Elido(token=os.environ["ELIDO_TOKEN"])
link = client.links.create(
destination_url="https://shop.example.com/spring-sale",
)
print(link.short_url) # https://s.elido.me/abc123
Go:
import "github.com/elido/elido-go/v2/elido"
client := elido.NewClient(elido.WithToken(os.Getenv("ELIDO_TOKEN")))
link, err := client.Links.Create(ctx, &elido.LinkCreateInput{
DestinationURL: "https://shop.example.com/spring-sale",
})
if err != nil {
return fmt.Errorf("create link: %w", err)
}
fmt.Println(link.ShortURL)
Ruby (без официального SDK — используем net/http):
require "net/http"
require "json"
uri = URI("https://api.elido.app/v1/links")
req = Net::HTTP::Post.new(uri)
req["Authorization"] = "Bearer #{ENV['ELIDO_TOKEN']}"
req["Content-Type"] = "application/json"
req.body = { destination_url: "https://shop.example.com/spring-sale" }.to_json
res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http| http.request(req) }
link = JSON.parse(res.body)
puts link["short_url"]
PHP (Guzzle):
$client = new GuzzleHttp\Client(['base_uri' => 'https://api.elido.app/v1/']);
$res = $client->post('links', [
'headers' => ['Authorization' => 'Bearer ' . getenv('ELIDO_TOKEN')],
'json' => ['destination_url' => 'https://shop.example.com/spring-sale'],
]);
$link = json_decode((string) $res->getBody(), true);
echo $link['short_url'];
Все пять вариантов дают одинаковый результат. Тело ответа содержит короткий URL, канонический ID ссылки, ID рабочего пространства и временную метку создания. Слаг (slug) — в примере выше это abc123 — генерируется сервером, если вы не передали custom_slug в запросе. Алфавит слага — base62 ([0-9A-Za-z]); длина по умолчанию — шесть символов.
Четыре эндпоинта, которые вы действительно будете использовать#
У API больше четырех эндпоинтов, но большинство интеграций ограничиваются этим набором.
Создание ссылки#
POST /v1/links принимает целевой URL и дополнительные поля:
custom_slug— слаг, который вы выбираете сами (должен быть уникальным в пределах рабочего пространства).domain_id— для ссылок на кастомных доменах; если не указано, используется основной домен рабочего пространства.tags— массив строк в свободной форме для организации ссылок.utm— параметры кампании, которые будут добавлены к целевому URL при редиректе.expires_at— временная метка ISO 8601, после которой ссылка будет возвращать 410 Gone.password— если установлено, перед перенаправлением будет отображаться страница запроса пароля.metadata— произвольный JSON-объект, который не интерпретируется при редиректе; полезен для ваших собственных ключей связки.
Пользовательский слаг (custom_slug) — это поле, на котором часто обжигаются команды. Если вы передадите слаг, который уже используется другой ссылкой в том же рабочем пространстве, API вернет 409 Conflict. Наивный обработчик повторов, который просто добавляет счетчик (my-slug-1, my-slug-2), создает проблему дубликатов, описанную в начале статьи. Правильное поведение при повторных попытках описано ниже в разделе про идемпотентность.
Чтение ссылки#
GET /v1/links/{id} возвращает полную запись ссылки, включая текущее количество кликов, метку времени последнего клика и все настройки. ID ссылки — это канонический идентификатор: слаги могут меняться (Pro+ поддерживает переименование слагов), а ID — нет.
GET /v1/links?domain_id=…&tag=…&limit=… выводит список ссылок в рабочем пространстве с фильтрами. Пагинация основана на курсорах; значение next_cursor в ответе является непрозрачным и передается обратно в параметре запроса cursor для получения следующей страницы.
Обновление ссылки#
PATCH /v1/links/{id} принимает те же поля, что и создание. Самые частые обновления: изменение целевого URL (полезно для ротации кампаний без перепечатки QR-кодов), изменение тегов, продление expires_at. Обновление слага выполняется через отдельный эндпоинт POST /v1/links/{id}/rename, который настраивает 301 редирект со старого слага на заданный период удержания (по умолчанию 30 дней).
Удаление ссылки#
DELETE /v1/links/{id} выполняет «мягкое» удаление. Ссылка будет возвращать 410 Gone в течение следующих 90 дней, после чего будет удалена окончательно. В панели управления есть раздел «Корзина» с мягко удаленными ссылками; их можно восстановить через интерфейс или через POST /v1/links/{id}/restore в течение 90-дневного окна.
Ключи идемпотентности#
Каждый изменяющий запрос — POST, PATCH, DELETE — принимает заголовок Idempotency-Key. Значение заголовка — произвольная строка до 255 символов; сервер хранит тело ответа и код состояния в течение 24 часов, привязывая их к паре (workspace_id, idempotency_key), и возвращает сохраненный ответ при повторном использовании того же ключа.
Официальные SDK автоматически генерируют ключи идемпотентности, если они не указаны явно. Вы можете их переопределить:
const link = await elido.links.create(
{ destinationUrl: "https://shop.example.com/spring-sale" },
{ idempotencyKey: "order-12345-link" },
);
Основной сценарий использования — цикл повторных попыток. Если ваша задача создает ссылку в рамках обработки входящего заказа, сгенерируйте ключ идемпотентности на основе ID заказа. Повторный запуск той же задачи увидит тот же ключ, попадет в кэш идемпотентности и вернет изначально созданную ссылку вместо создания второй.
Важный нюанс: кэш идемпотентности живет 24 часа, а не вечно. Повторная попытка через три дня после сбоя задачи создаст новую ссылку. Если ваша интеграция работает с многодневными батчами, сохраните ID ссылки, возвращенный при первом успешном создании, и проверяйте его перед повторным выпуском.
Второй нюанс: идемпотентность работает в рамках рабочего пространства. Один и тот же ключ в двух разных рабочих пространствах создаст две разные ссылки. Это правильная семантика для API с поддержкой нескольких рабочих пространств, но это может удивить команды, ожидающие глобальной уникальности ключа.
Обработка ошибок#
API возвращает стандартные коды состояния HTTP и структурированное тело ошибки:
{
"error": {
"code": "rate_limit_exceeded",
"message": "Workspace rate limit of 100 req/s exceeded. Retry after 1 second.",
"request_id": "req_01HXYZAB123",
"retry_after": 1
}
}
Коды, которые вы будете встречать чаще всего:
400 invalid_request— ошибка валидации полезной нагрузки. Полеmessageперечисляет конкретные поля. Не повторяйте запрос, исправьте данные.401 unauthorized— токен отсутствует или невалиден. Не повторяйте запрос без ротации токена.403 forbidden— у токена нет нужной области доступа. Проверьте список областей доступа токена в/settings/api.404 not_found— ресурс не существует или у токена нет к нему доступа (мы возвращаем 404 вместо 403, чтобы не раскрывать существование ресурса неавторизованным отправителям).409 conflict— слаг уже используется или обнаружено одновременное редактирование (PATCH для устаревшей версии). Получите актуальные данные и попробуйте снова.429 rate_limit_exceeded— сделайте паузу согласно значениюretry_after.500 internal_server_error— ошибка на стороне сервера. Можно безопасно повторить с тем же ключом идемпотентности.502 bad_gateway,503 service_unavailable,504 gateway_timeout— временные проблемы инфраструктуры. Сделайте паузу и повторите запрос.
Официальные SDK реализуют экспоненциальную задержку с джиттером (jitter) для кодов 429, 500, 502, 503 и 504. Они не повторяют запросы с кодами 400, 401, 403, 404 или 409 — это ошибки программирования или конфликты бизнес-логики, а не временные сбои. Кастомные HTTP-клиенты должны следовать тому же паттерну; повтор запроса 400 с той же нагрузкой не даст иного результата.
Поле request_id в теле ошибки — это то, что нужно указывать в тикетах поддержки. По этому ID мы можем отследить любой запрос через аудит-логи, логи приложения и метрики платформы — и мы не сможем найти запрос без него.
Лимиты запросов (Rate limits)#
Опубликованные лимиты составляют 100 запросов в секунду на рабочее пространство в тарифе Pro, 500 в тарифе Business и индивидуальный лимит в тарифе Enterprise. Бесплатный уровень — 10 запросов в секунду.
Состояние лимитов передается в трех заголовках каждого ответа API:
X-RateLimit-Limit— текущий лимит запросов в секунду.X-RateLimit-Remaining— количество запросов, оставшихся в текущей секунде.X-RateLimit-Reset— временная метка Unix, когда счетчик обнулится.
Лимит 100/с реализован по алгоритму token-bucket с возможностью всплеска (burst) до 200 — это значит, что вы можете отправить 200 запросов одновременно, если «ведро» полно, а затем перейти к устойчивой скорости 100/с. Большинство задач по созданию коротких ссылок комфортно укладываются в этот всплеск; интеграции с интенсивной аналитикой, перебирающие исторические события кликов, выигрывают от запаса тарифа Pro.
Для массовых операций в тарифах Business+ эндпоинт POST /v1/links/bulk принимает до 1000 ссылок в одном запросе и считается как одна единица лимита. Это правильный выбор для любой задачи, создающей более сотни ссылок за раз.
Что делают SDK, чего не делает обычный HTTP#
Официальные SDK предоставляют четыре возможности, которые быстро окупаются:
- Автоматический повтор с задержкой для кодов состояния, допускающих повтор.
- Генерация ключей идемпотентности, если они не указаны явно.
- Типизированные ошибки, чтобы вы могли использовать
catch (err) { if (err instanceof ElidoRateLimitError) { … } }вместо парсинга JSON в блоках catch. - Итераторы пагинации, благодаря которым эндпоинты со списками предоставляют асинхронные итераторы или генераторы вместо ручной обработки курсоров.
SDK для Go дополнительно предоставляет доступ к базовому HTTP-клиенту для инструментирования — это полезно, если вы хотите подключить его к существующей системе трассировки. Полный обзор представлен на странице возможностей API + SDKs; справочник API опубликован в /docs/api-reference.
Доступ к аналитике#
Эндпоинты аналитики предназначены только для чтения и находятся по адресу /v1/workspaces/{id}/analytics/. Самые частые запросы:
GET .../links/{id}/clicks?from=…&to=…— «сырые» события кликов с пагинацией. Полезно для пайплайнов экспорта.GET .../timeseries?from=…&to=…&bucket=day— агрегированное количество кликов за период времени.GET .../breakdown/country?from=…&to=…— распределение по странам.GET .../breakdown/referrer?from=…&to=…— распределение по реферерам.
Поток «сырых» событий кликов — самый объемный. Рабочее пространство с 10 млн кликов в месяц генерирует около 600 МБ JSON-данных в месяц. Для экспорта в таком масштабе в руководстве по экспорту в ClickHouse описан механизм массового экспорта, который обходит JSON-оболочку и стримит данные напрямую из хранилища аналитики.
Вебхуки для событий кликов#
Вебхуки — это противоположность опросу (polling): вместо того чтобы вы спрашивали API о новых кликах, API доставляет их на ваш эндпоинт. Настройка выполняется в /settings/webhooks:
await elido.webhooks.create({
url: "https://your-app.example/webhooks/elido",
events: ["link.click", "link.created", "link.expired"],
secret: process.env.WEBHOOK_SIGNING_SECRET,
});
Каждая доставка включает заголовок Elido-Signature, содержащий HMAC-SHA256 тела запроса с вашим общим секретом. Обязательно проверяйте подпись перед обработкой — без этого любой отправитель сможет отправить запрос на ваш эндпоинт вебхука, выдавая себя за Elido.
Семантика доставки — «как минимум один раз» (at-least-once) с экспоненциальной задержкой до максимального срока удержания в 72 часа. Подробности о структуре данных и поведении при повторах читайте в посте вебхуки против опроса.
Практический пример: автоматизация кампаний#
Интеграция, которая чаще всего мотивирует к переходу на API, выглядит так. Ваша система автоматизации маркетинга создает кампанию в Customer.io или HubSpot. При публикации кампании срабатывает хук. Ваш обработчик создает короткую ссылку, прикрепляет ее к записи кампании и отправляет обратно в инструмент управления кампаниями для подстановки в шаблон письма.
На TypeScript:
import { Elido } from "@elido/sdk";
const elido = new Elido({ token: process.env.ELIDO_TOKEN! });
export async function onCampaignPublished(campaign: Campaign) {
const link = await elido.links.create(
{
destinationUrl: campaign.destinationUrl,
tags: ["campaign", `campaign:${campaign.id}`, campaign.channel],
utm: {
source: campaign.channel,
medium: "email",
campaign: campaign.slug,
},
metadata: { campaign_id: campaign.id, batch: campaign.batchId },
},
{
idempotencyKey: `campaign-${campaign.id}-link`,
},
);
await campaignStore.update(campaign.id, { shortUrl: link.shortUrl });
return link;
}
Ключ идемпотентности вычисляется на основе ID кампании. Если хук публикации кампании сработает дважды (а вебхуки доставляются по принципу at-least-once), второй вызов вернет ту же ссылку без создания дубликата. Поле metadata хранит ваши собственные ключи связки, чтобы вы могли соотнести события кликов Elido с кампанией без парсинга тегов.
Для сквозной атрибуции кампаний с UTM-шаблонами и передачей конверсий ознакомьтесь с материалом отслеживание UTM от начала до конца.
Чего еще нет в API#
Две вещи, о которых часто спрашивают, но которые пока недоступны:
- Единый GET-запрос аналитики для ссылки, возвращающий все распределения за один вызов. Текущая модель требует отдельных вызовов для кликов, стран, рефереров, устройств и временных рядов. Агрегация есть в планах; пока что SDK параллелят эти запросы с помощью одного вспомогательного метода.
- Повтор (replay) вебхуков через API. Панель управления отображает историю доставок вебхуков и поддерживает повторную отправку, но в API этого пока нет. Это также стоит в планах.
Если функция есть в спецификации OpenAPI, она поддерживается. Если она есть в этом посте, но ее нет в спецификации — считайте ее планируемой, а не гарантированной.
Что еще почитать#
- Что такое смарт-ссылки — базовый материал по разделу функций; рассказывает, как движок редиректа обрабатывает ссылки на границе сети (edge).
- Вебхуки против опроса для отслеживания кликов — когда какой паттерн интеграции использовать.
- Отслеживание конверсий на стороне сервера через короткие ссылки — расширение использования API в потоке передачи конверсий.
- Массовый импорт кампаний из Google Таблиц — практический пример использования эндпоинта массовых операций.
- Операционное руководство: гайд по MCP-серверу для подключения поверхности API Elido к Claude, Cursor и другим клиентам с поддержкой MCP.
- Разделы продукта:
/features/api-sdksи/solutions/developers.