API URL-шортенера - одна из менее значительных интеграций в типичном бэклоге инженерной команды. Три endpoint, заголовок авторизации, JSON payload. Страница документации обещает первый вызов за пять минут. Затем приходит продакшен-трафик, логика повторов создает дублирующиеся ссылки, дашборд заполняется вариантами /foo-1, /foo-2, /foo-3 одного и того же назначения, и кто-то заводит тикет.
Этот пост разбирает реальную интеграцию. Авторизацию, первый вызов, четыре endpoint, которые покрывают большинство сценариев использования, идемпотентность, обработку ошибок, лимиты запросов и подводные камни продакшена, которые пятиминутный быстрый старт обходит стороной. Примеры кода на TypeScript, Python, Go, Ruby и PHP - первые три через официальные SDK (@elido/sdk, elido-python, github.com/elido/elido-go), последние два через обычные HTTP-клиенты.
Что нужно заранее
Войдите в дашборд, перейдите на /dashboard/api-keys и создайте API-ключ (он начинается с elido_). Токены привязаны к рабочему пространству - токен, выпущенный в рабочем пространстве A, не может создавать ссылки в рабочем пространстве B. Токены машинных пользователей (для CI-систем, внутренних инструментов, интеграций machine-to-machine) создаются в /dashboard/machine-users и ротируются независимо от личных ключей. Оба вида несут заранее заданную роль в рабочем пространстве (viewer, editor или admin), а не разрешения по каждому endpoint, так что выдавайте CI-заданию роль editor, если оно только создает ссылки. В руководстве по разрешениям API-ключей перечислено, к чему имеет доступ каждая роль, включая объяснение, почему для изменений webhook нужна роль admin.
Базовый URL - https://api.elido.app/v1. Домены редиректа (f.elido.me, s.elido.me, b.elido.me) отделены от поверхности API. Ваши короткие ссылки разрешаются на домене редиректа; API служит для их создания, изменения и чтения.
Спецификация OpenAPI опубликована по адресу https://elido.app/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 в примере выше - генерируется сервером, если вы не передали slug в запросе. Алфавит slug - base62 ([0-9A-Za-z]); длина по умолчанию - шесть символов.
Четыре endpoint, которыми вы будете реально пользоваться
В API больше четырех endpoint, но большинство интеграций остаются в пределах этого набора.
Создание ссылки
POST /v1/links принимает целевой URL плюс необязательные поля:
slug- slug, который вы выбираете сами (должен быть уникален на домене).domain_id- для ссылок на собственном домене; в/v1/linksиспользуется короткий домен по умолчанию вашего тарифа, если поле не указано. Для пути с областью рабочего пространства/v1/workspaces/{workspace_id}/linksоно обязательно.title- метка, отображаемая в дашборде.tags- массив произвольных строк для организации.expires_at- метка времени RFC 3339, после которой ссылка возвращает 410 Gone.redirect_status-301,302(по умолчанию) или307.password- пока не принимается при создании; сразу после этого задайте его черезPATCH, и редирект будет сначала показывать страницу с паролем.utmиmetadata- запланированы. Сегодня добавляйте UTM-параметры прямо вdestination_url, а собственные ключи связывания храните вtags.
Собственный slug - это поле, которое кусает команды в продакшене. Если вы передадите slug, уже занятый другой ссылкой на том же домене, API вернёт 409 Conflict. Наивный обработчик повторов, добавляющий счётчик (my-slug-1, my-slug-2), создаёт проблему дублирующихся ссылок, описанную во введении. Правильное поведение при повторе описано в разделе об идемпотентности ниже.
Чтение ссылки
GET /v1/links/{id} возвращает полную запись ссылки, включая short_url и всю конфигурацию. Количество кликов в записи ссылки отсутствует, оно приходит из расположенных ниже endpoint аналитики. ID ссылки - канонический идентификатор: slug может меняться, ID - нет.
GET /v1/links?host=…&tags=…&limit=… перечисляет ссылки в рабочем пространстве с фильтрами. Пагинация курсорная; next_cursor в ответе непрозрачен и передаётся обратно как параметр запроса cursor в следующем запросе.
Обновление ссылки
PATCH /v1/links/{id} принимает те же поля, что и создание. Самые частые обновления: изменение целевого URL (полезно для ротации кампании без перепечатки QR-кодов), изменение тегов, продление expires_at. Slug меняется через тот же PATCH, если передать новый slug. Старый slug сразу перестаёт разрешаться; отдельный endpoint переименования, сохраняющий 301 со старого slug на период хранения, запланирован, но ещё не реализован.
Удаление ссылки
DELETE /v1/links/{id} выполняет мягкое удаление и возвращает 204 No Content. Ссылка перестаёт перенаправлять и исчезает из ответов запросов списка и чтения. Раздел корзины с endpoint восстановления и 90-дневным окном до окончательного удаления запланирован; сегодня API-вызова для восстановления удалённой ссылки нет.
Ключи идемпотентности
Каждый изменяющий запрос - 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- ошибка валидации payload. Полеmessageперечисляет конкретные поля. Не повторяйте запрос; исправьте payload.401 unauthorized- токен отсутствует или недействителен. Не повторяйте запрос без ротации токена.403 forbidden- роль токена не разрешает действие (ключ уровняviewerне может создавать ссылки). Проверьте роль ключа на/dashboard/api-keys.404 not_found- ресурс не существует, либо у токена нет к нему доступа (мы возвращаем 404, а не 403, чтобы не раскрывать существование ресурса неавторизованным вызывающим).409 conflict- slug уже занят, либо обнаружено одновременное редактирование (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 и тем же payload не даст другого результата.
request_id в теле ошибки - поле, которое нужно указывать в тикетах поддержки. Мы можем отследить любой запрос по этому ID через журнал аудита, лог приложения и метрики платформы - и не можем отследить запрос без него.
Лимиты запросов
Опубликованные лимиты - 100 запросов в секунду на рабочее пространство на тарифе Pro, 500 на Business и согласованный лимит на Enterprise. Бесплатный тариф - 10 запросов в секунду.
Состояние лимита отображается в трех заголовках ответа на каждый ответ API:
X-RateLimit-Limit- текущий лимит в секунду.X-RateLimit-Remaining- количество запросов, оставшихся в текущей секунде.X-RateLimit-Reset- метка времени Unix, когда сбрасывается бакет.
Лимит 100/с реализован как token bucket с емкостью всплеска 200 - это значит, что вы можете сразу отправить 200 запросов, если бакет полон, а затем перейти на устойчивую скорость 100/с. Большинство заданий по созданию коротких ссылок комфортно умещаются во всплеск; интеграциям с большим объемом аналитики, которые постранично проходят исторические события кликов, пригодится запас тарифа Pro.
Для массовых операций endpoint POST /v1/links/bulk принимает до 100 ссылок за запрос и засчитывается как одна единица лимита. Это правильный endpoint для любого задания, создающего больше сотни ссылок за раз. Более глубокий разбор темпа относительно token bucket, выбора кодов статуса для повтора и того, как ключи идемпотентности не дают повторам дублировать ссылки, - в статье лимиты запросов, повторы и идемпотентность в продакшене.
Что дают SDK сверх обычного HTTP
Официальные SDK поставляют четыре вещи, которые быстро окупаются:
- Автоматический повтор с задержкой для кодов статуса, допускающих повтор.
- Генерация ключей идемпотентности, если они не заданы явно.
- Типизированные ошибки, так что можно написать
catch (err) { if (err instanceof ElidoRateLimitError) { … } }вместо разбора JSON в блоках catch. - Итераторы пагинации, так что списочные endpoint отдают асинхронные итераторы или генераторы вместо ручной работы с курсором.
Go SDK дополнительно предоставляет доступ к базовому HTTP-клиенту для инструментирования - полезно, если вы хотите встроить его в вашу существующую систему трассировки. Страница функции API + SDK описывает полную поверхность; справочник API опубликован по адресу /docs/api-reference.
Доступ к аналитике
Endpoint аналитики доступны только для чтения и находятся в /v1/workspaces/{id}/analytics/; руководство по API аналитики ссылок перечисляет все отчеты, их параметры и формы ответов. Самые частые запросы:
GET .../clicks/recent?from=…&to=…- отдельные клики, от новых к старым, с пагинацией черезnext_cursor. Полезно для экспортных конвейеров.GET .../timeseries?from=…&to=…&interval=day- количество кликов по временным интервалам;intervalможет бытьhourилиday, аtzзадаёт часовой пояс интервалов.GET .../breakdown/country?from=…&to=…- разбивка по географии.GET .../breakdown/referrer?from=…&to=…- разбивка по источникам перехода.
Остальные отчёты: summary, links/top, остальные разбивки (host, device, browser, destination) и списки лидеров (top-countries, top-regions, top-cities, top-referrers, top-destinations). from и to - даты в формате YYYY-MM-DD, причём to не включается; добавляйте link_id, чтобы сузить любой отчёт до одной ссылки, и limit, чтобы задать размер разбивок и списков лидеров.
Поток сырых событий кликов - самый объемный. Рабочее пространство с 10 млн кликов в месяц выдает около 600 МБ JSON сырых данных о событиях в месяц. Для экспорта в таком масштабе руководство по экспорту аналитики описывает механизм массового экспорта, который обходит JSON-обертку и передает данные напрямую из аналитического хранилища.
Webhook для событий ссылок
Webhook - это противоположность опроса: вместо того чтобы вы спрашивали API, что изменилось, API сам доставляет события ссылок и доменов на ваш endpoint. Настраивается в /dashboard/webhooks:
await elido.webhooks.create({
url: "https://your-app.example/webhooks/elido",
events: ["link.created", "link.updated", "link.expired"],
secret: process.env.WEBHOOK_SIGNING_SECRET,
});
Событие click.created на каждый клик пока в дорожной карте, но еще недоступно, так что сегодня данные о кликах приходят из endpoint аналитики. Каждая доставка включает заголовок X-Elido-Signature (также отправляется как X-Webhook-Signature) со значением v1=<hex>: HMAC-SHA256 с ключом вашего секрета endpoint, вычисленный по значению X-Webhook-Timestamp, точке и сырому телу запроса. Проверяйте подпись перед обработкой - без этого любой вызывающий может отправить запрос на ваш webhook-endpoint и выдать себя за Elido.
Семантика доставки - «не менее одного раза»: неудачная доставка повторяется с задержкой в несколько минут, всего по умолчанию три попытки. Подробную форму и поведение повторов сравнивает статья webhook против опроса, которая разбирает оба паттерна интеграции.
Разобранный пример: автоматизация кампаний
Интеграция, которая чаще всего мотивирует использование 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}`,
`batch:${campaign.batchId}`,
campaign.channel,
],
},
{
idempotencyKey: `campaign-${campaign.id}-link`,
},
);
await campaignStore.update(campaign.id, { shortUrl: link.shortUrl });
return link;
}
Ключ идемпотентности выводится из ID кампании. Если хук публикации кампании срабатывает дважды (а так и бывает - доставка webhook гарантирует «не менее одного раза»), второй вызов вернет ту же ссылку, не создав дубликат. Теги campaign: и batch: содержат ваши собственные ключи связывания, чтобы можно было сопоставить события кликов Elido с кампанией; отдельное поле metadata для этого запланировано. UTM-параметры должны находиться в самой campaign.destinationUrl, пока поле utm не появится.
Для сквозной атрибуции кампаний с UTM-шаблонами и пересылкой конверсий полный конвейер разбирает опорная статья про отслеживание UTM.
Чего пока нет в API
Две вещи, о которых часто спрашивают, но которых пока нет:
- Единый GET аналитики по одной ссылке, возвращающий все разбивки за один вызов. Текущая модель требует отдельных вызовов для кликов, страны, источника перехода, устройства и временного ряда. Агрегация пока в дорожной карте; сейчас выполняйте запросы параллельно из собственного кода.
- Повтор доставки webhook из API. Дашборд показывает историю доставки webhook и поддерживает повтор; API пока нет. Это тоже в дорожной карте.
Если функция есть в спецификации OpenAPI, она поддерживается. Если она упомянута в этом посте, но не в спецификации, считайте ее запланированной, а не гарантированной.
Читайте также
- Умные ссылки: как это работает - опорная статья кластера функций; описывает, как движок редиректа разрешает ссылку на edge.
- Webhook против опроса для отслеживания кликов - когда использовать какой паттерн интеграции.
- Серверное отслеживание конверсий через короткие ссылки - расширение API в поток пересылки конверсий.
- Массовый импорт кампаний из Google Sheets - разобранный пример массового endpoint.
- API URL-шортенера: лимиты запросов, повторы, идемпотентность - усиление интеграции для продакшен-трафика.
- Разрешения API-ключей для работы со ссылками - ключи, привязанные к рабочему пространству, ограничения ролей и ротация.
- Бесплатный API URL-шортенера: работающие примеры кода - вызов создания на curl, JavaScript, Python и Go, и что ограничивают бесплатные тарифы.
- API аналитики ссылок: получение статистики кликов с помощью API-ключа - все отчеты, параметры запросов и ежедневный скрипт для Slack.
- Операционный разбор: руководство по MCP-серверу для подключения поверхности API Elido к Claude, Cursor и другим MCP-совместимым клиентам.
- Поверхность продукта:
/features/api-sdksи/solutions/developers.
Попробуйте Elido
Вставьте URL - получите короткую ссылку
Без регистрации. Ссылка живёт 30 дней. Зарегистрируйтесь, чтобы оставить её навсегда.
Бесплатно, без регистрации · 2 в день