Три endpoint'и, заголовок авторизації, JSON-тіло. API скорочувача URL - одна з найпростіших інтеграцій у будь-якому беклозі, і quickstart дає вам робоче коротке посилання за кілька хвилин. Що quickstart пропускає - це все, що відбувається, коли інтеграція працює під навантаженням: rate limiter відкидає запити, транзитний 503 посеред пакетної операції, черга задач доставляє одне й те саме повідомлення двічі. Помилитесь тут - отримаєте дублікати посилань, втрачену роботу і шторм 429-помилок, що лише погіршує ситуацію.
Цей пост - компаньйон з налаштування продакшн-інтеграції до API quickstart. Він охоплює три механіки, що відрізняють демо від надійної інтеграції: ліміти запитів і як регулювати темп відносно них, які помилки повторювати і як відступати з backoff, та ключі ідемпотентності, що не дають повторній спробі створити друге посилання. Приклади використовують API Elido, але патерни однакові для будь-якого добре побудованого API скорочувача посилань. Якщо ви розглядаєте короткі посилання як інфраструктуру, якою управляєте з коду, ширше обгрунтування цього є в короткі посилання як Terraform.
Ліміти запитів: token bucket і заголовок Retry-After
Elido тарифікує API за допомогою token bucket, прив'язаного до робочого простору. Стійкі швидкості: 10 запитів на секунду на Free, 30 на Pro і 100 на Business, з burst-місткістю 20, 60 і 200. Наприклад, на Pro повний bucket дозволяє відправити 60 запитів одразу, перш ніж швидкість повернеться до стійких 30 на секунду. Зверху діє окремий bucket на API-ключ, тож один галасливий ключ не з'їсть увесь робочий простір. Більшість задач зі створення посилань вкладаються в burst і взагалі не відчувають ліміту.
Здогадуватися, коли спрацював ліміт, не потрібно. Відхилений запит несе три сигнали:
Retry-After- скільки секунд чекати перед повтором (зараз1).X-RateLimit-Scope- який bucket спрацював:workspaceабоapi_key.- JSON-тіло -
{"error":"workspace rate limit exceeded"}, що називає ліміт.
Заголовків із залишком токенів немає, тому темп регулюйте на боці клієнта: тримайте паралелізм не вище стійкої швидкості вашого тарифу, а на першому 429 зупиніться й дочекайтесь Retry-After, замість того щоб мчати в стіну відмов. Завчасне регулювання тримає пропускну здатність рівною; сліпі повтори після кожної відмови марнують запити й, якщо всі клієнти повторюють одночасно, породжують thundering herd.
Коли потрібно створити багато посилань, не зациклюйте endpoint одиночного створення. POST /v1/workspaces/{workspace_id}/links/bulk приймає до 100 посилань в одному запиті. Один bulk-виклик переносить сотню посилань за одну поїздку, тоді як сотня одиночних викликів витратить сотню токенів і більшу частину burst. Саме так імпорт із Google Sheets переносить посилання цілої кампанії, не зачіпаючи лімітер.
429 Too Many Requests - статус, який RFC 6585 резервує саме для цього - приходить із заголовком Retry-After, що каже, скільки секунд чекати. Поважайте його. Це число лімітера каже, коли з'явиться токен, а це краща інформація, ніж будь-яка здогадка вашого backoff.
Повторні спроби: які коди і як відступати з backoff
Не кожна помилка варта повторної спроби, а повторення неправильної - це шлях перетворити малий збій на аварію. Розподіліть відповіді на два кошики.
Повторюйте ці, бо вони транзитні: 429 (занадто швидко), і 500, 502, 503, 504 (серверна або gateway-помилка, яка може зникнути сама). Не повторюйте ці, бо той самий запит провалиться ідентично: 400 (некоректне payload), 401 (токен відсутній або неправильний), 403 (токен не має потрібного scope), 404 (ресурс не існує або не ваш), і 409 (конфлікт slug або редагування з застарілою версією). Перший кошик - «зачекайте і спробуйте знову». Другий - «виправте код або вхідні дані». Повторення 400 у щільному циклі просто перетворює баг у DoS-атаку на самого себе.
Для кодів, що підлягають повторній спробі, алгоритм, який має значення, - exponential backoff з jitter. Простий exponential backoff - подвоювати час очікування з кожною спробою - все одно синхронізує клієнтів, бо кожен клієнт, що впав в один момент, також повторює в одні й ті ж моменти. Додавання випадковості розосереджує їх. Публікація AWS про exponential backoff and jitter є канонічним посиланням і показує, чому версія з jitter різко скорочує конкуренцію. Компактний варіант на TypeScript:
const RETRYABLE = new Set([429, 500, 502, 503, 504]);
async function withRetry<T>(
call: () => Promise<Response>,
max = 5,
): Promise<Response> {
let attempt = 0;
while (true) {
const res = await call();
if (res.ok || !RETRYABLE.has(res.status) || attempt >= max) return res;
// Honor server guidance first; otherwise back off exponentially with full jitter.
const retryAfter = Number(res.headers.get("retry-after"));
const base =
Number.isFinite(retryAfter) && retryAfter > 0
? retryAfter * 1000
: Math.min(1000 * 2 ** attempt, 20_000);
const wait = Math.random() * base; // full jitter
await new Promise((r) => setTimeout(r, wait));
attempt++;
}
}
Три речі роблять це безпечним, а не небезпечним. Обмежує кількість спроб, тому стійкий збій виразно провалюється замість нескінченного кручення. Поважає Retry-After, коли сервер його надсилає, і лише тоді відступає до обчисленого backoff. І джитерує, щоб флот воркерів, що відновлюється від одного збою, не кинувся одночасно. Поки опубліковано лише Go SDK; SDK для TypeScript, Python та Ruby ще не в реєстрах пакетів, тож цими мовами ця невелика обгортка над звичайним HTTP - увесь клієнт, який вам потрібен.
Є одне правило, що пов'язує повторні спроби з наступним розділом: повторна спроба create безпечна лише тоді, коли create є ідемпотентним. Інакше кожна спроба ризикує породити друге посилання.
Ідемпотентність: як не створювати дублікати посилань
Класична помилка виглядає так. Ваш воркер створює коротке посилання, посилання створюється, але 200 не повертається назад - з'єднання обривається на зворотному шляху. Воркер бачить timeout, вважає, що сталася помилка, і повторює. Тепер у вас два посилання для однієї кампанії. При масштабуванні дашборд заповнюється /foo, /foo-1, /foo-2, і дублікати спотворюють усі подальші звіти.
Ключі ідемпотентності закривають цей розрив. Надішліть заголовок Idempotency-Key у запиті, що змінює стан - будь-який рядок до 255 символів - і сервер зберігає відповідь, прив'язану до нього. Надішліть той самий ключ знову - отримаєте початкову відповідь назад, із тим самим статусним кодом і тілом, без повторного виконання операції. Патерн той самий, що Stripe документує для ідемпотентних запитів, і це стандартний спосіб зробити ненадійну мережу безпечною для записів.
Деталь, що вирішує все, - звідки береться ключ. Не генеруйте випадковий ключ для кожної спроби - це знищує сенс, бо тоді кожна повторна спроба виглядає як нова операція. Виводьте його зі стабільного бізнес-ідентифікатора, щоб одна й та сама логічна дія завжди давала однаковий ключ:
const res = await withRetry(() =>
fetch(`https://api.elido.app/v1/workspaces/${WORKSPACE_ID}/links`, {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.ELIDO_API_KEY}`,
"Content-Type": "application/json",
"Idempotency-Key": `order-${order.id}-link`,
},
body: JSON.stringify({ destination_url: order.landingUrl }),
}),
);
const link = await res.json();
Тепер повторна спроба тієї ж задачі знову несе order-12345-link, потрапляє на збережену відповідь і повертає вже існуюче посилання. Рівно одне посилання на замовлення, скільки б разів черга не передоставила. Саме це дозволяє безпечно поєднувати цикл backoff вище зі створеннями: повторна спроба і ключ ідемпотентності - дві половини одної гарантії.
Два обмеження, які варто пам'ятати. Ключ прив'язаний до робочого простору: той самий ключ у двох робочих просторах створить два посилання - це правильно для multi-tenant API, але дивує команди, що вважають ключі глобальними. І кеш не є вічним - у Elido він зберігається 24 години, прив'язаний до (workspace, key). Повторна спроба в межах вікна дедуплікує; повторна спроба через три дні, від задачі, яка нарешті відновилась, створить нове посилання. Для багатоденних пакетних задач не покладайтесь лише на ключ. Зберігайте ID посилання, повернений при першому успіху, і перевіряйте його перед повторним викликом create. IETF стандартизує цей заголовок у чернетці Idempotency-Key, і застереження про вікно в 24 години також там зазначене.
Якщо ви налаштовуєте API-інтеграцію сьогодні і хочете, щоб вона витримала власні повторні спроби, починайте з безкоштовного робочого простору, згенеруйте service-account токен і поставте ключ ідемпотентності в самий перший виклик create - а не додавайте його після того, як з'являться дублікати.
Збираємо все разом
Продакшн-якісний виклик create - це три механіки, складені докупи. Тримайте темп клієнта на рівні стійкої швидкості вашого тарифу, щоб рідко отримувати 429. Огортайте виклик у jittered backoff, що повторює лише транзитні коди і поважає Retry-After. Несіть ключ ідемпотентності, виведений з бізнес-ID, щоб повторна спроба була безпечною. З простим fetch обгортка вище дає перші два, а ключ надаєте ви:
export async function shortenForOrder(order: Order) {
const res = await withRetry(() =>
fetch(`https://api.elido.app/v1/workspaces/${WORKSPACE_ID}/links`, {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.ELIDO_API_KEY}`,
"Content-Type": "application/json",
"Idempotency-Key": `order-${order.id}-link`,
},
body: JSON.stringify({
destination_url: order.landingUrl,
tags: [`order:${order.id}`],
}),
}),
);
if (res.status === 429) {
// withRetry gave up and we are still limited. Defer the job.
throw new RetryableJobError(Number(res.headers.get("retry-after")) || 1);
}
if (!res.ok) throw new Error(`create failed: ${res.status}`); // non-retryable
return res.json();
}
Нічого екзотичного. Це та сама дисципліна, якої заслуговує будь-який API зі значним числом записів, застосована до посилань. Нагорода - інтеграція, що робить правильні речі під навантаженням, замість того щоб тихо псувати ваш інвентар посилань. Щодо read-сторони того ж API - отримання даних кліків без перевантаження limiter'а - компроміси описані в webhooks vs polling для відстеження кліків, а повна поверхня endpoint'ів є на сторінці API and SDKs і в огляді developer solutions.
Пов'язані статті в блозі
- API скорочувача URL: quickstart за 30 хвилин на п'яти мовах
- Короткі посилання як Terraform: управління посиланнями як кодом
- Webhooks vs polling для відстеження кліків
- Масовий імпорт кампаній з Google Sheets
- Підключення API до Claude та Cursor через MCP
- Безкоштовний API скорочення URL: форма запиту, авторизація та ліміти
Поширені запитання
Які ліміти запитів у API скорочувача URL?
У Elido ліміти діють на рівні робочого простору й залежать від тарифу, за схемою token bucket: 10 запитів на секунду на Free (burst 20), 30 на Pro (burst 60) і 100 на Business (burst 200). Зверху діє окремий bucket на кожен API-ключ. Коли ліміт спрацьовує, API повертає 429 із заголовками Retry-After та X-RateLimit-Scope, що вказує, який bucket вичерпано, тож клієнт знає, скільки чекати. Масове створення приймає до 100 посилань за запит, тому bulk-endpoint - правильний інструмент для великих задач.
Які API-помилки варто повторювати?
Повторюйте лише транзитні збої: 429 (ліміт вичерпано), 500, 502, 503 і 504. Використовуйте exponential backoff з jitter і обмеженням кількості спроб. Ніколи не повторюйте 400, 401, 403, 404 або 409 - це помилки валідації, проблеми з авторизацією або конфлікти, і той самий запит знову провалиться. Поєднуйте повторні спроби з ключем ідемпотентності, щоб повторний виклик create не породив дублікат посилання.
Що таке ключ ідемпотентності і коли він потрібен?
Ключ ідемпотентності - це непрозорий рядок, який ви надсилаєте в запиті, що змінює стан, щоб сервер міг розпізнати повторну спробу і повернути початковий результат замість того, щоб виконати дію двічі. Він потрібен щоразу, коли create або update може бути повторений - а це завжди, бо мережі відмовляють і черги задач доставляють хоча б раз. Виводьте ключ зі стабільного бізнес-ідентифікатора, наприклад ID замовлення, щоб одна й та сама логічна операція завжди несла однаковий ключ.
Як уникнути створення дублікатів коротких посилань?
Надсилайте ключ ідемпотентності, виведений зі стабільного бізнес-ідентифікатора, у кожному виклику create. Якщо виклик повторюється, сервер повертає спочатку створене посилання, а не карбує нове. Для задач, що можуть повторюватись через кілька днів - за межами вікна кешу ідемпотентності - також зберігайте ID посилання, повернутий при першому успіху, і шукайте його перед повторним викликом create.
Як довго діє ключ ідемпотентності?
У Elido кеш ідемпотентності зберігається 24 години, прив'язаний до робочого простору і ключа. Повторна спроба в межах цього вікна повертає збережену відповідь; повторна спроба після - вважається свіжим запитом і створить новий ресурс. Для багатоденних пакетних задач зберігайте ID посилання при першому успіху, а не покладайтесь лише на ключ.
Спробуйте Elido
Вставте URL - отримайте коротке посилання
Без реєстрації. Посилання живе 30 днів. Зареєструйтесь, щоб зберегти назавжди.
Без реєстрації. Зареєструйтеся безкоштовно і отримайте 50 посилань на місяць.