Для розробників
Публічне API відкрите й без ключа. Ключ потрібен, якщо вам потрібні вищі квоти чи вебхуки замість опитування.
Статус: це опис можливостей, а не комерційна пропозиція. Жодного SLA чи гарантії доступності зараз немає. Ключі видає оператор сервісу вручну; самообслуговування й оплати в інтерфейсі немає.
Плани
Free
без ключа- Усе публічне API: /api/v1
- 600 читань на хвилину з IP (сплеск 150)
- Для сайтів, ботів і домашніх проєктів
Pro
1 000 000 / добуорієнтовно- Ключ, до 50 запитів на секунду
- Квоти рахуються на ключ, а не на IP
- Вебхуки замість опитування
Business
10 000 000 / добуорієнтовно- Ключ, до 200 запитів на секунду
- Те саме, що Pro, із вищими квотами
- Умови погоджуються індивідуально
Цифри для Pro і Business орієнтовні й можуть змінитися до видачі ключа. Цін немає: рахунків і платіжної системи в сервісі не існує.
Як користуватися ключем
Передайте ключ заголовком X-API-Key або Authorization: Bearer. Усі ендпоїнти ті самі, що в документації API.
curl -s -H "X-API-Key: $SVITLOPULSE_KEY" https://svitlopulse.andrewboyko.name/api/v1/oblasts
curl -s -H "Authorization: Bearer $SVITLOPULSE_KEY" https://svitlopulse.andrewboyko.name/api/v1/groups/lvivoblenergo/3.2- Ключ — секрет. Тримайте його лише на сервері, не вшивайте в JavaScript сторінки чи мобільний застосунок: його легко витягти.
- Для запитів із браузера відвідувачів ключ не потрібен: користуйтеся безключовим API чи віджетами.
- Скомпрометований ключ оператор може відкликати: напишіть нам, і ми видамо новий. Відкликаний ключ отримує 401.
Квоти й заголовки
Відповіді з ключем мають заголовки X-RateLimit-Limit, X-RateLimit-Remaining (денна квота) і X-RateLimit-Reset (unix-час, коли квота оновиться).
| HTTP | code | Що означає |
|---|---|---|
| 401 | invalid_key, revoked_key | Ключ невідомий або відкликаний. |
| 429 | rate_limited | Перевищено ліміт запитів на секунду. Заголовок Retry-After каже, скільки чекати. |
| 429 | quota_exceeded | Вичерпано добову квоту до X-RateLimit-Reset. |
- Не перевищуйте квоти: на 429 чекайте
Retry-Afterі збільшуйте паузу. - Для оновлень у реальному часі використовуйте SSE
/api/v1/streamабо вебхуки, а також ETag: запит ізIf-None-Matchповертає 304.
Вебхуки
Замість опитування ми надсилаємо POST на ваш HTTPS-адрес, коли стан черги чи області змінюється. Вебхуки доступні лише власникам ключа: ви реєструєте їх самі тим самим ключем.
curl -s -X POST https://svitlopulse.andrewboyko.name/api/v1/webhooks \
-H "X-API-Key: $SVITLOPULSE_KEY" -H "Content-Type: application/json" \
-d '{"url":"https://example.com/hooks/svitlo","operator_id":"lvivoblenergo","group":"3.2"}'
# or a whole region: {"url":"…","oblast":"lviv-oblast"}
# 201 -> {"id":…,"secret":"whsec_…","events":["outage_started","power_restored","schedule_changed"]}- Область дії: одна черга (
operator_id+group) або одна область (oblast, ISO чи slug). - Секрет
whsec_…повертається один раз, якщо ви не передали власний (16–128 символів). Збережіть його. - Лише HTTPS-адреси, що ведуть на публічні хости: приватні, loopback і link-local адреси відхиляються. Кількість вебхуків на ключ обмежена. Список і видалення:
GET /api/v1/webhooks,DELETE /api/v1/webhooks/{id}.
| Подія | Коли |
|---|---|
outage_started | Черга перейшла в стан «світла немає». |
power_restored | Черга перейшла в стан «світло є». |
schedule_changed | Оператор змінив графік черги. |
Кожен запит має заголовок підпису, а також X-SvitloPulse-Event, X-SvitloPulse-Event-Id (однаковий в усіх повторах, це ключ ідемпотентності) і X-SvitloPulse-Attempt.
X-SvitloPulse-Signature: t=1730000000,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bdТут t — unix-час відправлення, а v1 — HMAC-SHA256 від рядка {t}.{тіло запиту} із вашим секретом вебхука, у hex. Підписується точне тіло запиту, байт у байт.
Значення в прикладі заголовка — ілюстрація формату, а не справжній підпис.
Тіло — JSON: id, type, created_at, scope (group або oblast), operator_id, group, oblast, oblast_slug, data (стан такий самий, як у /api/v1/status, плюс previous_state). Події виводяться з публічних даних, тож вебхук не показує більше, ніж API.
Перевірка підпису
- Рахуйте HMAC із сирого тіла, до розбору JSON.
- Порівнюйте у постійний час і відхиляйте запити, де
tвідрізняється від поточного часу більш ніж на 5 хвилин: це захищає від повтору. - Відповідайте 2xx протягом 5 секунд, редиректи не відстежуються; важку роботу виносьте в чергу.
- Невдалу доставку повторюємо через 1, 5 і 30 хвилин (разом чотири спроби), а вебхук, що стабільно не відповідає, вимикається. Обробляйте події ідемпотентно за
X-SvitloPulse-Event-Id.
import crypto from 'node:crypto';
export function verify(rawBody, header, secret, toleranceSec = 300) {
const parts = Object.fromEntries(header.split(',').map((p) => p.trim().split('=')));
const t = Number(parts.t);
if (!t || Math.abs(Date.now() / 1000 - t) > toleranceSec) return false;
const expected = crypto.createHmac('sha256', secret).update(`${t}.${rawBody}`).digest('hex');
const got = Buffer.from(parts.v1 || '', 'hex');
const want = Buffer.from(expected, 'hex');
return got.length === want.length && crypto.timingSafeEqual(got, want);
}<?php
function verify_svitlopulse(string $rawBody, string $header, string $secret, int $tolerance = 300): bool {
$parts = [];
foreach (explode(',', $header) as $p) { [$k, $v] = array_pad(explode('=', trim($p), 2), 2, ''); $parts[$k] = $v; }
$t = (int)($parts['t'] ?? 0);
if (!$t || abs(time() - $t) > $tolerance) return false;
$expected = hash_hmac('sha256', $t . '.' . $rawBody, $secret);
return hash_equals($expected, $parts['v1'] ?? '');
}
// $raw = file_get_contents('php://input');
// verify_svitlopulse($raw, $_SERVER['HTTP_X_SVITLOPULSE_SIGNATURE'] ?? '', getenv('SVITLOPULSE_WEBHOOK_SECRET'));import hashlib, hmac, time
def verify(raw_body: bytes, header: str, secret: str, tolerance: int = 300) -> bool:
parts = dict(p.strip().split("=", 1) for p in header.split(",") if "=" in p)
try:
t = int(parts.get("t", "0"))
except ValueError:
return False
if not t or abs(time.time() - t) > tolerance:
return False
expected = hmac.new(secret.encode(), f"{t}.".encode() + raw_body, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, parts.get("v1", ""))Як отримати ключ
Напишіть на contact@svitlopulse.example і розкажіть:
- що ви будуєте і чи публічний проєкт (посилання);
- для яких областей чи черг потрібні дані;
- орієнтовне навантаження: запитів на секунду й на добу;
- для чого потрібні вебхуки (їх ви реєструєте самі після отримання ключа).
Адреса-заглушка. Домен svitlopulse.example не існує: оператор сервісу має замінити цю адресу на робочу перед запуском.
Чесні застереження
- Первинні дані належать операторам системи розподілу. Перш ніж видавати ключі для комерційного використання, потрібно узгодити ліцензування цих даних: без цього ключі видаємо лише для некомерційних і дослідницьких задач.
- Жодного SLA, гарантії безперервності чи часу відповіді немає. Дані можуть бути застарілими, неповними чи відсутніми (
stale,unknown). - Це не офіційне джерело. Не будуйте на ньому автоматичних рішень, де від цього залежить безпека людей.
- Приватність незмінна: дані агреговані до черги й області, окремих будинків і датчиків немає, і ключ цього не відкриває.
- Формат може розширюватися новими полями; не покладайтеся на відсутність невідомих полів.