API kodów pocztowych, TERYT i PRG

Darmowe REST API do danych adresowych Polski. Kody pocztowe, TERYT SIMC/ULIC, punkty adresowe PRG (GUGiK), geokodowanie WGS84, dopasowywanie i walidacja adresów. Odpowiedzi w JSON, bez rejestracji dla limitów testowych, klucz API dla produkcji.

Gotowy zacząć? Załóż darmowe konto i wygeneruj klucz API w 30 sekund — plan Free 3 000 zapytań/miesiąc, bez karty.
Załóż darmowe konto Zaloguj

Quickstart — pierwszy request w 30 sekund

Wszystkie endpointy są pod https://api.adresy.app/api/v1. Odpowiedzi to JSON kodowany UTF-8. Metoda GET dla odczytu, POST dla operacji wsadowych i dopasowywania.

curl -s "https://api.adresy.app/api/v1/match?q=Rynek+1,+Wrocław"

Przykładowa odpowiedź:

{
  "results": [
    {
      "input": "Rynek 1, Wrocław",
      "status": "found",
      "match": {
        "miejscowosc": "Wrocław",
        "ulica_norm": "Rynek",
        "nr_budynku": "1",
        "kod_pocztowy": "50-106",
        "teryt_simc": "0986283",
        "teryt_ulic": "19357",
        "gmina": "Wrocław",
        "powiat": "Wrocław",
        "wojewodztwo": "DOLNOŚLĄSKIE",
        "lat": 51.10893,
        "lon": 17.03262,
        "score": 0.98
      }
    }
  ]
}

Najprostszy sposób — GET /api/v1/match?q= — przyjmuje dowolny „brudny" adres w jednym parametrze i zwraca dopasowanie z rejestru PRG.

Przykładowa odpowiedź (skrócona):

{
  "kod": "50-079",
  "found": true,
  "total": 412,
  "ulic": 7,
  "results": [
    {
      "miejscowosc": "Wrocław",
      "ulica": "Ruska",
      "teryt_simc": "0986283",
      "teryt_ulic": "18834",
      "gmina": "Wrocław",
      "powiat": "Wrocław",
      "wojewodztwo": "dolnośląskie",
      "liczba_adresow": 58,
      "numery_przyklad": ["11","13","15","17","19","21","23","25","27","29"]
    }
  ]
}

Uwierzytelnianie i limity

Bez klucza API
3 zapytania / minutę (na IP). Wystarczające do szybkich testów.
Klucz API (Free)
30 zapytań / minutę, 3 000 / miesiąc. Darmowe konto + klucz API.
Klucz API (Business)
600 zapytań / minutę, 10 mln / miesiąc, SLA 99,9%.
Enterprise
Dedykowana infrastruktura, webhooki PRG, on-premise.
curl -s "https://api.adresy.app/api/v1/lookup/kod-pocztowy?kod=00-001" \
  -H "X-API-Key: sk_live_xxxxxxxxxxxxxxxx"

Limit przekroczony? Odpowiedź 429 Too Many Requests z nagłówkiem Retry-After w sekundach. Szczegóły w artykule o limitach.

1. Wyszukiwanie po kodzie pocztowym

GET /api/v1/lookup/kod-pocztowy

Zwraca wszystkie ulice i grupy numerów budynków przypisane do danego kodu pocztowego. Dane pochodzą z rejestru PRG (GUGiK), agregowane per ulica.

Parametry

NazwaTypWymaganeOpis
kodstringtakKod pocztowy w formacie NN-NNN, np. 50-079.
limitintnieMaksymalna liczba ulic w odpowiedzi. Domyślnie 200, maksymalnie 500.

Przykład — curl

curl -s "https://api.adresy.app/api/v1/lookup/kod-pocztowy?kod=00-001&limit=50"

Przykład — Python

import httpx

r = httpx.get(
    "https://api.adresy.app/api/v1/lookup/kod-pocztowy",
    params={"kod": "00-001"},
    headers={"X-API-Key": "sk_live_xxx"},
    timeout=10,
)
data = r.json()
print(f"Znaleziono {data['total']} adresów na {data['ulic']} ulicach")

Przykład — JavaScript (fetch)

