Dokumentacja
Strona działa na jednym publicznym API z osiemnastoma natychmiastowymi endpointami. Każdy jest udokumentowany poniżej: co robi, jego limity i przykład żądania/odpowiedzi. Bez konta; wyniki natychmiastowe nie są zapisywane. API zarządzania na końcu tej strony (monitorowanie uptime, strony statusu, krótkie linki) to wyjątek stanowy: są trwałe pod własnymi kluczami zarządzania.
Kody statusów
- 301 — Przekierowanie stałe
Przekierowanie stałe. Wyszukiwarki przenoszą sygnały rankingowe na adres docelowy.
- 302 — Przekierowanie tymczasowe
Przekierowanie tymczasowe. Oryginalny URL pozostaje kanoniczny.
- 307 — Przekierowanie tymczasowe
Przekierowanie tymczasowe zachowujące metodę i treść zapytania.
- 308 — Przekierowanie stałe
Przekierowanie stałe zachowujące metodę i treść zapytania.
- 200 — Sukces
Osiągnięto adres docelowy. Tu łańcuch się kończy.
- 404 — Błąd klienta
Cel nie istnieje — zepsuty adres przekierowania.
- 500 — Błąd serwera
Cel został rozpoznany, ale serwer nie odpowiedział poprawnie.
Sklasyfikowane wyniki, nie błędy
Na czterech endpointach natychmiastowych zablokowane cele, nierozwiązywalne domeny i nieprawidłowe URL-e nie są błędami: każde poprawne żądanie zwraca HTTP 200 ze statusem klasyfikującym wynik. Tylko niepoprawne żądania, zbyt duże ciała i limity częstotliwości dają odpowiedzi błędu.
Wspólne limity
- Protokoły
- http, https
- Metoda
- GET
- Limity częstotliwości
- 30 żądań/minutę i 500/dobę na IP — wspólne dla wszystkich endpointów
- Ciało żądania
- do 10 KiB, ścisły JSON
Katalog serwisów i zgłoszenia społeczności (Phase 14)
Kuratorski publiczny katalog popularnych serwisów internetowych z anonimowymi zgłoszeniami społeczności. Odczyty są publiczne i cache'owane; POST zgłoszenia to wyjątek stanowy — zapisuje dane i niesie limity częstości, deduplikację i moderację. Sygnał społeczności i test automatyczny to zawsze osobne wskaźniki; nic tutaj nie twierdzi „potwierdzona awaria”.
GET /api/v1/catalog/services?sort=reports_24h&page=1&page_size=20
→ 200
{ "items": [ { "slug": "chatgpt", "name": "ChatGPT",
"category": { "slug": "ai-assistants", "name": "AI Assistants" },
"community": { "signal": "reports_increasing", "reports_15m": 3,
"reports_1h": 11, "reports_24h": 42,
"last_report_at": "2026-10-20T09:58:12Z" },
"automated_check": { "state": "operational",
"checked_at": "2026-10-20T09:55:00Z" } } ],
"page": 1, "page_size": 20, "total": 1 }
POST /api/v1/catalog/services/chatgpt/reports
{ "problem_type": "site_unreachable", "comment": "Login loop on mobile" }
→ 201
{ "service_slug": "chatgpt", "problem_type": "site_unreachable",
"comment": "Login loop on mobile", "created_at": "2026-10-20T10:01:33Z" }
Reads are public and cached (ETag + max-age=30); unknown/draft/hidden
slugs all answer 404 (no existence oracle); merged slugs 301 to the
target. Reports are anonymous: no account, no network fetch on
submission, rate limited per IP, comments plain text (≤500 chars).
Community signal and the automated check are always separate blocks —
no value on this API means "confirmed down".- Paginacja
- page ≥1, page_size domyślnie 20, maks. 100; deterministyczna kolejność (tie-break po slug)
- Limity zgłoszeń
- dedup 1 na serwis na 10 min na IP, potem 10/godz. i 30/dobę na IP; 429 + Retry-After; fail-closed
- Moderacja i prywatność
- polityka auto-publish lub hold-all; wyłączniki awaryjne; przechowywany jest tylko codziennie rotowany pseudonimowy hash — nigdy surowy IP ani user-agent
Redirect Checker — POST /api/v1/check
Przechodzi całą łańcuch przekierowań i zapisuje wynik.
POST /api/v1/check
Content-Type: application/json
{ "url": "https://bit.ly/example" }
{
"id": "9pr2kxq4",
"status": "completed",
"input_url": "https://bit.ly/example",
"final_url": "https://example.com/product",
"final_domain": "example.com",
"final_status": 200,
"redirect_count": 3,
"duration_ms": 482,
"redirects": [
{ "sequence": 0, "requested_url": "https://bit.ly/example",
"status_code": 301, "next_url": "https://tracker.example/click",
"duration_ms": 83 },
{ "sequence": 3, "requested_url": "https://example.com/product",
"status_code": 200, "duration_ms": 184 }
],
"errors": null
}
GET /api/v1/check/{id} — pobiera zapisany wynik po jego publicznym ID.- Maksymalna liczba przekierowań
- 20 skoków
- Limit czasu żądania
- 15 sekund
GET /api/v1/check/{id} — pobiera zapisany wynik po jego publicznym ID.
Uptime Checker — POST /api/v1/uptime-check
Sprawdza, czy strona odpowiada. Wynik jest klasyfikowany: UP przy poprawnej odpowiedzi, HTTP_ERROR gdy strona zwraca 4xx/5xx, plus TIMEOUT, DNS_ERROR, TLS_ERROR, CONNECTION_ERROR i BLOCKED_TARGET.
POST /api/v1/uptime-check
Content-Type: application/json
{ "url": "https://example.com" }
{
"url": "https://example.com",
"status": "UP",
"reachable": true,
"httpStatus": 200,
"httpStatusText": "OK",
"responseTimeMs": 184,
"finalUrl": "https://example.com/",
"redirectCount": 1,
"tls": { "enabled": true, "valid": true }
}- Maksymalna liczba przekierowań
- 10 skoków
- Limit czasu żądania
- 10 sekund
HTTP Status Checker — POST /api/v1/http-status-check
Zwraca końcowy kod statusu URL, nagłówki odpowiedzi końcowej, zwięzłe podsumowanie łańcucha przekierowań i stan TLS.
POST /api/v1/http-status-check
Content-Type: application/json
{ "url": "https://example.com" }
{
"url": "https://example.com",
"status": "UP",
"reachable": true,
"httpStatus": 200,
"httpStatusText": "OK",
"finalUrl": "https://example.com/",
"redirectCount": 1,
"headers": {
"Content-Type": ["text/html; charset=utf-8"],
"Server": ["nginx"]
},
"headers_analysis": {
"security_missing": ["Strict-Transport-Security", "Content-Security-Policy"],
"caching": { "Cache-Control": "max-age=604800" }
},
"tls": { "enabled": true, "valid": true }
}- Maksymalna liczba przekierowań
- 10 skoków
- Limit czasu żądania
- 10 sekund
Response Time Checker — POST /api/v1/response-time-check
Wykonuje 1–5 pomiarów i klasyfikuje werdykt szybkości na podstawie mediany czasu do pierwszego bajtu. Zwraca czas każdego pomiaru, min/medianę/maks/średnią oraz rozbicie czasów ostatniego skoku.
POST /api/v1/response-time-check
Content-Type: application/json
{ "url": "https://example.com", "samples": 3 }
{
"url": "https://example.com",
"samples": 3,
"status": "UP",
"verdict": "FAST",
"completed": 3,
"timesMs": [184, 201, 176],
"stats": { "minMs": 176, "medianMs": 184, "maxMs": 201, "meanMs": 187 },
"breakdown": { "dnsMs": 12, "connectMs": 23, "tlsMs": 45, "ttfbMs": 184, "totalMs": 190 },
"httpStatus": 200,
"httpStatusText": "OK",
"finalUrl": "https://example.com/",
"redirectCount": 1,
"tls": { "enabled": true, "valid": true }
}- Pomiary
- 1–5 (domyślnie 3)
- Limit czasu pomiaru
- 10 sekund
- Limit serii
- 30 sekund
SSL Checker — POST /api/v1/ssl-check
Sprawdza certyfikat TLS przedstawiany przez host: ważność, wystawcę, SAN, wynegocjowany protokół i szyfr oraz łańcuch. Tylko sonda TLS — strona nigdy nie jest pobierana; uszkodzony certyfikat jest zgłaszany z konkretnym powodem.
POST /api/v1/ssl-check
Content-Type: application/json
{ "url": "https://example.com" }
{
"url": "https://example.com",
"host": "example.com",
"status": "UP",
"reachable": true,
"handshakeMs": 45,
"tls": {
"version": "TLS 1.3",
"cipher": "TLS_AES_256_GCM_SHA384",
"verified": true,
"status": "valid",
"daysRemaining": 89,
"notBefore": "2026-08-01T00:00:00Z",
"notAfter": "2026-10-30T00:00:00Z",
"subject": "CN=example.com",
"issuer": "CN=R3,O=Let's Encrypt,C=US",
"sans": ["example.com", "www.example.com"],
"chain": ["CN=example.com", "CN=R3,O=Let's Encrypt,C=US"]
}
}- Port
- z adresu URL (domyślnie 443)
- Limit czasu sondy
- 10 sekund
DNS Checker — POST /api/v1/dns-check
Wyszukuje rekordy A, AAAA, CNAME, MX, TXT i NS hosta. Tylko wyszukiwanie i raport: żadne połączenia nie są nawiązywane, a każdy rozwiązany adres jest oznaczany jako publiczny lub niepubliczny.
POST /api/v1/dns-check
Content-Type: application/json
{ "url": "example.com" }
{
"url": "example.com",
"host": "example.com",
"status": "UP",
"durationMs": 38,
"records": {
"a": ["93.184.216.34"],
"aaaa": ["2606:2800:220:1:248:1893:25c8:1946"],
"cname": [],
"mx": [{ "host": "mail.example.com", "pref": 10 }],
"txt": ["v=spf1 include:_spf.example.com ~all"],
"ns": ["ns1.example.com", "ns2.example.com"]
},
"addressLabels": [
{ "address": "93.184.216.34", "classification": "public", "reason": "public" }
]
}- Rodziny rekordów
- A, AAAA, CNAME, MX, TXT, NS
- Limit czasu wyszukiwania
- 10 sekund łącznie, 3 sekundy na rodzinę
Sprawdzanie wygaśnięcia domeny — POST /api/v1/domain-expiry-check
Odpowiada, kiedy domena wygasa, przez stały przepływ RDAP rdap.org: werdykt, pozostałe dni, daty rejestracji/wygaśnięcia i statusy rejestru. Pierwsze narzędzie w repozytorium odpytujące stałą usługę zewnętrzną: wyłącznie HTTPS na każdym skoku, bez proxy, ograniczone przekierowania i rozmiar; odpytywana domena nigdy nie jest kontaktowana. 404 staje się werdyktem not_found (niezarejestrowana / rejestr bez RDAP / nic nie opublikowano), 429 i 5xx — uczciwym UNKNOWN_ERROR.
POST /api/v1/domain-expiry-check
Content-Type: application/json
{ "url": "example.com" }
{
"url": "example.com",
"host": "example.com",
"status": "UP",
"verdict": "ok",
"daysRemaining": 214,
"registration": "2019-01-15T05:00:00Z",
"expiration": "2027-04-15T04:00:00Z",
"lastChanged": "2026-08-01T11:20:00Z",
"registryStatuses": ["ok"],
"source": "rdap",
"durationMs": 420
}- Werdykty
- ok, expiring (≤30 dni), expired, not_found, unknown
- Limit czasu odpytania
- 10 sekund łącznie (DOMAIN_EXPIRY_TIMEOUT)
- Połączenia wychodzące
- tylko rdap.org; HTTPS na każdym skoku; proxy wyłączone; limit 10 przekierowań; limit rozmiaru 1 MiB; domena nigdy nie jest kontaktowana
Zapytanie WHOIS — POST /api/v1/whois-check
Odpowiada, co rejestr wie o domenie: rejestrator, serwery nazw, daty i flagi statusu. Najpierw RDAP przez rdap.org, z fallbackiem na klasyczny WHOIS (surowe TCP 43) dla stref bez RDAP — pierwsze narzędzie w repozytorium z surowym TCP: każde przekierowanie musi być celem host[:43], jest sprawdzane wspólnym potokiem SSRF i wybierane na przypięty adres publiczny; limit 2 przekierowań, limit odpowiedzi 64 KiB; odpytywana domena nigdy nie jest kontaktowana. 404 od RDAP włącza fallback; jednoznaczna odpowiedź o braku domeny pojawia się jako notFound.
POST /api/v1/whois-check
Content-Type: application/json
{ "url": "mail.ru" }
{
"url": "mail.ru",
"host": "mail.ru",
"status": "UP",
"source": "whois",
"registrar": "VK-TECH",
"nameservers": ["ns1.mail.ru", "ns2.mail.ru"],
"registration": "1997-10-01T00:00:00Z",
"expiration": "2026-10-01T00:00:00Z",
"statuses": ["REGISTERED, DELEGATED, VERIFIED"],
"durationMs": 640
}- Źródła
- RDAP przez rdap.org; klasyczny WHOIS (TCP 43) przez łańcuch przekierowań whois.iana.org
- Limit czasu odpytania
- 15 sekund łącznie (WHOIS_TIMEOUT)
- Połączenia wychodzące
- przekierowania tylko host[:43]; walidacja SSRF i przypięcie przed połączeniem; limit 2 przekierowań; limit 64 KiB; domena nigdy nie jest kontaktowana
Dostępność domeny — POST /api/v1/domain-availability
Odpowiada per TLD, czy nazwa jest do zarejestrowania, z uczciwą maszyną stanów: AVAILABLE tylko wtedy, gdy zapytanie RDAP zwróci 404, a osadzone metadane TLD potwierdzą, że rejestr obsługuje RDAP; brak tylko-WHOIS pozostaje UNKNOWN (prawdopodobnie dostępna); RESTRICTED wynika z metadanych. Pełna obsługa IDN (Unicode na wejściu, punycode kanonicznie, displayDomain na wyjściu) i opcjonalne zapytania wielu TLD (1–10 na żądanie). Premium w MVP zawsze unknown — ceny to osobny przyszły podsystem.
POST /api/v1/domain-availability
Content-Type: application/json
{ "domain": "пример.рф", "tlds": ["com", "io"] }
{
"url": "пример.рф",
"displayDomain": "пример.рф",
"host": "xn--e1afmkfd.xn--p1ai",
"status": "UP",
"results": [
{
"tld": "com",
"availability": "AVAILABLE",
"reason": "rdap_not_found",
"confidence": "high",
"source": "rdap",
"premium": "unknown",
"checkedAt": "2026-09-03T12:00:02Z"
},
{
"tld": "io",
"availability": "UNKNOWN",
"reason": "tld_rdap_unknown",
"confidence": "low",
"source": "tld-metadata",
"premium": "unknown",
"checkedAt": "2026-09-03T12:00:02Z"
}
],
"tldMetaVersion": "2026-09-02",
"durationMs": 1240
}- TLD-y na żądanie
- 1–10 unikalnych (Unicode lub punycode); maksymalnie 4 równoległe zapytania w ramach 15-sekundowego budżetu (DOMAIN_AVAILABILITY_TIMEOUT)
- Wyniki
- AVAILABLE | REGISTERED | RESTRICTED | INVALID | UNKNOWN | ERROR — UNKNOWN nigdy nie jest podawane za AVAILABLE; PREMIUM jest w MVP nieosiągalne
- Cache
- di:avail 60s, di:reg 6h, fail-open; wyników negatywnych nie zapisuje się w cache
Wiek domeny — POST /api/v1/domain-age
Podaje, ile lat ma domena: dokładną datę utworzenia z rejestru i deterministyczny podział na lata/miesiące/dni. Cienka projekcja danych WHOIS — jedno zapytanie obsługuje dostępność, wiek i rejestratora w oknie cache.
POST /api/v1/domain-age
Content-Type: application/json
{ "domain": "example.com" }
{
"url": "example.com",
"host": "example.com",
"status": "UP",
"age": {
"known": true,
"createdAt": "2019-01-15T05:00:00Z",
"ageInDays": 2809,
"years": 7,
"months": 7,
"days": 20
},
"source": "rdap",
"durationMs": 390
}- Matematyka wieku
- tylko daty UTC; pełne lata/miesiące przez kotwicę miesiąca; dni łącznie zaokrąglone w dół
- Nieznany wiek
- age.known=false + ograniczony powód (no_data, inconsistent_data) — nigdy zgadywanka
- Cache
- di:reg 6h współdzielone z dostępnością/rejestratorem (fail-open)
Sprawdzanie rejestratora — POST /api/v1/domain-registrar
Podaje, u którego rejestratora zarejestrowana jest domena, znormalizowane względem kuratorowanej tabeli aliasów (GoDaddy, Namecheap, Cloudflare i więcej) — nieznane nazwy pokazywane dokładnie tak, jak opublikowano. Organizacja rejestru i pochodzenie RDAP/WHOIS w zestawie; współdzieli cache rejestracji z wiekiem/dostępnością.
POST /api/v1/domain-registrar
Content-Type: application/json
{ "domain": "example.com" }
{
"url": "example.com",
"host": "example.com",
"status": "UP",
"registrar": "GoDaddy.com, LLC",
"registrarCanonical": "GoDaddy",
"registrarIanaId": 625,
"registry": "Verisign, Inc.",
"source": "rdap",
"checkedAt": "2026-09-04T12:00:00Z",
"durationMs": 410
}- Rejestrator
- nazwa wyświetlana + kanoniczny badge aliasu przy trafieniu; IANA ID pokazuje „—” (dane rejestru jeszcze go nie niesą)
- Nieopublikowano
- rejestracje za proxy prywatności lub zredagowane odpowiadają UP z powodem no_data — nigdy pusta skorupa
Generator domen — POST /api/v1/domain-generate
Generuje kandydatów na domeny z 1–3 słów kluczowych: formy bezpośrednie, prefiksy, sufiksy, złożenia i formy skrócone — zdeduplikowane, uporządkowane i limitowane. Czyste i natychmiastowe: żadnych wywołań rejestru podczas generowania; sprawdzenia per kandydat to osobne jawne akcje.
POST /api/v1/domain-generate
Content-Type: application/json
{ "keywords": ["coffee", "map"], "tlds": ["com", "io"], "limit": 24 }
{
"status": "UP",
"candidates": [
{ "domain": "coffee.com", "strategy": "direct" },
{ "domain": "coffee.io", "strategy": "direct" },
{ "domain": "getcoffee.com", "strategy": "prefix" },
{ "domain": "coffeemap.com", "strategy": "compound" }
],
"total": 24
}- Kandydaci
- twarde 50, domyślnie 24; ≤10 TLD-ów filtrowanych przez migawkę metadanych TLD
- Brak ruchu do rejestru
- generowanie nie wykonuje żadnych wywołań rejestru — sprawdzenia per kandydat trafiają do endpointu dostępności z jego limitami
Przegląd domeny — POST /api/v1/domain-overview
Złożony profil: dostępność na własnym TLD domeny, data utworzenia z deterministycznym podziałem wieku, rejestrator (znormalizowany, z numerem IANA), projekcja wygaśnięcia i wnioski konfiguracyjne rejestru. Sekcje są niezależne — nieudane zapytanie nigdy nie czyści pozostałych.
POST /api/v1/domain-overview
Content-Type: application/json
{ "domain": "example.com" }
{
"url": "example.com",
"host": "example.com",
"status": "UP",
"availability": {
"tld": "com", "availability": "REGISTERED", "reason": "registered",
"confidence": "high", "source": "rdap", "premium": "unknown",
"checkedAt": "2026-09-05T12:00:00Z"
},
"registration": {
"known": true, "createdAt": "2019-01-15T05:00:00Z",
"ageYears": 7, "ageMonths": 7, "ageDays": 21,
"registrar": "GoDaddy.com, LLC", "registrarCanonical": "GoDaddy",
"registrarIanaId": 625, "registry": "Verisign, Inc.", "source": "rdap",
"checkedAt": "2026-09-05T12:00:01Z"
},
"expiry": {
"host": "example.com", "source": "rdap", "verdict": "ok",
"daysRemaining": 222, "expiration": "2027-04-15T04:00:00Z",
"checkedAt": "2026-09-05T12:00:02Z"
},
"holds": [],
"dnssec": [{ "id": "dnssec-absent", "state": "info", "severity": "info", "detail": "unsigned" }],
"tldMetaVersion": "2026-09-02",
"durationMs": 1620
}- Sekcje
- dostępność (z bramą metadanych) + rejestracja (di:reg, 6h współdzielone) + wygaśnięcie (rdap.org) + wnioski holds/dnssec
- Wnioski to fakty
- holds/DNSSEC to sygnały konfiguracyjne rejestru — nigdy werdykty bezpieczeństwa; DNS/SSL zostają w swoich narzędziach
Sprawdzanie bezpieczeństwa — POST /api/v1/security-check
Pasywna, doradcza rewizja konfiguracji jednej pobranej odpowiedzi: nagłówki bezpieczeństwa (głębokość HSTS, akcenty CSP, COOP/COEP/CORP, X-XSS-Protection), flagi cookie, fakty TLS, degradacje łańcucha i ujawnianie informacji. Bez sondowania, twierdzeń o podatności i ocen; wartości cookie nie opuszczają backendu. Status HTTP nie warunkuje sprawdzenia (osiągalny 404/500 jest analizowany).
POST /api/v1/security-check
Content-Type: application/json
{ "url": "https://example.com" }
{
"url": "https://example.com",
"finalUrl": "https://example.com/",
"status": "UP",
"durationMs": 610,
"summary": { "total": 14, "passed": 9, "warn": 3, "failed": 2 },
"findings": [
{ "id": "hsts-present", "state": "fail", "severity": "high" },
{ "id": "tls-cert", "state": "pass", "severity": "info", "detail": "valid" }
],
"cookies": [
{ "name": "session", "secure": false, "httpOnly": true, "sameSite": "Lax" }
],
"tls": { "enabled": true, "version": "TLS 1.3", "certStatus": "valid", "daysRemaining": 89 }
}- Zakres
- ~23 identyfikatory sprawdzeń: nagłówki/cookies/TLS/łańcuch/ujawnianie; tylko pasywne pobranie
- Cookies
- Tylko nazwy i flagi (Secure/HttpOnly/SameSite) — wartości nie opuszczają backendu
- Limit czasu sprawdzenia
- 10 sekund łącznie (SECURITY_CHECK_TIMEOUT)
URL Analyzer — POST /api/v1/url-analyze
Czysty syntaktyczny rozbiór adresu URL: zredagowana tożsamość kanoniczna (schemat, host, formy IDN, naiwna domena rejestrowalna, port, ścieżka, zapytanie, fragment), uporządkowane parametry z duplikatami i wersjonowaną klasyfikacją śledzenia, liczniki kategorii, informacyjne sygnały i noty normalizacji. Żadnych żądań, żadnego DNS, żadnego zapisu — analiza jest lokalna i trwa milisekundy.
POST /api/v1/url-analyze
Content-Type: application/json
{ "url": "https://xn--e1afmkfd.xn--p1ai/search?q=thing&utm_source=news&id=7&id=8" }
{
"status": "UP",
"identity": {
"hasCredentials": false,
"scheme": "https",
"host": "xn--e1afmkfd.xn--p1ai",
"displayHost": "пример.рф",
"registrableDomain": "пример.рф",
"isIdn": true,
"path": "/search",
"query": "q=thing&utm_source=news&id=7&id=8",
"normalized": "https://xn--e1afmkfd.xn--p1ai/search?q=thing&utm_source=news&id=7&id=8"
},
"parameters": [
{ "name": "q", "value": "thing", "classification": { "category": "unknown", "confidence": "none" } },
{ "name": "utm_source", "value": "news", "classification": { "category": "utm", "label": "utm_source", "confidence": "exact" } },
{ "name": "id", "value": "7", "occurrence": 1 },
{ "name": "id", "value": "8", "occurrence": 2 }
],
"tracking": { "utm": 1, "analytics": 0, "adsClickId": 0, "sessionSensitive": 0, "unknown": 3 },
"signals": ["idn_host"],
"normalization": { "schemeAssumed": false, "punycodeApplied": true, "defaultPortOmitted": false, "hostLowercased": false },
"registryVersion": "2026-09-1"
}- Zakres
- Komponenty + uporządkowane parametry + klasyfikacja rejestru + sygnały informacyjne; zero sieci
- Redagowanie
- Hasła nigdy nie są zwracane; wartości wrażliwych parametrów są wszędzie maskowane (***), nazwy pozostają widoczne
URL Expander — POST /api/v1/url-expand
Błyskawiczne rozwinięcie łańcucha przekierowań na współdzielonym silniku, bez zapisu: każdy hop przechodzi tę samą centralną weryfikację SSRF, pętle i limit 10 przekierowań są rozróżniane przez reason, a końcowy URL dostaje pełną analizę śledzenia. Zwracane adresy są redagowane — hasła nigdy nie opuszczają backendu. Bez cache: łańcuchy są wrażliwe na czas, świeżość to właściwa odpowiedź.
POST /api/v1/url-expand
Content-Type: application/json
{ "url": "https://bit.ly/3xyz" }
{
"url": "https://bit.ly/3xyz",
"status": "UP",
"finalUrl": "https://example.com/landing?utm_source=n",
"hopCount": 2,
"chain": [
{ "url": "https://bit.ly/3xyz", "status": 301, "location": "https://bit.ly/abc" },
{ "url": "https://bit.ly/abc", "status": 302, "location": "https://example.com/landing?utm_source=n" }
],
"analysis": { "status": "UP", "tracking": { "utm": 1, "analytics": 0, "adsClickId": 0, "sessionSensitive": 0, "unknown": 0 } },
"source": "engine",
"durationMs": 820
}- Zakres
- Zredagowany łańcuch hopów + sklasyfikowany status (pętla vs limit) + analiza końcowego URL; bez DNS/TLS/timingu per hop (domena Redirect Checkera)
- Budżet rozwijania
- 10 sekund łącznie (URL_EXPAND_TIMEOUT); limit hopów 10
UTM Builder — POST /api/v1/utm-build
Deterministyczne, bezstanowe budowanie URL-i kampanii: niepowiązane parametry zachowują kolejność, kanoniczne UTM są nadpisywane lub zachowywane per pole, duplikaty zapadają się do ostatniego wystąpienia, fragment jest zachowywany, a cele z danymi logowania są odrzucane in-band. Ten sam wkład zawsze daje wyjście identyczne bajt w bajt.
POST /api/v1/utm-build
Content-Type: application/json
{ "url": "https://example.com/landing?foo=1&utm_source=old#pricing", "utmSource": "newsletter", "utmMedium": "email", "utmCampaign": "spring_sale", "utmContent": "header_cta" }
{
"status": "UP",
"generatedUrl": "https://example.com/landing?foo=1&utm_source=newsletter&utm_medium=email&utm_campaign=spring_sale&utm_content=header_cta#pricing",
"warnings": ["existing_utm_overwritten:utm_source", "empty_field_skipped:utm_term"],
"parameters": { "utm_source": "newsletter", "utm_medium": "email", "utm_campaign": "spring_sale", "utm_content": "header_cta" }
}- Zakres
- Dodawanie UTM w stałej kolejności + polityka preserve/overwrite/collapse + ostrzeżenia; czysto, bez sieci
- Determinizm
- Wyjście identyczne bajt w bajt dla tego samego wkładu; pola ≤256 znaków; nieznane pola żądania są odrzucane
Website Health — POST /api/v1/website-health
Diagnostyka złożona: jedno żądanie normalizuje cel i uruchamia ograniczone równoległe lanes nad istniejącymi silnikami — jeden check HTTP zasila pięć sekcji (dostępność, status, czas odpowiedzi, nagłówki, konfiguracja bezpieczeństwa), a DNS, próba TLS i profil domeny działają jako osobne lanes. Werdykt to zagregowany stan ogólny ze szczegółami każdej weryfikacji i linkami do narzędzi szczegółowych. Wyniki opisują wyłącznie wykonane sprawdzenia — nigdy gwarancji bezpieczeństwa.
POST /api/v1/website-health
Content-Type: application/json
{ "url": "https://example.com" }
{
"target": {
"canonical": "https://example.com/",
"displayHost": "example.com",
"scheme": "https",
"hasCredentials": false,
"isDomain": true,
"isHttp": true,
"valid": true
},
"overall": "WARNING",
"summaryKey": "health.summary.warning",
"checks": [
{ "type": "http_availability", "state": "pass",
"summaryKey": "health.check.httpAvailability.pass",
"value": "200", "durationMs": 612, "applicable": true,
"detailUrl": "/uptime-checker" },
{ "type": "http_status", "state": "pass",
"summaryKey": "health.check.httpStatus.pass",
"value": "200 OK", "applicable": true,
"detailUrl": "/http-status-checker" },
{ "type": "response_time", "state": "pass",
"summaryKey": "health.check.responseTime.pass",
"value": "612 ms", "applicable": true,
"detailUrl": "/response-time-checker" },
{ "type": "headers", "state": "warn",
"summaryKey": "health.check.headers.warn",
"value": "2 missing", "reason": "findings", "applicable": true,
"detailUrl": "/security-checker" },
{ "type": "security", "state": "warn",
"summaryKey": "health.check.security.warn",
"value": "14 of 21 passed", "reason": "findings", "applicable": true,
"detailUrl": "/security-checker" },
{ "type": "tls", "state": "pass",
"summaryKey": "health.check.tls.pass",
"value": "TLS 1.3 · valid, 89 days", "durationMs": 140,
"applicable": true, "detailUrl": "/ssl-checker" },
{ "type": "dns", "state": "pass",
"summaryKey": "health.check.dns.pass",
"value": "A, AAAA, MX, NS", "durationMs": 95,
"applicable": true, "detailUrl": "/dns-checker" },
{ "type": "domain", "state": "pass",
"summaryKey": "health.check.domain.pass",
"value": "expires in 214 days", "durationMs": 730,
"applicable": true, "detailUrl": "/domain-overview" }
],
"meta": { "checksTotal": 8, "checksPassed": 6, "checksWarn": 2,
"checksFailed": 0, "checksUnknown": 0,
"tldMetaVersion": "2026-09-03", "durationMs": 1480 }
}- Zakres
- Jedno wykonanie HTTP zasila pięć sekcji; lanes DNS/TLS/domena; agregat HEALTHY/WARNING/ERROR/PARTIAL/UNKNOWN
- Limity
- Budżet ogólny 25 s + budżety lanes, ≤3 lanes równolegle; nieprawidłowe cele in-band; bez cache — świeżość najważniejsza
Uptime Monitoring — API zarządzania /api/v1/monitors
Stanowy wyjątek od narzędzi błyskawicznych: utwórz monitor dla jednego adresu URL, a worker sprawdza go według stałego harmonogramu — z historią sprawdzeń, statystykami dostępności, epizodami incydentów i powiadomieniami webhook. Własnością jest klucz zarządzania pokazywany dokładnie raz przy tworzeniu; każde odczytanie i zapis uwierzytelnia się nim, a błędny klucz jest nieodróżnialny od nieistniejącego monitora ( jednolity 404).
POST /api/v1/monitors
Content-Type: application/json
{ "url": "https://example.com", "name": "Prod site", "interval": 300 }
201 Created
{
"monitor": { "id": "b1…", "name": "Prod site",
"normalizedTarget": "https://example.com/",
"state": "unknown", "interval": 300 },
"manageUrl": "/monitors/b1…?key=…",
"key": "…shown exactly once…"
}
GET /api/v1/monitors/{id}?key=… detail + stats window + incidents
GET /api/v1/monitors/{id}/history cursor-paginated executions
GET /api/v1/monitors/{id}/history/daily per-day buckets (30-day cap)
GET /api/v1/monitors/{id}/incidents incident episodes
POST /api/v1/monitors/{id}/check enqueue a priority manual check
PATCH /api/v1/monitors/{id} name / interval / webhook / pause
DELETE /api/v1/monitors/{id} cascade delete
Credentials in the target are rejected (credentials_not_allowed); the
target is SSRF-pre-validated at creation and re-validated on every run.- Zakres
- Tworzenie / lista / szczegóły / pauza / usuwanie / ręczne sprawdzenie / historia / incydenty; stałe interwały 60–3600s; in-band INVALID_URL i BLOCKED_TARGET przy tworzeniu
- Zarządzanie kluczem
- Klucz 256-bit pokazywany raz (przechowywany sha256); poświadczenia w celach są odrzucane; SSRF sprawdzany przy tworzeniu i przed każdym uruchomieniem
Publiczne strony statusu — API zarządzania /api/v1/status-pages
Złóż swoje monitory w publiczną stronę statusu: wybierz komponenty, ułóż je w kolejności, nadaj nazwy wyświetlane i opublikuj pod stałym slugiem. Strony żyją pod tym samym kluczem zarządzania co monitory; szkice są prywatne, publikacja jest idempotentna i odwracalna, a każda mutacja usuwa wpis publicznej pamięci podręcznej — zmiany dostępu działają w ramach żądania.
POST /api/v1/status-pages
Content-Type: application/json
X-Monitor-Key: <your key>
{ "name": "Prod API" }
201 Created
{
"page": { "publicId": "p1…", "name": "Prod API",
"slug": "prod-api", "published": false },
"manageUrl": "/status-pages/p1…?key=…"
}
GET /api/v1/status-pages/{id}?key=… editor payload (members + monitors)
PATCH /api/v1/status-pages/{id} name/description/slug + membership
POST /api/v1/status-pages/{id}/publish → live at /status/{slug}
POST /api/v1/status-pages/{id}/unpublish → removed within the request
DELETE /api/v1/status-pages/{id} cascade delete
GET /api/v1/public/status-pages/{slug} the public DTO (no key; cached 60s)
Membership is same-key only (≤25 monitors, replace-list semantics);
publish requires ≥1 visible member; the public DTO is an explicit
projection — target URLs, keys and internal ids never cross.- Zakres
- Tworzenie / lista / edycja / lista członków typu replace (≤25, tylko własny klucz) / publikacja / wycofanie / usunięcie; slugi 3-63 znaki z listą słów zastrzeżonych; maks. 5 stron na klucz
- Powierzchnia publiczna
- GET /api/v1/public/status-pages/{slug} + /status/{slug}; wyłącznie jawne publiczne DTO — docelowe URL, klucze, hosty powiadomień i wewnętrzne id nigdy nie przekraczają granicy; pamięć podręczna Redis 60s, fail-open
Skracacz URL — API zarządzania /api/v1/short-links
Zamień długi URL w krótki link /go/{code}, którym możesz zarządzać: zwalidowany cel, kryptograficznie losowy kod, statystyki kliknięć w oknach dzisiaj/7 dni/30 dni. Linki żyją pod tym samym kluczem zarządzania co Twoje monitory i strony statusu — tworzenie przypina się do istniejącego klucza i nigdy nie tworzy nowego. Cele są walidowane przy tworzeniu i edycji (składnia, poświadczenia odrzucane, klasyfikacja SSRF); publiczne przekierowanie niczego nie pobiera i odpowiada 302 bez buforowania.
POST /api/v1/short-links
Content-Type: application/json
X-Monitor-Key: <your key>
{ "destinationUrl": "https://example.com/launch?utm_source=newsletter" }
201 Created
{
"link": { "id": "sl1…", "code": "a3f9k2mz",
"shortUrl": "/go/a3f9k2mz",
"destination": "https://example.com/launch?utm_source=newsletter" }
}
GET /api/v1/short-links?key=… your links (newest first)
GET /api/v1/short-links/{id}?key=… full link + stats
PATCH /api/v1/short-links/{id}?key=… destination/title/expiry (re-validated)
POST /api/v1/short-links/{id}/disable public 404, stats kept (enable restores)
DELETE /api/v1/short-links/{id}?key=… soft delete (idempotent)
GET /api/v1/short-links/{id}/stats?key=… total + today/7d/30d clicks
GET /go/a3f9k2mz → 302 Found + Location (Cache-Control: no-store);
unknown/disabled/expired/deleted all answer 404.
Destinations are validated at create/update (http/https, credentials
rejected, SSRF classification); the redirect itself never fetches.- Zakres
- Tworzenie / listowanie / edycja / wyłączanie / włączanie / miękkie usuwanie / statystyki; ≤100 aktywnych linków na klucz, 30 utworzeń na klucz na godzinę; cel jest rewalidowany przy każdej zmianie
- Publiczne przekierowanie
- GET /go/{code} → 302 + no-store; nieznany/wyłączony/przedawniony/usunięty odpowiada tym samym neutralnym 404; przekierowanie to czyste wyszukanie — nigdy nie przyjmuje parametru celu ani nie wyszukuje/pobiera celu; bez IP i user-agentów
Zgłoś krótki link — anonimowe API zgłoszeń nadużyć
Jeśli link /go/{code} wygląda na phishing, malware, spam lub oszustwo — zgłoś go: konto nie jest potrzebne, a odpowiedzi nie otrzymasz. Zgłoszenia trafiają do kolejki operatora; tylko operator może wyłączyć link. Na każde poprawne zgłoszenie endpoint odpowiada tak samo — zgłaszający nie dowiaduje się, czy kod istnieje.
POST /api/v1/short-link-reports
Content-Type: application/json
{ "code": "a3f9k2mz", "reason": "phishing",
"detail": "Imitates a login page" }
202 Accepted
{ "status": "received" }
Reasons: phishing | malware | spam | scam | other (detail ≤500 chars,
optional plain text). The SAME 202 comes back for unknown, disabled
and deleted codes — no existence oracle. Limits per IP: one report
per code per 10 min, 10/hour, 30/day (429 + Retry-After; Redis
failure = 503, fail-closed). Reports go to the operator queue; only
the operator can take a link down (the owner key cannot override).- Limity i prywatność
- 1 zgłoszenie na kod na 10 min z IP, potem 10/godz. i 30/dzień (429 + Retry-After; przy błędzie Redis 503, fail-closed); powody phishing | malware | spam | scam | other; bez konta, bez e-maila, surowy IP nie jest przechowywany — tylko codziennie rotujący odcisk
- Jednolita odpowiedź
- każde poprawne zgłoszenie otrzymuje to samo 202 received — dla żywych, wyłączonych, usuniętych i nieznanych kodów (brak wyroczni istnienia); zły kod lub nieznany powód to 400 przed jakimkolwiek lookupem
Konta i klucze API — /api/v1/auth/register i /api/v1/api-keys
Konta przeglądarkowe (e-mail + hasło, otwarta rejestracja; e-mail w tej wersji nie jest potwierdzany) z sesjami po stronie serwera w utwardzonym ciasteczku. Klucze API uwierzytelniają wywołania serwer–serwer w ramach jawnej listy rodzin zasobów. Błędy logowania są generyczne — nieznany e-mail i złe hasło są nieodróżnialne, także po czasie odpowiedzi.
POST /api/v1/auth/register
Content-Type: application/json
{ "email": "you@example.com", "password": "…", "displayName": "…" }
201 Created + Set-Cookie: wg_session (HttpOnly; Secure; SameSite=Lax)
{ "account": { "id": "acc…", "email": "you@example.com", … } }
POST /api/v1/auth/login 200 + Set-Cookie (401 generic on failure)
POST /api/v1/auth/logout deletes the server-side session
GET /api/v1/account the session's own profile
PATCH /api/v1/account update display name (displayName)
GET /api/v1/account/summary account-owned resource counts
POST /api/v1/account/claim management key → re-own key-held resources
POST /api/v1/api-keys (SESSION) create; secret shown ONCE
{ "name": "CI", "scopes": ["monitors:read"] }
GET /api/v1/api-keys (SESSION) list — no secrets
POST /api/v1/api-keys/{id}/revoke (SESSION) immediate 401 on next use
Bearer access (Authorization: Bearer wg_live_…) is an EXPLICIT
allowlist only: account GET/PATCH, monitors CRUD + reads, status-pages
GET, short-links CRUD + stats; everything else refuses API keys.
Missing/unknown/revoked → generic 401; wrong scope → 403; per-key and
per-account rate limits → 429 + Retry-After.- Lista Bearer
- account GET/PATCH (account:read), monitory (odczyt/zapis), strony statusu GET (odczyt), skrócone linki (odczyt/zapis); wszystko inne odrzuca klucze API. Zasoby trzymane przez klucz są niewidoczne, dopóki nie zostaną dołączone przez claim-by-key.
- Traktowanie sekretu
- Pełny klucz wg_live_ pokazuje się dokładnie raz przy tworzeniu; przechowywany jest tylko jednokierunkowy skrót. Brakujące/nieznane/odwołane klucze odpowiadają jednym ogólnym 401; brak zakresu → 403; limity → 429 + Retry-After.
Tożsamość e-mail konta (potwierdzenie i odzyskiwanie)
Fundament e-mail Phase J: potwierdź adres, odzyskaj hasło i potwierdź niedawną znajomość hasła dla czynności wrażliwych.
POST /api/v1/auth/verification/request (SESSION) 202 {accepted:true}
POST /api/v1/auth/verification/confirm { "token": "…" } → 200 {verified:true}
POST /api/v1/auth/password-reset/request { "email": "you@example.com" } → 202 (always the same answer)
POST /api/v1/auth/password-reset/confirm { "token": "…", "password": "…" } → 200 {reset:true}
POST /api/v1/auth/reauthenticate (SESSION) { "password": "…" } → 200 {confirmed:true}
Reset revokes ALL sessions in the same transaction; API keys stay
active. Tokens ride mail links as URL fragments — never query strings —
and every mismatch (wrong account, replay, expiry, unknown) answers the
same generic 404.- Tokeny
- jednorazowe, 24 h (potwierdzenie) / 30 min (reset), tylko we fragmencie, ten sam 404 przy każdej rozbieżności
- Reset
- atomowo wylogowuje wszystkie sesje; klucze API pozostają aktywne
- Ponowne uwierzytelnienie
- 5 minut, po stronie serwera, związane z sesją
- Przestrzenie robocze
- GET /api/v1/workspaces — osobiste + organizacje, no-store, tylko sesja
- Organizacje
- tworzenie/lista/odczyt/zmiana nazwy/usunięcie + przekazanie własności; zweryfikowane sesje, optymistyczne wersje, reauth dla czynności destrukcyjnych
- Zaproszenia
- zaproszenia e-mail z jednorazowymi linkami na 7 dni, czterema stałymi rolami, limitami z rezerwacją miejsc i wymaganymi oknami częstości
- Zasoby organizacji
- współdzielone monitory/strony/linki pod /organizations/{orgId}; RBAC sesji, wersjonowane mutacje, stare klucze wykluczone na poziomie SQL
- Przenoszenie zasobów
- atomowe przenoszenie osobistych monitorów/stron/linków do organizacji (domknięcie ≤100, wersje, idempotentność)
- Klucze organizacji
- klucze serwisowe (wg_live_…), pokazywane raz, z zakresami, przeżywają odejście twórcy, natychmiastowe unieważnienie
- Audyt organizacji
- historia tylko-do-dodawania zatwierdzonych zmian (365 dni), odczyt Owner/Admin, minimalna ujawnienie aktorów