REST API для сокращения ссылок
Базовый URL: https://loq.su.
Все ответы в JSON, кодировка UTF-8.
Создать ссылку
POST /api/links
Тело запроса
| Поле | Тип | Обязательное | Описание |
|---|---|---|---|
url | string | да | http(s)-URL до 2048 символов; не может указывать на сам loq.su |
alias | string | нет | свой slug, [a-zA-Z0-9_-]{3,32}, не из зарезервированных (api, admin, metrics, …) |
expires_at | RFC3339 string | нет | UTC-таймстамп истечения, минимум на минуту в будущее |
max_clicks | integer > 0 | нет | лимит переходов; после — 410 Gone |
Ответ 201 Created
{
"slug": "abc123",
"short_url": "https://loq.su/abc123",
"target_url": "https://example.com/long",
"expires_at": "2026-12-31T00:00:00Z",
"max_clicks": 100,
"created_at": "2026-04-27T12:00:00Z"
}
Перейти по ссылке
GET /:slug
Возвращает 302 Found с Location: <target_url>
и атомарно инкрементит счётчик. Логирует структурированный JSON
со slug, target host, статусом, UA, referer и SHA-256-хешем IP.
404— slug не найден410— ссылка забанена / истекла / лимит исчерпан
Предпросмотр
GET /preview/:slug
HTML-страница с целевым URL и кнопкой «Перейти». Тот же экран
автоматически отдаётся для /:slug, если хост цели
совпадает с записью в blocklist (защита от фишинга/малвара).
Ошибки
| Код | Когда |
|---|---|
400 | невалидный JSON / URL / alias / expires_at |
409 | такой alias уже занят |
422 | хост целевого URL в blocklist |
429 | превышен rate-limit |
503 | Redis недоступен (rate-limiter оффлайн) |
Тело: {"error": "<человекочитаемое сообщение>"}.
Rate limits
Ограничение фиксируется по хешу IP-адреса клиента
(через X-Forwarded-For). Никаких токенов / ключей
не требуется.
- 10 создаваний в минуту с одного IP
- 100 создаваний в час с одного IP
На редиректы лимита нет (только глобальный nginx safety net).
Примеры
curl — простое
curl -s -X POST https://loq.su/api/links \
-H 'content-type: application/json' \
-d '{"url":"https://example.com/long"}'
curl — с алиасом и лимитами
curl -s -X POST https://loq.su/api/links \
-H 'content-type: application/json' \
-d '{
"url": "https://example.com/promo",
"alias": "summer-26",
"expires_at": "2026-09-01T00:00:00Z",
"max_clicks": 1000
}'
Python
import urllib.request, json
req = urllib.request.Request(
"https://loq.su/api/links",
method="POST",
headers={"content-type": "application/json"},
data=json.dumps({"url": "https://example.com"}).encode(),
)
with urllib.request.urlopen(req) as r:
print(json.loads(r.read())["short_url"])
JavaScript (browser)
const r = await fetch('https://loq.su/api/links', {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({ url: 'https://example.com' }),
});
const { short_url } = await r.json();
console.log(short_url);