const res = await fetch(
  "https://api.adresy.app/api/v1/lookup/kod-pocztowy?kod=00-001",
  { headers: { "X-API-Key": "sk_live_xxx" } }
);
const data = await res.json();

2. Kod pocztowy dla adresu

GET /api/v1/lookup/kod-dla-adresu

Odwrotne zapytanie — podaj miejscowość, ulicę i numer, otrzymasz kod pocztowy (lub zakres kodów, jeśli ulica obejmuje kilka). Idealne do walidacji formularzy e-commerce.

curl -s "https://api.adresy.app/api/v1/lookup/kod-dla-adresu" \
  --data-urlencode "miejscowosc=Wrocław" \
  --data-urlencode "ulica=Rynek" \
  --data-urlencode "nr=1" \
  -G
{
  "found": true,
  "kod_pocztowy": "50-106",
  "miejscowosc": "Wrocław",
  "ulica": "Rynek",
  "nr": "1",
  "teryt_simc": "0986283",
  "teryt_ulic": "19357"
}

3. TERYT — SIMC i ULIC

GET /api/v1/lookup/teryt

Zwraca kody TERYT (SIMC dla miejscowości, ULIC dla ulic) wraz z przynależnością administracyjną: gmina, powiat, województwo. Obsługuje wyszukiwanie po nazwie miejscowości, nazwie ulicy lub bezpośrednio po kodzie SIMC.

curl -s "https://api.adresy.app/api/v1/lookup/teryt" \
  --data-urlencode "miejscowosc=Kraków" \
  --data-urlencode "ulica=Floriańska" -G
{
  "found": true,
  "results": [{
    "simc": "0950463",
    "nazwa": "Kraków",
    "typ": "miasto",
    "gmina": "Kraków",
    "powiat": "Kraków",
    "wojewodztwo": "małopolskie",
    "kody_admin": "12612011",
    "ulice": [{
      "ulic": "05123",
      "cecha": "ul.",
      "nazwa_1": "Floriańska",
      "nazwa_pelna": "ul. Floriańska"
    }]
  }]
}

4. Dekodowanie TERYT

GET /api/v1/lookup/teryt/decode

Zamienia kody TERYT na pełne dane adresowe. Obsługuje automatyczną detekcję (SIMC — 7 cyfr, ULIC — 5 cyfr) i format złożony SIMC#ULIC#NR.

# Auto-detect kodu
curl -s "https://api.adresy.app/api/v1/lookup/teryt/decode?kod=0986283"

# Pełny adres
curl -s "https://api.adresy.app/api/v1/lookup/teryt/decode?kod=0986283%2318834%2315"

5. Adresy w pobliżu punktu (geokodowanie odwrotne)

GET /api/v1/lookup/blisko

Zwraca najbliższe punkty adresowe dla podanych współrzędnych WGS84. Silnik: PostGIS, indeks GiST, promień podajesz w metrach.

curl -s "https://api.adresy.app/api/v1/lookup/blisko?lat=51.1089&lon=17.0326&radius=200&limit=5"
{
  "found": true,
  "count": 5,
  "results": [
    {
      "miejscowosc": "Wrocław",
      "ulica": "Rynek",
      "nr_budynku": "1",
      "kod_pocztowy": "50-106",
      "distance_m": 12.4,
      "lat": 51.10893,
      "lon": 17.03262
    }
  ]
}

6. Szybkie dopasowanie adresu — GET /match?q=

GET /api/v1/match

Najprostszy endpoint — podaj adres jako tekst w parametrze q, otrzymasz dopasowanie z pełnym TERYT, kodem pocztowym i współrzędnymi. Idealny do integracji formularzy, walidacji adresów w locie i autouzupełniania.

Parametry

NazwaTypWymaganeOpis
qstringtakAdres do dopasowania, np. Rynek 1, Wrocław lub ul. Marszalkowska 1 Warszawa.
min_scorefloatnieMinimalny score dopasowania (0-1). Domyślnie 0.6.
wojewodztwostringnieOgraniczenie do województwa (np. dolnośląskie).

Przykład — curl

curl -s "https://api.adresy.app/api/v1/match?q=Rynek+1,+Wrocław"

Przykład — JavaScript

