API skracacza URL to jedna z mniejszych integracji w typowym backlogu zespołu inżynierskiego. Trzy punkty końcowe, nagłówek autoryzacji, payload JSON. Strona dokumentacji obiecuje pierwsze wywołanie w pięć minut. Potem pojawia się ruch produkcyjny, logika ponowień tworzy duplikaty linków, panel administracyjny wypełnia się wariantami /foo-1, /foo-2, /foo-3 tego samego celu, a ktoś otwiera zgłoszenie w systemie ticketowym.
Ten wpis przeprowadzi Cię przez rzeczywistą integrację. Autoryzacja, pierwsze wywołanie, cztery punkty końcowe pokrywające większość przypadków użycia, idempotencja, obsługa błędów, limity zapytań i produkcyjne haczyki, które pięciominutowy szybki start zazwyczaj pomija. Przykłady kodu w TypeScript, Python, Go, Ruby i PHP — pierwsze trzy z wykorzystaniem oficjalnych SDK (@elido/sdk, elido-python, github.com/elido/elido-go), dwa ostatnie przy użyciu zwykłych klientów HTTP.
Wymagania wstępne#
Zaloguj się do panelu kontrolnego, przejdź do /settings/api i utwórz osobisty token dostępu. Tokeny mają zakres ograniczony do przestrzeni roboczej (workspace) — token wydany w przestrzeni A nie może tworzyć linków w przestrzeni B. Tokeny kont usługowych (dla systemów CI, wewnętrznych narzędzi, integracji machine-to-machine) tworzy się na tym samym ekranie w planach Pro i wyższych; mają one jawne zakresy (links:write, analytics:read, domains:write) i są rotowane niezależnie od tokenów osobistych.
Adres bazowy URL to https://api.elido.app/v1. Domeny przekierowań (f.elido.me, s.elido.me, b.elido.me) są oddzielone od warstwy API. Twoje krótkie linki rozwiązują się w domenie przekierowań; API służy do ich tworzenia, modyfikowania i odczytywania.
Specyfikacja OpenAPI jest opublikowana pod adresem https://api.elido.app/v1/openapi.json i jest zgodna z OpenAPI 3.1. Oficjalne SDK są generowane na podstawie tej specyfikacji i publikowane ponownie przy każdym wydaniu API; możesz również wygenerować własnego klienta w dowolnym języku obsługującym OpenAPI.
Pierwsze wywołanie#
Utwórz krótki link z adresu docelowego. Pięć linii w 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 (brak oficjalnego SDK — używamy 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'];
Wszystkie pięć metod daje ten sam rezultat. Body odpowiedzi zawiera krótki URL, kanoniczny ID linku, ID przestrzeni roboczej oraz znacznik czasu utworzenia. Slug — w powyższym przykładzie abc123 — jest generowany przez serwer, chyba że przekażesz custom_slug w żądaniu. Alfabet sluga to base62 ([0-9A-Za-z]); domyślna długość to sześć znaków.
Cztery punkty końcowe, których faktycznie będziesz używać#
API posiada więcej niż cztery punkty końcowe, ale większość integracji ogranicza się do tego zestawu.
Tworzenie linku#
POST /v1/links przyjmuje adres docelowy oraz opcjonalne pola:
custom_slug— wybrany przez Ciebie slug (musi być unikalny w ramach przestrzeni roboczej).domain_id— dla linków w domenach niestandardowych; w przypadku pominięcia używana jest główna domena przestrzeni roboczej.tags— tablica dowolnych ciągów znaków służąca do organizacji.utm— parametry kampanii dodawane do celu w momencie przekierowania.expires_at— znacznik czasu ISO 8601, po którym link zwraca 410 Gone.password— jeśli ustawione, przekierowanie wyświetla stronę hasła przed przesłaniem dalej.metadata— nieprzejrzysty obiekt JSON, którego mechanizm przekierowania nie interpretuje; przydatny dla Twoich własnych kluczy powiązań.
Własny slug (custom slug) to pole, które sprawia problemy zespołom na produkcji. Jeśli przekażesz slug używany już przez inny link w tej samej przestrzeni roboczej, API zwróci 409 Conflict. Naiwny mechanizm ponowień, który dodaje licznik (my-slug-1, my-slug-2), tworzy problem duplikowania linków opisany we wstępie. Prawidłowe zachowanie przy ponowieniach opisano w sekcji dotyczącej idempotencji poniżej.
Odczyt linku#
GET /v1/links/{id} zwraca pełny rekord linku, w tym aktualną liczbę kliknięć, znacznik czasu ostatniego kliknięcia oraz całą konfigurację. ID linku jest identyfikatorem kanonicznym — slugi mogą się zmieniać (plany Pro+ wspierają zmianę nazwy sluga), identyfikatory ID pozostają stałe.
GET /v1/links?domain_id=…&tag=…&limit=… listuje linki w przestrzeni roboczej z filtrami. Stronicowanie opiera się na kursorach; next_cursor w odpowiedzi jest wartością nieprzejrzystą i wraca jako parametr zapytania cursor w następnym żądaniu.
Aktualizacja linku#
PATCH /v1/links/{id} przyjmuje te same pola co endpoint tworzenia. Najczęstsze aktualizacje to: zmiana adresu docelowego (przydatna przy rotacji kampanii bez konieczności ponownego drukowania kodów QR), zmiana tagów, przedłużenie expires_at. Aktualizacja sluga to oddzielny punkt końcowy POST /v1/links/{id}/rename, który obsługuje przekierowanie 301 ze starego sluga przez konfigurowalny okres retencji (domyślnie 30 dni).
Usuwanie linku#
DELETE /v1/links/{id} wykonuje usunięcie miękkie (soft-delete). Link zwraca 410 Gone przez następne 90 dni, po czym zostaje trwale usunięty. Widok kosza w panelu administracyjnym pokazuje miękko usunięte linki; możesz je przywrócić za pomocą panelu lub przez POST /v1/links/{id}/restore w ciągu 90-dniowego okna.
Klucze idempotencji#
Każde żądanie zmieniające stan — POST, PATCH, DELETE — przyjmuje nagłówek Idempotency-Key. Wartość nagłówka to nieprzejrzysty ciąg znaków o długości do 255 znaków; serwer przechowuje body odpowiedzi i kod statusu przez 24 godziny pod kluczem (workspace_id, idempotency_key) i zwraca zapisaną odpowiedź, jeśli ten sam klucz zostanie przesłany ponownie.
Oficjalne SDK generują klucze idempotencji automatycznie, gdy nie zostaną podane. Możesz to nadpisać:
const link = await elido.links.create(
{ destinationUrl: "https://shop.example.com/spring-sale" },
{ idempotencyKey: "order-12345-link" },
);
Przypadek użycia to pętla ponowień. Jeśli Twoje zadanie tworzy link w ramach przetwarzania zamówienia nadrzędnego, wygeneruj klucz idempotencji z ID zamówienia. Ponowienie tego samego zadania użyje tego samego klucza, trafi do pamięci podręcznej idempotencji i zwróci pierwotnie utworzony link zamiast tworzyć drugi.
Kluczowy haczyk: pamięć podręczna idempotencji żyje przez 24 godziny, nie wiecznie. Ponowienie zawieszonego zadania po trzech dniach utworzy nowy link. Jeśli integracja obejmuje wielodniowe partie danych, przechowuj ID linku zwrócone przez pierwsze udane utworzenie i sprawdź je przed ponownym wysłaniem żądania.
Drugi haczyk: idempotencja działa w ramach przestrzeni roboczej. Ten sam klucz w dwóch różnych przestrzeniach utworzy dwa linki. Jest to prawidłowa semantyka dla API obsługującego wiele przestrzeni roboczych, ale może zaskoczyć zespoły zakładające, że klucz jest unikalny globalnie.
Obsługa błędów#
API zwraca standardowe kody statusu HTTP oraz ustrukturyzowane body błędu:
{
"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
}
}
Kody, które będziesz widzieć najczęściej:
400 invalid_request— błąd walidacji payloadu. Polemessagezawiera listę konkretnych pól. Nie ponawiaj; popraw payload.401 unauthorized— brak tokena lub jest on nieprawidłowy. Nie ponawiaj bez rotacji tokena.403 forbidden— token nie posiada wymaganego zakresu. Sprawdź listę zakresów tokena pod adresem/settings/api.404 not_found— zasób nie istnieje lub token nie ma do niego dostępu (zwracamy 404 zamiast 403, aby uniknąć wycieku informacji o istnieniu zasobów nieuprawnionym dzwoniącym).409 conflict— slug jest już w użyciu lub wykryto jednoczesną edycję (PATCH na nieaktualnej wersji). Pobierz dane ponownie i spróbuj jeszcze raz.429 rate_limit_exceeded— wstrzymaj się zgodnie z wartościąretry_after.500 internal_server_error— błąd po stronie serwera. Można bezpiecznie ponowić z tym samym kluczem idempotencji.502 bad_gateway,503 service_unavailable,504 gateway_timeout— przejściowe problemy z infrastrukturą. Wstrzymaj się i ponów.
Oficjalne SDK implementują wykładnicze wycofywanie (exponential backoff) z jitterem dla błędów 429, 500, 502, 503 i 504. Nie ponawiają one żądań dla błędów 400, 401, 403, 404 lub 409 — są to błędy programistyczne lub konflikty logiki biznesowej, a nie usterki przejściowe. Niestandardowe klienty HTTP powinny postępować według tego samego wzorca; ponowienie błędu 400 z tym samym payloadem nie przyniesie innego rezultatu.
request_id w body błędu to pole, które należy podać w zgłoszeniach do pomocy technicznej. Na podstawie tego ID możemy prześledzić każde żądanie w logach audytowych, logach aplikacji i metrykach platformy — i nie możemy prześledzić żądania bez niego.
Limity zapytań#
Opublikowane limity zapytań to 100 żądań na sekundę na przestrzeń roboczą w planie Pro, 500 w planie Business oraz negocjowany limit w planie Enterprise. Plan darmowy to 10 req/s.
Stan limitów jest widoczny w trzech nagłówkach każdej odpowiedzi API:
X-RateLimit-Limit— aktualny limit na sekundę.X-RateLimit-Remaining— liczba żądań pozostałych w bieżącej sekundzie.X-RateLimit-Reset— znacznik czasu Unix określający, kiedy koszyk się zresetuje.
Limit 100/s jest implementacją token-bucket z możliwością gwałtownego wzrostu (burst) do 200 — co oznacza, że możesz wysłać 200 żądań naraz, jeśli koszyk jest pełny, a następnie przejść do stałego tempa 100/s. Większość zadań tworzenia krótkich linków mieści się komfortowo w limicie burst; integracje silnie obciążone analityką, które przeglądają historyczne zdarzenia kliknięć, korzystają z większego zapasu w planie Pro.
W przypadku operacji masowych w planach Business+, punkt końcowy POST /v1/links/bulk przyjmuje do 1000 linków na żądanie i liczy się jako jedna jednostka limitu zapytań. Jest to właściwy endpoint dla każdego zadania, które tworzy jednorazowo ponad sto linków.
Co dają SDK, czego nie daje zwykłe HTTP#
Oficjalne SDK dostarczają cztery rzeczy, które szybko się zwracają:
- Automatyczne ponowienie z wycofywaniem dla kodów statusu kwalifikujących się do ponowienia.
- Generowanie kluczy idempotencji, gdy nie zostały podane jawnie.
- Typowane błędy, dzięki czemu możesz użyć
catch (err) { if (err instanceof ElidoRateLimitError) { … } }zamiast parsować JSON w blokach catch. - Iteratory stronicowania, dzięki którym punkty końcowe listujące linki udostępniają asynchroniczne iteratory lub generatory zamiast wymagać ręcznej obsługi kursorów.
SDK dla Go dodatkowo udostępnia bazowy klient HTTP do celów instrumentacji — przydatne, jeśli chcesz wpiąć go w swoją istniejącą konfigurację śledzenia (tracing). Strona funkcji API + SDKs opisuje pełny zakres możliwości; dokumentacja API jest opublikowana pod adresem /docs/api-reference.
Dostęp do analityki#
Punkty końcowe analityki są tylko do odczytu i znajdują się pod adresem /v1/workspaces/{id}/analytics/. Najczęstsze zapytania:
GET .../links/{id}/clicks?from=…&to=…— surowe zdarzenia kliknięć ze stronicowaniem. Przydatne dla potoków eksportu.GET .../timeseries?from=…&to=…&bucket=day— zagregowane liczby kliknięć dla zakresu czasu.GET .../breakdown/country?from=…&to=…— podział geograficzny.GET .../breakdown/referrer?from=…&to=…— podział według źródeł odesłań (referrer).
Kanał surowych zdarzeń kliknięć jest największy. Przestrzeń robocza z 10 milionami kliknięć miesięcznie generuje około 600 MB danych JSON surowych zdarzeń miesięcznie. W przypadku eksportu na taką skalę, przewodnik eksportu ClickHouse opisuje mechanizm eksportu masowego, który pomija kopertę JSON i przesyła dane strumieniowo bezpośrednio z magazynu analitycznego.
Webhooks dla zdarzeń kliknięć#
Webhooks to odwrotność odpytywania (polling) — zamiast pytać API o nowe kliknięcia, API dostarcza je do Twojego punktu końcowego. Konfiguracja pod adresem /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,
});
Każde dostarczenie zawiera nagłówek Elido-Signature zawierający HMAC-SHA256 body żądania z Twoim współdzielonym kluczem tajnym. Zweryfikuj sygnaturę przed przetworzeniem — bez tego każdy dzwoniący może wysłać żądanie do Twojego punktu końcowego webhooka i podszyć się pod Elido.
Semantyka dostarczania to at-least-once (co najmniej raz) z wykładniczym wycofywaniem do maksymalnego czasu retencji 72 godzin. Szczegółowy opis kształtu danych i zachowania ponowień znajdziesz we wpisie webhooks vs polling, który porównuje te dwa wzorce integracji.
Przykładowy scenariusz: automatyzacja kampanii#
Integracja, która motywuje większość wdrożeń API, wygląda następująco. Twoja automatyzacja marketingu tworzy kampanię w Customer.io lub HubSpot. W momencie opublikowania kampanii uruchamiany jest hook. Twój handler tworzy krótki link, dołącza go do rekordu kampanii i przesyła z powrotem do narzędzia do zarządzania kampaniami, aby wstawić go do szablonu e-mail.
W 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;
}
Klucz idempotencji pochodzi z ID kampanii. Jeśli hook opublikowania kampanii zostanie uruchomiony dwukrotnie (a zdarza się to — dostarczenia webhooków są typu at-least-once), drugie wywołanie zwróci ten sam link bez tworzenia duplikatu. Pole metadata przechowuje Twoje własne klucze powiązań, dzięki czemu możesz skorelować zdarzenia kliknięć z Elido z kampanią bez konieczności parsowania tagów.
Dla pełnej atrybucji kampanii z szablonami UTM i przesyłaniem konwersji, artykuł o śledzeniu UTM end-to-end opisuje cały proces.
Czego jeszcze nie ma w API#
Dwie rzeczy, o które często padają pytania, a które nie są obecnie dostępne:
- Pojedynczy punkt końcowy GET dla analityki linku, który zwraca wszystkie podziały w jednym wywołaniu. Obecny model wymaga oddzielnych wywołań dla kliknięć, krajów, źródeł odesłań, urządzeń i szeregów czasowych. Agregacja jest w planach; obecnie SDK paralelelizują te żądania za pomocą pojedynczej metody pomocniczej.
- Ponowne odtwarzanie webhooków (replay) przez API. Panel administracyjny pokazuje historię dostarczeń webhooków i wspiera ponowne odtwarzanie; API jeszcze tego nie oferuje. To również znajduje się w planach.
Jeśli funkcja znajduje się w specyfikacji OpenAPI, jest wspierana. Jeśli znajduje się w tym wpisie, ale nie ma jej w specyfikacji, traktuj ją jako planowaną, a nie gwarantowaną.
Powiązane lektury#
- Smart links wyjaśnione — fundament dla klastra funkcji; opisuje, jak silnik przekierowań rozwiązuje link na krawędzi sieci (edge).
- Webhooks vs polling dla śledzenia kliknięć — kiedy stosować dany wzorzec integracji.
- Śledzenie konwersji po stronie serwera przez krótkie linki — rozszerzenie API o proces przesyłania informacji o konwersjach.
- Masowy import kampanii z Google Sheets — praktyczny przykład użycia punktu końcowego do operacji masowych.
- Przegląd operacyjny: przewodnik po serwerze MCP dotyczący łączenia API Elido z Claude, Cursor i innymi klientami obsługującymi MCP.
- Strony produktów:
/features/api-sdksoraz/solutions/developers.