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
📚 Nowy w API? Przeczytaj Przewodnik po API dla początkujących — tłumaczy krok po kroku, zawiera gotowy kod Python, Make.com, n8n i Google Sheets.

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
    }
  ]
}

5b. Geokodowanie odwrotne — GET /api/v1/reverse

GET /api/v1/reverse

Podaj współrzędne WGS84, otrzymaj jeden najbliższy punkt adresowy PRG z odległością w metrach. Endpoint zoptymalizowany pod wysokie obciążenie: indeks KNN PostGIS, czas odpowiedzi <10 ms. Zasięg: cała Polska (49–54,9°N, 14,1–24,2°E).

Różnica od /lookup/blisko: /reverse zwraca jeden rekord z polem score (1,0 = dokładnie na adresie, 0,0 = na granicy promienia), /lookup/blisko zwraca listę wielu adresów w promieniu bez score.

ParametrTypDomyślnieOpis
latfloatSzerokość geograficzna WGS84
lonfloatDługość geograficzna WGS84
radiusint500Promień w metrach (1–5000)
limitint1Liczba wyników (1–10)
curl -s "https://api.adresy.app/api/v1/reverse?lat=52.2297&lon=21.0122&radius=500"
{
  "found": true,
  "count": 1,
  "results": [
    {
      "miejscowosc": "Warszawa",
      "ulica": "Nowy Świat",
      "nr_budynku": "18",
      "kod_pocztowy": "00-373",
      "powiat": "Warszawa",
      "wojewodztwo": "mazowieckie",
      "teryt_simc": "0918123",
      "dzielnica": "Śródmieście",
      "distance_m": 34.2,
      "score": 0.93,
      "lat": 52.22951,
      "lon": 21.01198
    }
  ]
}

Typowe zastosowania: lokalizacja GPS w aplikacji mobilnej → nazwa ulicy, kliknięcie na mapę → pełny adres, geofencing, IoT (czujniki z GPS), walidacja czy punkt jest w zasięgu adresu.

6. Autouzupełnianie formularza — GET /lookup/autofill

GET /api/v1/lookup/autofill

Ostatni krok trójstopniowego autocomplete: po wybraniu przez użytkownika miejscowości (/miejscowosci), ulicy (/ulice) i numeru (/numery) — wywołaj autofill, aby jednym zapytaniem wypełnić wszystkie pozostałe pola formularza: kod pocztowy, gminę, powiat, województwo i współrzędne GPS.

Wymaga klucza API z planem Starter lub wyższym (capability autocomplete).

Parametry

ParametrTypOpis
symstring, wymaganyKod SIMC miejscowości (z /miejscowosci)
sym_ulstring, opcjonalnyKod ULIC ulicy (z /ulice); pominąć dla adresów bez nazwy ulicy
nrstring, wymaganyNumer budynku (z /numery)
curl -s "https://api.adresy.app/api/v1/lookup/autofill?sym=0986283&sym_ul=18834&nr=1" \
  -H "X-API-Key: twoj-klucz"
{
  "found": true,
  "miejscowosc": "Wrocław",
  "ulica": "Rynek",
  "nr_budynku": "1",
  "kod_pocztowy": "50-106",
  "gmina": "Wrocław",
  "powiat": "Wrocław",
  "wojewodztwo": "dolnośląskie",
  "teryt_simc": "0986283",
  "teryt_ulic": "18834",
  "lat": 51.110051,
  "lon": 17.031717
}

Gdy adres nie istnieje w PRG:

{ "found": false }

7. 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).

8. 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
}

9. 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 zbudować autocomplete adresu i autouzupełnianie formularza?
Używamy trzystopniowego flow: (1) GET /api/v1/miejscowosci?q=wrocł — użytkownik wpisuje miasto, dostajesz listę z kodami SIMC; (2) GET /api/v1/ulice?sym=0986283&q=ryn — po wybraniu miasta pobierasz ulice (kody ULIC); (3) GET /api/v1/numery?sym=0986283&sym_ul=18834 — po wybraniu ulicy pobierasz numery budynków. Na końcu: GET /api/v1/lookup/autofill?sym=0986283&sym_ul=18834&nr=1 — jednym zapytaniem wypełniasz wszystkie pola: kod pocztowy, gmina, powiat, województwo, współrzędne GPS. Wszystkie cztery endpointy wymagają planu Starter+.
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.
Jaka jest różnica między /api/v1/reverse a /lookup/blisko?
GET /api/v1/reverse to endpoint geokodowania odwrotnego — podaj współrzędne GPS, otrzymaj jeden (lub kilka) najbliższy punkt adresowy PRG z polem score (1,0 = idealnie na adresie, 0,0 = na granicy promienia). Optymalizowany pod wysoką przepustowość: indeks KNN PostGIS, czas odpowiedzi poniżej 10 ms. GET /api/v1/lookup/blisko zwraca listę wszystkich adresów w zadanym promieniu bez scoringu — przydatne gdy potrzebujesz wszystkich sąsiadów punktu. Do geokodowania odwrotnego (GPS → nazwa ulicy) używaj /reverse.
Jak zintegrować geokodowanie odwrotne z aplikacją mobilną?
Pobierz pozycję z navigator.geolocation.getCurrentPosition() (web) lub CLLocationManager/FusedLocationProvider (iOS/Android), następnie wywołaj GET /api/v1/reverse?lat={lat}&lon={lon}. Endpoint działa w promieniu do 5 km i zwraca adres z dokładnością punktu adresowego PRG (centroid budynku). Czas odpowiedzi <10 ms nadaje się do użycia w pętli odświeżania GPS bez cachowania.

Następne kroki