const res = await fetch(
  "https://api.adresy.app/api/v1/match?q=" + encodeURIComponent("Rynek 1, Wrocław"),
  { headers: { "X-API-Key": "sk_live_xxx" } }
);
const data = await res.json();
console.log(data.results[0].match.kod_pocztowy);

Odpowiedź

{
  "results": [
    {
      "input": "Rynek 1, Wrocław",
      "status": "found",
      "match": {
        "miejscowosc": "Wrocław",
        "ulica_norm": "Rynek",
        "nr_budynku": "1",
        "kod_pocztowy": "50-106",
        "teryt_simc": "0986283",
        "teryt_ulic": "19357",
        "gmina": "Wrocław",
        "powiat": "Wrocław",
        "wojewodztwo": "DOLNOŚLĄSKIE",
        "lat": 51.10893,
        "lon": 17.03262,
        "score": 0.98,
        "match_layer": "EXACT"
      }
    }
  ]
}

Pola status: found (jednoznaczne dopasowanie), ambiguous (kilka kandydatów, zwrócony najlepszy), not_found (brak dopasowania).

7. Dopasowywanie adresów — POST /match

POST /api/v1/match

Najmocniejszy endpoint. Bierze „brudny" adres (literówki, skróty, bez kodu) i dopasowuje go do rzeczywistego punktu PRG z pełnym TERYT, kodem pocztowym i współrzędnymi. Używa fuzzy-match (RapidFuzz) + heurystyk skrótów polskich.

curl -s -X POST "https://api.adresy.app/api/v1/match" \
  -H "Content-Type: application/json" \
  -H "X-API-Key: sk_live_xxx" \
  -d '{
    "adres": "ul. Marsz Pilsudskiego 5a, Wroclaw",
    "min_score": 80
  }'
{
  "match": true,
  "score": 94,
  "result": {
    "miejscowosc": "Wrocław",
    "ulica_wejsciowa": "Marsz Pilsudskiego",
    "ulica_dopasowana": "Marszałka Józefa Piłsudskiego",
    "nr_budynku": "5A",
    "kod_pocztowy": "50-033",
    "teryt_simc": "0986283",
    "teryt_ulic": "12873",
    "gmina": "Wrocław",
    "powiat": "Wrocław",
    "wojewodztwo": "dolnośląskie",
    "lat": 51.10012,
    "lon": 17.03159,
    "srid": 4326
  },
  "candidates": 3
}

8. Walidacja pliku CSV/XLSX

POST /api/v1/match/file

Prześlij plik z listą adresów (kolumny: miejscowosc, ulica, nr, kod), otrzymasz ten sam plik z dopisanymi kolumnami: dopasowanie, TERYT, współrzędne, score. Format wejściowy: CSV (UTF-8) lub XLSX, do 100 tys. wierszy na request.

curl -s -X POST "https://api.adresy.app/api/v1/match/file" \
  -H "X-API-Key: sk_live_xxx" \
  -F "[email protected]" \
  -F "min_score=82" \
  -o wynik.csv

Tryb streaming — wyniki lecą NDJSON zanim całość się zakończy:

curl -N -X POST "https://api.adresy.app/api/v1/match/stream" \
  -H "X-API-Key: sk_live_xxx" \
  -F "[email protected]"

Kody błędów

StatusZnaczenieDziałanie
200OK
400Nieprawidłowe parametrySprawdź pole detail w JSON.
401Brak / zły klucz APINagłówek X-API-Key.
404Nie znalezionoPole found: false jest normą, nie błędem.
429Rate limitOdczytaj Retry-After, zwolnij.
500Błąd serweraPonów z backoffem. Zgłoś przez /kontakt/.

FAQ — najczęstsze pytania developerów

