Dokumentacja API
Wszystkie liczby, które serwis pokazuje, można pobrać maszynowo. Endpointy są otwarte: bez klucza i bez rejestracji. Dokumentacja jest po polsku, endpointy mówią JSON-em.
Przykłady odpowiedzi endpointów danych zarejestrowano w produkcji 3 sierpnia 2026 — w trakcie awarii bazy. Dlatego endpointy danych odpowiadają tu kopertą błędu; poza /api/zrodla, którego przykład odpowiada aktualnemu katalogowi kodu i od bazy nie zależy. To nie jest usterka dokumentacji: kontrakt zakłada, że błąd jest jawny, a przykład błędu jest tak samo prawdziwy jak przykład danych. Aktualny stan pokazuje status danych.
Spis endpointów
| Metoda | Endpoint | Co zwraca |
|---|---|---|
| GET | /api/metrics | katalog wskaźników (koperta danych) |
| POST | /api/metrics/values | wartości wybranych wskaźników (koperta danych) |
| GET | /api/status | stan maszynerii danych (raport, zawsze 200) |
| GET | /api/zrodla | rejestr źródeł wskaźników (z kodu, zawsze 200) |
| POST | /api/charts/share | tworzy link do udostępnionego wykresu |
| GET | /api/charts/share/{id} | zapisana konfiguracja udostępnionego wykresu |
| GET | /feed.xml | przekierowanie 301 na /analizy/feed.xml (Atom) |
Zasady
- Pochodzenie danych. Każdy wskaźnik ma udokumentowane identyfikatory serii źródłowej w /wykresy/zrodla (wersja maszynowa: /api/zrodla); jak pilnujemy liczb — w /jak-to-dziala.
- Bez obietnic stabilności schematu przed v1. Pola i kody mogą się zmienić. Zmiany odnotowujemy w /zmiany, nigdy po cichu.
- Bez SLA. Historii dostępności nie prowadzimy, więc niczego o niej nie obiecujemy.
/api/statusmówi, co jest prawdą teraz.
Koperta danych: live / archival / error
Każda odpowiedź endpointów danych (/api/metrics, /api/metrics/values) jest opakowana w kopertę z jawnym statusem:
type DataStatus = 'live' | 'archival' | 'error';
interface DataEnvelope<T> {
dataStatus: DataStatus;
asOf: string | null; // vintage danych: '2026-06', '2026-Q2' albo '2026'; null gdy nie dotyczy
data: T;
}| dataStatus | HTTP | Znaczenie |
|---|---|---|
| live | 200 | dane z bazy podstawowej |
| archival | 200 | oznaczona kopia zapasowa — nigdy nie udaje danych na żywo, a asOf mówi, z kiedy jest |
| error | 503 | nie ma nic do pokazania; data jest puste |
Czwartej, nieoznaczonej ścieżki nie ma. Odpowiedź 503 z pustą kopertą jest zamierzona: audyt z 2 sierpnia 2026 wykazał błędy w 47 z 98 ręcznie przepisanych wartości i obie serie zostały usunięte zamiast poprawione „na oko” (szczegóły w /jak-to-dziala) — kopia archiwalna została wtedy opróżniona. Gdy baza nie odpowiada, endpointy danych mówią wprost: błąd.
Formaty okresów: 2026-06 (miesiąc), 2026-Q2 (kwartał), 2026 (rok). Uwaga praktyczna: porównywanie okresów różnych częstotliwości jako napisów daje błędne wyniki — w ASCII '2026-Q1' > '2026-08', choć kalendarzowo jest odwrotnie.
Obsługa koperty po stronie klienta:
const res = await fetch('https://www.podziemieanalityczne.pl/api/metrics');
const envelope = await res.json();
if (envelope.dataStatus === 'error') {
// res.status === 503; envelope.data jest puste — nie ma danych do pokazania.
} else if (envelope.dataStatus === 'archival') {
// Kopia zapasowa z vintage envelope.asOf — oznacz ją tak w swoim UI.
} else {
// 'live' — dane z bazy podstawowej.
}Błędy walidacji
Odrzucone żądanie (błędne ciało, przekroczony limit) dostaje wspólny kształt błędu — inny niż koperta danych:
{ "code": "INVALID_BODY", "message": "Nieprawidłowe żądanie." }code jest stały i maszynowy (do rozgałęziania w kliencie), message to polski komunikat gotowy do pokazania użytkownikowi. Kody wymienione są przy każdym endpoincie.
GET /api/metrics
Katalog wskaźników — lista tego, o co można pytać /api/metrics/values. Bez parametrów. Odpowiedź jest liczona przy każdym żądaniu, nie z cache'u builda.
Odpowiedź: DataEnvelope<Metric[]>, gdzie każdy element data ma pola:
| Pole | Typ | Znaczenie |
|---|---|---|
| id | string | identyfikator do /api/metrics/values (w bazie podstawowej UUID) |
| slug | string | stały, czytelny identyfikator, np. inflacja-cpi |
| name | string | polska nazwa wskaźnika |
| unit | string | jednostka: %, tys., pkt… |
| source | string | źródło serii |
| description | string | null | opis (może być pusty) |
| color | string | kolor linii (hex, motyw jasny), jakim seria jest rysowana samodzielnie — tak jak na własnej stronie /dane/<slug>. Od 8 sierpnia 2026 kolory na wykresie zależą od całego zestawienia, nie od serii, więc każdy wiersz katalogu niesie tę samą wartość; kolorów porównania nie da się wyprowadzić z tego pola. Pole zostaje dla zgodności. |
| dash | string | wzór linii serii rysowanej samodzielnie — z tego samego powodu co color dziś w każdym wierszu solid (możliwe wartości: solid, dashed, dotted, dash-dot). Nie identyfikuje serii w zestawieniu. |
| sort_order | number | porządek w katalogu |
| displayTransform | string? | opis tego, jak źródło publikuje serię — nie tego, co robi serwis. index-to-percent-change znaczy: GUS publikuje tę serię jako indeks (100 = brak zmiany); none — wartość jest już w jednostce docelowej. Serwis niczego nie przelicza: wartości serwujemy i rysujemy tak, jak publikuje je źródło (errata z 7 sierpnia 2026). Brak pola: katalog nie klasyfikuje tej serii. |
| breaks | object[]? | nieciągłości metodologiczne serii wpisane do katalogu. Najważniejsze pola elementu: period — pierwszy okres liczony już na nowej podstawie; comparableAcross — czy odczyty sprzed i po nieciągłości wolno porównywać; obsStatus — kod SDMX (B przełamanie szeregu, D różnica definicji bez przełamania); titlePl/descriptionPl/sourceUrl — opis i adres, pod którym można to sprawdzić. Brak pola znaczy „katalog nie odnotował nieciągłości”, nie „nieciągłości nie ma”. |
| comparableFrom | string? | najwcześniejszy okres, od którego seria jest na dzisiejszej podstawie metodycznej. Brak pola znaczy „nie ustalono”, nigdy „porównywalna od zawsze”. |
| licence | object? | warunki ponownego użycia wartości serii. Pola: id (identyfikator SPDX, np. CC-BY-4.0, albo null, gdy nie ma identyfikatora SPDX (np. regulamin wydawcy)), namePl, url, permits.display / permits.redistribute (czy wolno wyświetlać / serwować dalej), attributionPl — treść atrybucji, którą trzeba nieść przy wyświetlaniu i redystrybucji, verifiedAt i evidenceUrl — kiedy i gdzie warunki zostały odczytane. Kto re-serwuje te wartości, jest związany tymi warunkami. |
Pola oznaczone ? występują tylko wtedy, gdy katalog coś o serii orzeka — ich brak jest odpowiedzią („nie ustalono”), a nie błędem.
Statusy: 200 (koperta live lub archival), 503 (koperta error).
curl -s https://www.podziemieanalityczne.pl/api/metricsPrzykład zarejestrowany 3 sierpnia 2026, baza w stanie error — kompletna odpowiedź, nie skrót:
HTTP/1.1 503 Service Unavailable
Content-Type: application/json
{"dataStatus":"error","asOf":null,"data":[]}POST /api/metrics/values
Wartości wybranych wskaźników. Metoda wyłącznie POST z ciałem JSON (GET na tym adresie dostaje 405):
{
"metricIds": ["<id z katalogu /api/metrics>"],
"startPeriod": "2024-01"
}| Pole | Typ | Zasady |
|---|---|---|
| metricIds | string[] | wymagane; maksymalnie 20 pozycji, każda niepusta i ≤64 znaki. Identyfikatory pochodzą z pola id katalogu. Walidowany jest tylko kształt żądania — id spoza katalogu nie dają 400. Gdy katalog odpowiada na żywo, odpowiedź ma status 200, a nieznane identyfikatory są wymienione w polu unknownIds: brak takiego wskaźnika to fakt o żądaniu, a nie awaria naszego źródła. Gdy katalogu nie da się odczytać na żywo, nie odróżnimy id nieistniejącego od takiego, którego chwilowo nie widzimy — wtedy odpowiedzią jest koperta error. Pusta lista zwraca pustą kopertę live — nic nie zażądano, więc koperta o niczym nie orzeka. |
| startPeriod | string | opcjonalne; dolna granica okna (włącznie) w formacie 2026-06, 2026-Q2 albo 2026 (lata 1900–2999). Serwer przelicza granicę do częstotliwości danych — częstotliwość każdej serii podaje pole frequencyPl w /api/zrodla. Okres niepoprawny kalendarzowo (np. 2026-13) → 400. |
Odpowiedź: DataEnvelope<MetricValue[]>, gdzie każdy element data ma pola id, metric_id, period (format jak wyżej) i value (liczba). Jeżeli któryś z żądanych identyfikatorów nie istnieje w katalogu serwowanym na żywo, koperta dokłada pole unknownIds — tablicę tych identyfikatorów. Pola nie ma, gdy nie ma czego wypisać.
Kody błędów walidacji (400): INVALID_JSON, INVALID_BODY, INVALID_PERIOD, TOO_MANY_METRIC_IDS, INVALID_METRIC_IDS. Poza tym: 200 z kopertą live/archival albo 503 z kopertą error.
curl -s -X POST https://www.podziemieanalityczne.pl/api/metrics/values \
-H 'Content-Type: application/json' \
-d '{"metricIds":["<id-z-katalogu>"],"startPeriod":"2024-01"}'Przykłady zarejestrowane 3 sierpnia 2026, baza w stanie error. Poprawne żądanie:
HTTP/1.1 503 Service Unavailable
Content-Type: application/json
{"dataStatus":"error","asOf":null,"data":[]}Żądanie z 21 identyfikatorami (limit to 20):
HTTP/1.1 400 Bad Request
Content-Type: application/json
{"code":"TOO_MANY_METRIC_IDS","message":"Za dużo wskaźników w jednym żądaniu."}Żądanie z pustą listą {"metricIds":[]} — jedyna odpowiedź live osiągalna przy leżącej bazie, bo o niczym nie orzeka:
HTTP/1.1 200 OK
Content-Type: application/json
{"dataStatus":"live","asOf":null,"data":[]}GET /api/status
Stan maszynerii danych — ten sam raport, który strona /status pokazuje ludziom. Zawsze 200: ten endpoint raportuje stan, a nie serwuje dane, więc zepsuty pipeline to udany raport z "overall": "error" w ciele. Odpowiedź ma Cache-Control: no-store — każde żądanie to świeże sprawdzenie.
Sprawdzenie bazy to dokładnie ta ścieżka kodu, którą serwuje /api/metrics — nie osobny pomiar HTTP. Sekcja catalog opisuje katalog zapisany w kodzie (fakty statyczne, nie wynik sprawdzenia na żywo) — każdy komponent raportu deklaruje swój rodzaj w polu kind.
overall mówi o dostępności danych, nie o ich wieku. Wiek jest osobnym komponentem: components.freshness z polem status o wartościach ok, stale albo unknown, licznikiem sprawdzonych serii i tablicą stale, w której każda przeterminowana seria jest wymieniona z nazwą, ostatnim okresem i liczbą okresów opóźnienia. Seria, która przestała się aktualizować, nie zmienia overall na error — baza odpowiada, więc awarii nie ma; monitoring, który chce wiedzieć o wieku danych, czyta components.freshness.status. unknown nie znaczy ok: znaczy, że przy tym żądaniu nie dało się zapytać.
curl -s https://www.podziemieanalityczne.pl/api/statusPrzykład zarejestrowany 3 sierpnia 2026 (sformatowany dla czytelności, treść bez zmian), baza w stanie error. Zapis jest starszy niż komponent freshness i dlatego go nie zawiera — przepisanie zarejestrowanej odpowiedzi zrobiłoby z niej zapis, którego nikt nigdy nie otrzymał:
HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: no-store
{
"overall": "error",
"checkedAt": "2026-08-03T20:27:53.723Z",
"components": {
"database": {
"kind": "live-check",
"configured": true,
"status": "failed",
"detail": "Odczyt katalogu z bazy nie powiódł się (baza nieosiągalna albo pusty katalog)."
},
"metricsApi": {
"kind": "live-check",
"endpoint": "/api/metrics",
"dataStatus": "error",
"asOf": null,
"httpStatus": 503,
"servedMetrics": 0,
"detail": "Stan pochodzi z tej samej ścieżki kodu, którą serwuje /api/metrics — to nie jest osobny pomiar HTTP."
},
"catalog": {
"kind": "static",
"metricsDefined": 25,
"wired": { "dbw": 9, "nbp": 3 },
"unwired": 13,
"detail": "Fakty o katalogu zapisanym w kodzie (src/lib/gus/mappings.ts). To nie jest wynik sprawdzenia na żywo."
}
},
"note": "Raport z chwili sprawdzenia. Historii dostępności nie prowadzimy, więc żadnej tu nie ma — ani procentów uptime, ani listy incydentów."
}GET /api/zrodla
Rejestr źródeł wskaźników — maszynowa wersja strony /wykresy/zrodla: dokładne identyfikatory serii, które odpytuje kod pobierający dane (zmienna, przekrój i sposób prezentacji GUS DBW; host i seria NBP), wraz z kanonicznym adresem strony każdego podłączonego wskaźnika. Bez parametrów.
Ten endpoint nie dotyka bazy danych: rejestr jest zapisany w kodzie, więc odpowiedź jest deterministyczna, zawsze 200 — także przy leżącej bazie — i zmienia się wyłącznie z wdrożeniem. Nagłówki: Content-Type: application/json; charset=utf-8 oraz Cache-Control: public, max-age=3600 — ta sama polityka cache co dla /llms.txt i kanałów Atom. Odpowiedź celowo nie zawiera żadnych wartości liczbowych — to spis źródeł; po liczby jest /api/metrics/values.
| Pole | Typ | Znaczenie |
|---|---|---|
| descriptionPl | string | jednozdaniowy opis odpowiedzi |
| generated | string | skąd bierze się odpowiedź (z kodu, bez migawki i bez liczb) |
| humanPageUrl | string | adres wersji dla ludzi |
| count | number | liczba wskaźników we wszystkich trzech grupach łącznie |
| gusDbw | object | pola system, host i metrics[] — serie z kompletem identyfikatorów: zmienna (variable), sposób prezentacji (basis) i okna przekrojów (windows[]: section, from, to — null gdy okno trwa) |
| nbp | object | pola system, apiHost, staticHost i metrics[] — każda seria z polem series: kursy (kind: "fx" z kodem waluty i agregacją) albo stopa referencyjna (kind: "reference-rate" z id pozycji w XML) |
| unwired | object | pola descriptionPl i metrics[] — wskaźniki nazwane w katalogu, ale celowo niepodłączone do czasu weryfikacji identyfikatorów; bez adresu strony, bo strony nie mają |
curl -s https://www.podziemieanalityczne.pl/api/zrodlaPrzykłady zarejestrowane lokalnie z kodu, który obsługuje ten endpoint — odpowiedź jest deterministyczna, więc rejestracja nie ma daty, a test pilnuje, by fragmenty poniżej były bajt w bajt zgodne z aktualną odpowiedzią. Nagłówki:
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Cache-Control: public, max-age=3600Pierwszy element gusDbw.metrics (sformatowany dla czytelności, treść bez zmian) — CPI z trzema oknami API i starszą publikacją w polu archives. Od danych za 2026 r. GUS zmienił klasyfikację COICOP:
{
"slug": "inflacja-cpi",
"namePl": "Inflacja CPI (r/r)",
"variable": 305,
"basis": 5,
"basisLabelPl": "analogiczny okres roku poprzedniego = 100",
"windows": [
{
"section": 736,
"filter": {
"2": 33617,
"562": 6902025,
"563": 6656078
},
"from": "2010-01",
"to": "2013-12",
"rangePl": "styczeń 2010 – grudzień 2013"
},
{
"section": 909,
"filter": {
"2": 33617,
"562": 6902025,
"784": 7215815
},
"from": "2014-01",
"to": "2025-12",
"rangePl": "styczeń 2014 – grudzień 2025"
},
{
"section": 1698,
"filter": {
"2": 33617,
"562": 6902025,
"1337": 14916914
},
"from": "2026-01",
"to": null,
"rangePl": "styczeń 2026 – nadal"
}
],
"frequencyPl": "miesięczna",
"unitPl": "indeks",
"archives": [
{
"slug": "inflacja-cpi",
"from": "1982-01",
"to": "2009-12",
"sources": [
{
"file": "miesieczne_wskazniki_cen_towarow_i_uslug_konsumpcyjnych_od_1982_roku__2_2.csv",
"url": "https://stat.gov.pl/download/gfx/portalinformacyjny/pl/defaultstronaopisowa/4741/1/1/miesieczne_wskazniki_cen_towarow_i_uslug_konsumpcyjnych_od_1982_roku__2_2.csv",
"sha256": "14f3a0a617c98e12eb49aa2fd669cc8e72e300948767b5efd316c6842e21b923"
}
],
"methodPl": "Polska; analogiczny miesiąc poprzedniego roku = 100."
}
],
"pageUrl": "https://www.podziemieanalityczne.pl/dane/inflacja-cpi"
}Element nbp.metrics dla kursu EUR/PLN — identyfikacja serii w polu series, prozą w sourcePl/detailPl:
{
"slug": "kurs-eur-pln",
"namePl": "Kurs EUR/PLN",
"frequencyPl": "miesięczna",
"unitPl": "zł",
"host": "api.nbp.pl",
"series": {
"kind": "fx",
"code": "EUR",
"aggregation": "month-end"
},
"sourcePl": "api.nbp.pl — tabela A kursów średnich",
"detailPl": "kod waluty EUR; wartość miesiąca = ostatnie notowanie w miesiącu (tabela A wychodzi tylko w dni robocze)",
"archives": [
{
"slug": "kurs-eur-pln",
"from": "1999-01",
"to": "2001-12",
"sources": [
{
"file": "archiwum_tab_a_1999.xls",
"url": "https://static.nbp.pl/dane/kursy/Archiwum/archiwum_tab_a_1999.xls",
"sha256": "d0e98f9ce3ed2476377e924ce58d4d54f5d08b51105654d07b1fb427883b5ccf"
},
{
"file": "archiwum_tab_a_2000.xls",
"url": "https://static.nbp.pl/dane/kursy/Archiwum/archiwum_tab_a_2000.xls",
"sha256": "a89a80286d3499bbd1754332cc445adf2699d3d2785923e2be4979ad1ba99056"
},
{
"file": "archiwum_tab_a_2001.xls",
"url": "https://static.nbp.pl/dane/kursy/Archiwum/archiwum_tab_a_2001.xls",
"sha256": "1af39509050e27cae17403fd2f144d6e120c578823592e752e78a45602a75d7e"
}
],
"methodPl": "Ostatnie notowanie tabeli A w miesiącu, w PLN za 1 EUR."
}
],
"pageUrl": "https://www.podziemieanalityczne.pl/dane/kurs-eur-pln"
}POST /api/charts/share
Tworzy link do udostępnionego wykresu: zapisuje wybór wskaźników i konfigurację, zwraca {"id":"..."} (8 znaków), a wykres jest potem dostępny pod /wykresy/s/{id}. Ciało żądania:
{
"selectedIds": ["<id z katalogu /api/metrics>"],
"config": {
"mode": "values",
"timeframe": "1R",
"dateMarkers": [],
"levelLines": []
}
}| Pole | Zasady |
|---|---|
| selectedIds | wymagane; co najmniej jedna pozycja, każda niepusta i ≤64 znaki; każde id musi istnieć w żywym katalogu — inaczej 400 SHARE_UNKNOWN_METRICS; najwyżej 12 różnych serii w jednym wykresie (duplikaty liczymy raz) — inaczej 400 SHARE_TOO_MANY_SERIES |
| config.mode | values albo indexed |
| config.timeframe | 1M, 3M, 6M, 1R, 2L, 5L, max albo custom |
| config.customStartPeriod | okres w formacie 2026-06 / 2026-Q2 / 2026 |
| config.forceSingleAxis | boolean |
| config.showTrend | boolean |
| config.dateMarkers | tablica ≤50 elementów; każdy element musi mieć poprawne pole period. Pola label i color są opcjonalne, ale sprawdzane: etykieta to tekst ≤80 znaków bez znaków sterujących, kolor to zapis szesnastkowy #RGB albo #RRGGBB |
| config.levelLines | tablica ≤50 elementów; każdy element musi mieć liczbowe pole value. label i color jak wyżej |
Pole config jest wymagane i musi zawierać tablice dateMarkers oraz levelLines — mogą być puste, ale muszą istnieć, a ich elementy nie mogą być puste (null). Bez nich generator wykresu przerywa rysowanie, więc taki link byłby martwy pod publicznym adresem — odpowiadamy 400 SHARE_CONFIG_UNRENDERABLE. Do 7 sierpnia 2026 r. przyjmowaliśmy tu pusty obiekt {} i ta dokumentacja tak mówiła; powstałych w ten sposób linków nie da się już naprawić. Pozostałe pola z listy powyżej są opcjonalne, a walidowane są tylko wartości pól z tej listy (pola spoza niej nie są sprawdzane). Link powstaje tylko przy żywym katalogu i żywej bazie: inaczej 503 SHARE_SOURCE_UNAVAILABLE. To celowe — link wybity przeciw martwemu katalogowi byłby martwy od urodzenia.
Limit żądań (jedyny w tym API): 30 na godzinę z jednego adresu IP → 429 RATE_LIMITED. Adres odczytujemy z nagłówka ustawianego przez platformę, a nie z tego, co poda klient — dopisanie własnego wpisu do x-forwarded-for nie otwiera nowej puli. Licznik jest wspólny dla wszystkich instancji; gdy jego magazyn nie odpowiada, limit nadal obowiązuje, ale tylko w obrębie pojedynczej instancji. To zabezpieczenie przed nadużyciem, nie gwarantowana kwota. Pozostałe kody: 400 INVALID_SELECTION / SHARE_CONFIG_UNRENDERABLE / INVALID_CONFIG / SHARE_STATE_TOO_LARGE (całe state ponad 16 KB) / SHARE_UNKNOWN_METRICS / SHARE_TOO_MANY_SERIES; 503 SHARE_CREATE_FAILED (zapis się nie powiódł). Ciało niebędące poprawnym JSON-em dostaje 400 SHARE_CREATE_FAILED — zaobserwowane 3 sierpnia 2026, nie INVALID_JSON jak w /api/metrics/values.
Przykład zarejestrowany 3 sierpnia 2026, baza w stanie error — poprawne żądanie, odmowa zgodna z kontraktem:
HTTP/1.1 503 Service Unavailable
Content-Type: application/json
{"code":"SHARE_SOURCE_UNAVAILABLE","message":"Udostępnianie jest chwilowo niedostępne — źródło danych nie odpowiada."}GET /api/charts/share/{id}
Zapisany stan udostępnionego wykresu: obiekt { selectedIds, config } — konfiguracja, nie dane liczbowe (te trzeba dobrać przez /api/metrics/values). Odczyt zwiększa licznik wyświetleń.
Oddajemy zapisany obiekt dosłownie i nie oceniamy, czy da się z niego narysować wykres. Zasady zapisu zaostrzyły się 7 sierpnia 2026 r., a starszych wpisów nie ruszamy — więc zapis może być nierysowalny, a odpowiedź i tak ma kod 200. To celowe: gdyby odczyt sądził konfigurację, każde przyszłe zaostrzenie zasad unieważniałoby działające linki. Ocena rysowalności żyje na stronie HTML /wykresy/s/{id}, która w takim wypadku mówi wprost, że link jest uszkodzony.
id to 8 znaków z alfabetu A–Z a–z 0–9 _ -; inny format → 400 INVALID_SHARE_ID. Nieistniejący link → 404 SHARE_NOT_FOUND. Uwaga: kod odpowiada 404 także wtedy, gdy zapytanie do bazy się nie powiedzie — przy niedostępnej bazie każdy odczyt kończy się 404 (zaobserwowane 3 sierpnia 2026). 503 SHARE_SOURCE_UNAVAILABLE pojawia się tylko, gdy klient bazy w ogóle nie jest skonfigurowany.
curl -s https://www.podziemieanalityczne.pl/api/charts/share/aaaaaaaaPrzykład zarejestrowany 3 sierpnia 2026 (id o poprawnym formacie, bez istniejącego wpisu; baza niedostępna):
HTTP/1.1 404 Not Found
Content-Type: application/json
{"code":"SHARE_NOT_FOUND","message":"Nie znaleziono takiego wykresu."}CORS i użycie w przeglądarce
Publiczne endpointy odczytu pozwalają na wywołania z innej domeny: Access-Control-Allow-Origin: *. Endpointy GET obsługują też HEAD i OPTIONS; /api/metrics/values pozwala na POST i OPTIONS z nagłówkiem Content-Type. Preflight odpowiada kodem 204. Dane są publiczne, więc żądania nie wysyłają poświadczeń.
Przykład do wklejenia na statycznej stronie działającej pod inną domeną:
async function fetchPublicMetrics() {
const catalogueResponse = await fetch('https://www.podziemieanalityczne.pl/api/metrics');
const catalogue = await catalogueResponse.json();
const metricIds = catalogue.data.slice(0, 3).map(({ id }) => id);
const valuesResponse = await fetch('https://www.podziemieanalityczne.pl/api/metrics/values', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ metricIds }),
});
return valuesResponse.json();
}
fetchPublicMetrics().then(console.log).catch(console.error);Inne maszynowe adresy
/analizy/feed.xmli/zmiany/feed.xml— kanały Atom 1.0: opublikowane artykuły z /analizy (wyłącznie opublikowane — szkice są niewidoczne, a kanał bez wpisów jest poprawnym, pustym Atomem) oraz dziennik zmian z /zmiany — te same wpisy co na stronie, słowo w słowo. Oba wysyłająContent-Type: application/atom+xmliCache-Control: public, max-age=3600. Dawny adres/feed.xml(RSS 2.0) odpowiada przekierowaniem301na kanał artykułów./llms.txt— opis serwisu dla agentów i modeli językowych według konwencji llms.txt: filary, kluczowe adresy i zasady danych (Markdown serwowany jakotext/plain)./sitemap.xmli/robots.txt— standardowe./statuscelowo nie figuruje w sitemap: strona raportuje stan z chwili sprawdzenia i sama prosi roboty o nieindeksowanie, a wpis w sitemap prosiłby o coś przeciwnego.- /status — raport z
/api/statusw wersji dla ludzi; obie strony powstają z tego samego obiektu, więc nie mogą się rozjechać.