API скорочувача URL — це одна з найменших інтеграцій у беклозі типової інженерної команди. Три ендпоїнти, заголовок автентифікації, JSON payload. Сторінка документації обіцяє перший виклик за п'ять хвилин. Потім з'являється продакшн-трафік, логіка повторних спроб створює дублікати посилань, дашборд заповнюється варіантами /foo-1, /foo-2, /foo-3 одного й того самого пункту призначення, і хтось створює тікет.
Цей пост присвячений реальній інтеграції. Автентифікація, перший виклик, чотири ендпоїнти, які охоплюють більшість випадків використання, ідемпотентність, обробка помилок, ліміти запитів та нюанси продакшну, які зазвичай пропускають у швидких стартах. Приклади коду на TypeScript, Python, Go, Ruby та PHP — перші три через офіційні SDK (@elido/sdk, elido-python, github.com/elido/elido-go), останні два через прості HTTP-клієнти.
Попередні вимоги#
Увійдіть у дашборд, перейдіть до /settings/api та створіть персональний токен доступу. Токени мають область дії воркспейсу (workspace-scoped) — токен, виданий у воркспейсі A, не може створювати посилання у воркспейсі B. Токени сервісних акаунтів (для систем CI, внутрішніх інструментів, інтеграції machine-to-machine) створюються на тому ж екрані в тарифах Pro та вище; вони мають явні області доступу (scopes: 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— параметри кампанії, які додаються до цільової адреси під час редиректу.expires_at— часова мітка ISO 8601, після якої посилання повертає 410 Gone.password— якщо встановлено, сторінка редиректу вимагатиме пароль перед переходом.metadata— довільний JSON-об'єкт, який редирект не інтерпретує; корисно для ваших власних ключів зв'язку (join keys).
Кастомний слаг — це поле, яке часто створює проблеми в продакшні. Якщо ви передасте слаг, який уже використовується іншим посиланням у тому ж воркспейсі, 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" },
);
Сценарій використання — цикл повторних спроб (retry loop). Якщо ваше завдання створює посилання як частину обробки замовлення, згенеруйте ключ ідемпотентності на основі 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— токен не має необхідної області доступу (scope). Перевірте список областей доступу токена в/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 реалізують експоненціальну витримку (backoff) з джиттером для помилок 429, 500, 502, 503 та 504. Вони не повторюють запити для 400, 401, 403, 404 або 409 — це помилки програмування або конфлікти бізнес-логіки, а не тимчасові збої. Кастомні HTTP-клієнти мають слідувати цій же схемі; повторення 400 з тим самим payload не дасть іншого результату.
request_id у тілі помилки — це поле, яке слід вказувати в тікетах підтримки. За цим ID ми можемо відстежити будь-який запит через аудит-лог, лог програми та метрики платформи — і ми не зможемо відстежити запит без нього.
Ліміти запитів (Rate limits)#
Опубліковані ліміти становлять 100 запитів на секунду (req/s) на воркспейс для Pro, 500 для Business та індивідуальний ліміт для Enterprise. Безкоштовний тариф — 10 req/s.
Стан лімітів відображається у трьох заголовках кожної відповіді 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 пропонують чотири речі, які швидко окупаються:
- Автоматичне повторення з витримкою (backoff) для відповідних статус-кодів.
- Генерація ключів ідемпотентності, якщо вони не надані явно.
- Типізовані помилки, щоб ви могли використовувати
catch (err) { if (err instanceof ElidoRateLimitError) { … } }замість парсингу JSON у блоках catch. - Ітератори пагінації, завдяки чому ендпоїнти списків надають асинхронні ітератори або генератори замість ручної обробки курсорів.
Go SDK додатково надає доступ до базового 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=…— розподіл за джерелами переходу (referrer).
Потік сирих подій кліків є найбільшим. Воркспейс із 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 кампанії. Якщо хук публікації кампанії спрацює двічі (а так буває — доставка вебхуків гарантується "принаймні один раз"), другий виклик поверне те саме посилання без створення дубліката. Поле metadata містить ваші власні ключі зв'язку, тому ви можете зіставити події кліків Elido з вашою кампанією без парсингу тегів.
Для наскрізної атрибуції кампаній із шаблонами UTM та передачею конверсій ознайомтеся з постом основи відстеження UTM.
Чого ще немає в API#
Дві речі, про які часто запитують, але які наразі недоступні:
- Єдиний GET-запит аналітики для посилання, що повертає всі розподіли за один раз. Поточна модель вимагає окремих викликів для кліків, країн, реферерів, пристроїв та часових рядів. Агрегація є в планах; наразі SDK паралелізують ці запити за допомогою одного допоміжного методу.
- Повторне відтворення вебхуків (replay) через API. Дашборд показує історію доставок вебхуків і підтримує повтор; API поки що ні. Це також у планах розробки.
Якщо функція є в специфікації OpenAPI — вона підтримується. Якщо вона згадується в цьому пості, але її немає в специфікації — вважайте її запланованою, але не гарантованою.
Що ще почитати#
- Пояснення смарт-посилань — основа для кластера функцій; як механізм редиректу обробляє посилання на edge.
- Вебхуки проти опитування для відстеження кліків — коли і який патерн інтеграції використовувати.
- Відстеження конверсій на стороні сервера через короткі посилання — розширення API для потоку передачі конверсій.
- Масовий імпорт кампаній із Google Sheets — практичний приклад використання ендпоїнта bulk.
- Операційна інструкція: гайд по серверу MCP для підключення API Elido до Claude, Cursor та інших клієнтів, що підтримують MCP.
- Продуктові сторінки:
/features/api-sdksта/solutions/developers.