Czy API jest darmowe?
Tak — bez rejestracji masz 3 zapytania na minutę na adres IP. Załóż darmowe konto i wygeneruj klucz API, aby uzyskać 30 req/min (plan Free). Plan Business (600 req/min, 10 mln/miesiąc) jest przeznaczony dla wdrożeń produkcyjnych.
Skąd pochodzą dane adresowe?
Bezpośrednio z publicznych rejestrów państwowych. Punkty adresowe — PRG (Państwowy Rejestr Granic) prowadzony przez GUGiK. Kody TERYT (SIMC, ULIC, TERC) — GUS. Kody pocztowe pochodzą z przypisania pocztowego w PRG plus weryfikacja krzyżowa z rejestrem Poczty Polskiej.
Jak często aktualizowane są dane?
PRG pobierany jest codziennie w nocy (import przyrostowy przez GML z geoportal.gov.pl). TERYT aktualizowany jest miesięcznie, zgodnie z cyklem publikacji GUS. Kody pocztowe — przy każdej aktualizacji PRG. Webhook dostępny w planie Enterprise — powiadomienie o każdej zmianie w wybranym obszarze.
Czy mogę używać API komercyjnie?
Tak, w ramach dowolnego planu z kluczem API. Dane PRG i TERYT są publiczne i redystrybucja jest dozwolona. Wymagamy jedynie podania źródła (Adresy.app) w przypadku publicznej prezentacji danych — dla wewnętrznych systemów nie jest wymagane.
Jak działa endpoint /match i kiedy go używać?
POST /api/v1/match przyjmuje „brudny" ciąg tekstowy adresu (literówki, polskie skróty typu „ul.", „pl.", brak kodu pocztowego) i dopasowuje go do kanonicznego rekordu PRG. Używa algorytmów fuzzy-match (RapidFuzz, odległość Levenshteina) plus heurystyk dla polskich skrótów. Używaj, gdy masz dane od użytkownika (formularz, import CSV, OCR). Dla czystych danych z wiarygodnego źródła szybszy jest /lookup/kod-dla-adresu.
Jakie są limity batcha /match/file?
100 tys. wierszy na pojedynczy request. Dla większych zbiorów użyj /api/v1/match/stream (NDJSON, streaming odpowiedzi — klient może przetwarzać wyniki równolegle z uploadem) lub podziel plik. Czas przetwarzania to ~200-500 wierszy na sekundę w zależności od jakości danych wejściowych.
Czy zwracacie współrzędne w WGS84?
Tak — wszystkie współrzędne w polach lat, lon są w układzie WGS84 (EPSG:4326). Wewnętrznie dane PRG przechowywane są w PUWG 1992 (EPSG:2180) i konwertowane w locie przez PyProj. Jeśli potrzebujesz natywnego układu polskiego, dodaj parametr srid=2180.
Co zrobić przy 429 Too Many Requests?
Odczytaj nagłówek Retry-After (liczba sekund), zaczekaj i ponów. Dla klienta produkcyjnego zaimplementuj wykładniczy backoff: 1s, 2s, 4s, 8s, 16s. Jeśli widzisz 429 przy normalnym obciążeniu, zwiększ plan lub rozważ cache lokalny — wiele zapytań o ten sam kod pocztowy można trzymać w Redis/memcached na 24h (dane zmieniają się rzadko).
Czy jest OpenAPI / Swagger?
Tak. Pełna specyfikacja OpenAPI 3.1 dostępna pod https://api.adresy.app/openapi.json, interaktywny Swagger UI pod /docs, Redoc pod /redoc. Możesz wygenerować klienta w dowolnym języku przez openapi-generator.
Czy dane są zgodne z RODO?
Tak. API zwraca wyłącznie dane adresowe (budynki, ulice, kody), które nie są danymi osobowymi w rozumieniu RODO. Nie logujemy zapytań klientów z kluczami API poza metrykami użycia (liczba requestów). Serwery znajdują się w EU (Falkenstein, DE). Umowa powierzenia dostępna w planie Business.
Jak dodać obsługę adresów historycznych (np. poprzednie nazwy ulic)?
Pole nazwa_2 w rekordzie ULIC zawiera poprzednią nazwę ulicy (gdy istnieje). Endpoint /lookup/teryt zwraca oba warianty. Aliasy historyczne (Stalingradu → Marszałkowska, etc.) przechowujemy osobno — dostępne w planie Business przez parametr historyczne=true.
Mogę hostować API u siebie (on-premise)?
Tak, w planie Enterprise. Dostarczamy Docker image (FastAPI + PostGIS), skrypt importu PRG/TERYT, dashboard monitoringu. Licencja roczna, aktualizacje danych przez repozytorium prywatne. Kontakt: kontakt.

Następne kroki