Analizy Rynku 10 min czytania

Open Data polskich przetargów 2024–2025 — 1,4 mln ogłoszeń z BZP i TED na GitHubie

Pełna baza polskich zamówień publicznych z dwóch ostatnich lat jest teraz otwartym zbiorem danych. CSV i Parquet, licencja CC BY 4.0, anonimizacja JDG zgodna z RODO. 1,4 mln ogłoszeń, 23 tys. zamawiających, 82 tys. wykonawców — do pobrania w minutę, do cytowania przez CITATION.cff.

A
Atlas Przetargów
Publikacja: 17 kwietnia 2026

Zespół analityków danych specjalizujących się w zamówieniach publicznych. Dane pochodzą z oficjalnych źródeł: BZP i TED.

Od dzisiaj pełna baza polskich zamówień publicznych z lat 2024–2025 jest dostępna jako otwarty zbiór danych na GitHubie. 1,4 miliona ogłoszeń z BZP i TED, 23 tysiące profili zamawiających, 82 tysiące profili wykonawców. Format CSV i Parquet, licencja CC BY 4.0, kod źródłowy na MIT. Dane z mocy prawa są publiczne (PZP art. 269), jednak w publikowanym pakiecie dodatkowo hashujemy NIP-y wykonawców rozpoznanych jako osoby fizyczne — dobrowolny środek ostrożności przy zbiorczej redystrybucji.

Co premiera zmienia

Do tej pory ktokolwiek chcący analizować polski rynek zamówień publicznych musiał sam scraper'ować BZP, przetwarzać XML-e TED, łączyć ogłoszenia otwierające z wynikowymi, normalizować kody województw i numery NIP. Kilka dni pracy zanim dostanie się pierwszy realnie użyteczny arkusz. Po premierze tego datasetu wystarczy pandas.read_parquet(...) — i masz gotową bazę do analiz, ML, badań naukowych, publicystyki danowej.

Zasięg Dwa lata najnowszych danych

2024 i 2025 razem — 1,4 miliona ogłoszeń. To pełna pokrywa obu źródeł: krajowego BZP (Biuletyn Zamówień Publicznych) i unijnego TED (Tenders Electronic Daily). Kolejne lata zostaną dołożone w następnych releasach.

Licencja CC BY 4.0 na dane, MIT na kod

Dane możesz używać komercyjnie, redystrybuować, przetwarzać — wymagane jest tylko wskazanie źródła. Kod eksportu i anonimizacji jest na MIT, więc jeśli chcesz zregenerować albo zbudować własną wersję — kod jest otwarty.

Publiczne + środek ostrożności Hash NIP-ów osób fizycznych

Dane z BZP i TED są z urzędu publiczne (PZP art. 269, EU Open Data Directive). W interfejsie Atlas pokazujemy je bez zmian. W publikowanym datasecie dodatkowo hashujemy stabilnym SHA-256 NIP-y wykonawców rozpoznanych jako osoby fizyczne (CEIDG, PESEL) — to dobrowolne zabezpieczenie przy zbiorczej redystrybucji na CC BY, nie wymóg prawny.

Formaty Parquet dla analityki, CSV dla wszystkich

Parquet z kompresją zstd jest w repozytorium (76 + 89 MB dla dwóch lat tenderów). CSV-y są w release assets — skompresowane gzip-em, łącznie 263 MB. Do wyboru, do koloru.

OGŁOSZEŃ 1 403 436 z BZP i TED, 2024–2025
ZAMAWIAJĄCYCH 23 753 agregowanych profili po NIP
WYKONAWCÓW 81 674 w tym 41% anonimizowanych
LICENCJA CC BY 4.0 komercyjna redystrybucja OK

Dla kogo ten dataset

Trzy typy odbiorców, którzy od lat robili podobne rzeczy własnymi siłami — i dla których ta publikacja oznacza konkretne godziny odzyskanego czasu:

  • Badacze akademiccy i doktoranci — ekonomia zamówień publicznych, konkurencja w postępowaniach, analiza cen, detekcja zmów. Polski rynek zamówień jest wart ponad 300 mld zł rocznie, a dostępność surowych danych była dotąd fragmentaryczna. Dataset ma plik CITATION.cff, więc Zotero, Mendeley i Google Scholar zaciągną cytowanie automatycznie.
  • Dziennikarze danowi i analitycy — Gazeta Wyborcza, Rzeczpospolita, Fundacja Stocznia, wykonawcy raportów dla Sejmu. Rynek publikuje teksty o wartościach kontraktów, nepotyzmie, koncentracji rynku — z tym datasetem nie trzeba negocjować z UZP o plik, wystarczy pobrać Parquet.
  • Developerzy ML i NLP — polskojęzyczny korpus tytułów i ogłoszeń rzędu 1,4 mln rekordów to wdzięczna baza do klasyfikacji (predykcja kategorii CPV), regresji (estymacja wartości), NER (ekstrakcja podmiotów z tytułów). Format Parquet jest natywny dla pandas, polars, DuckDB i Apache Arrow — żadnego ETL-a po drodze.

Metodologia

Dataset nie jest prostym dumpem tabeli z bazy produkcyjnej. Między surowymi plikami z BZP a tym, co trafiło na GitHub, jest pięć kroków przetwarzania. Każdy z nich dokumentujemy — od źródeł do anonimizacji — bo transparentność metodologii jest tym, co odróżnia użyteczny dataset od trzeciorzędowego CSV-a.

1. Źródła

Dwa niezależne kanały, oba oficjalne i publiczne:

ŹródłoRozwinięcieCo zawiera
bzpBiuletyn Zamówień Publicznych, ezamowienia.gov.plPolski krajowy portal publikacyjny prowadzony przez UZP. Wszystkie ogłoszenia poniżej i powyżej progu unijnego wymagane prawem PZP. Format: OCDS (Open Contracting Data Standard), JSON-y po REST API.
tedTenders Electronic Daily, ted.europa.euUnijna baza ogłoszeń powyżej progu UE, prowadzona przez Biuro Publikacji UE. Publikacje w nowym formacie eForms (XML) od 2023, wcześniejsze pod standardem TED eSender. Zbieramy polskie (country = PL).

Oba źródła są publiczne, ale mają różne identyfikatory, różne struktury, różne cykle publikacji. Surowe pliki wymagają integracji — i właśnie tą integracją zajmuje się Atlas od 2021 roku.

2. Deduplikacja BZP ↔ TED

Duże postępowania (powyżej ~430 tys. EUR dla dostaw i usług, ~5,4 mln EUR dla robót) są publikowane równocześnie w BZP i w TED. Jedno ogłoszenie, dwa rekordy w surowych danych — to potencjalne 20–25% duplikatów w analizach branżowych. Żeby temu zapobiec, każdy rekord TED ma flagę is_duplicate ustawianą przez naszą logikę dopasowania (porównujemy numery referencyjne, NIP zamawiającego, tytuł po normalizacji, datę publikacji w oknie ±3 dni).

Do analiz zdedupliokowanych filtruj is_duplicate = false. Do analizy per-źródło (np. ile postępowań publikowane jest w TED) — zostaw flagę, użyj kolumny source.

3. Geokodowanie i normalizacja

Każde miasto występujące w bazie jest rozwiązywane do współrzędnych geograficznych (latitude, longitude) oraz kodu województwa w standardzie NUTS-2 (PL11, PL12, ..., PL63). Tabelka city_cache zawiera samą mapę nazwa miasta → współrzędne, licząca prawie 5 tysięcy wpisów. Użyteczne dla wizualizacji na mapach (Deck.gl, Folium, Kepler) bez potrzeby osobnego geocoding API.

Numery NIP są normalizowane do formatu 10 cyfr bez myślników i spacji (kolumna nip_normalized). Oryginalny zapis z BZP często ma formę 123-456-78-90 albo NIP: 1234567890 — do joinów używaj zawsze nip_normalized.

4. Agregacja do profili

Poza surowymi ogłoszeniami wyliczamy dwa zbiory agregatów:

  • buyers.csv / buyers.parquet — po jednym rekordzie na NIP zamawiającego. Liczby: łączna liczba postępowań, łączna wartość, średnia wartość, top 5 kategorii CPV, trendy per rok, top 5 wykonawców wygrywających od tego zamawiającego.
  • contractors.csv / contractors.parquet — po jednym rekordzie na NIP wykonawcy. Analogicznie: liczba wygranych, łączna wartość, sezonowość, top zamawiający.

Agregaty są deterministyczne — regenerowalne tym samym export.py z dowolnego snapshotu bazy. Jeśli potrzebujesz własnego cięcia (np. per region, per kategoria CPV z odrzuceniem dostaw) — tabela tenders jest jedynym źródłem prawdy, reszta się z niej liczy.

5. Anonimizacja wykonawców

Zacznijmy od faktu prawnego: wyniki postępowań publicznych, w tym nazwa i NIP wykonawcy, są z urzędu publiczne. PZP art. 269 nakazuje ich publikację w BZP, a EU Open Data Directive 2019/1024 wprost zachęca do re-use. Atlas w interfejsie pokazuje te dane bez zmian — tak samo jak eGospodarka, Oferent, Bazhub i inne platformy agregujące BZP od lat. Żadna publiczna anonimizacja nie jest wymagana ani po stronie zamawiającego, ani wykonawcy.

Mimo to w publikowanym pakiecie open-data (GitHub, Zenodo, Kaggle) zdecydowaliśmy się dodatkowo zahashować NIP-y wykonawców rozpoznanych jako osoby fizyczne. Dlaczego? Bulk-redistribution 1,4 mln rekordów na licencji CC BY, pobieralnej przez dowolny bot, jest jakościowo inna od wyświetlania danych per zapytanie w wyszukiwarce. To ostrożność prewencyjna, nie wymóg RODO — gdyby kiedyś UODO albo sam wykonawca zgłosił zastrzeżenie co do zbiorczego datasetu, mamy już zaimplementowane rozwiązanie. Detekcję osoby fizycznej robimy trzema regułami, w kolejności pewności:

  1. PESEL (11 cyfr) w polu contractor_national_id — PESEL jest identyfikatorem osoby fizycznej z definicji. Firmy używają NIP-ów 10-cyfrowych. Jeśli pole ma 11 cyfr, to z 100% pewnością osoba fizyczna. W całej bazie jest 4 458 takich rekordów.
  2. Markery „osoba fizyczna", „jednoosobowa działalność", „prowadzący działalność", „CEIDG" w nazwie wykonawcy. 4 260 przypadków w całej bazie — oferent sam deklaruje formę prawną.
  3. Wzorzec „Imię Nazwisko" na końcu nazwy — klasyka CEIDG: "PHUP DELTABUD Krzysztof Łakomiec", "Firma Usługowa Danuta Frymark", "ALU-CAR Gorzyce Krzysztof Drozd". Dwa słowa Title case (albo ALL CAPS), pierwsze z nich musi być rozpoznanym polskim imieniem — mamy listę 253 najpopularniejszych imion męskich i żeńskich. Dlaczego lista imion? Bo bez niej regex łapałby „Roche Diagnostics Polska" (Diagnostics + Polska = dwa słowa title case) jako osobę fizyczną — a to oczywiście firma. Lista polskich imion eliminuje tego typu false positives.

Gdy którykolwiek warunek się spełnia, rekord jest transformowany tak:

PolePrzedPo anonimizacji
contractor_name"Firma Usługowa Danuta Frymark""[Osoba fizyczna]"
contractor_national_id"9876543210""anon-a1b2c3d4e5" (SHA-256 hash z solą)
contractor_city, contractor_provincebez zmianbez zmian — geografia to nie PII

Hash jest stabilny między releasami (ta sama sól = ten sam hash), więc łączenia cross-year po zanonimizowanym identyfikatorze dalej działają. Ale odwrócić hashu bez soli nie da się — a sól nie jest publiczna. W 2024 roku zanonimizowaliśmy 59 472 rekordy przetargów (25% tych z contractor_name); w 2025 — 64 964 rekordy; w agregacie contractors.csv — 33 714 profili wykonawców (41%).

Verifikacja po stronie zamawiającego. Przed publikacją sprawdziliśmy rozkład długości pola buyer_nip: w całej bazie 2,8 mln rekordów — wszystkie mają dokładnie 10 cyfr. Zero PESEL-i, zero osób fizycznych jako zamawiających. To zgadza się z literą PZP — osoba fizyczna nieprowadząca działalności nie może być zamawiającym w sensie ustawy. Gdyby kiedyś pojawił się taki przypadek, rozszerzymy pii_utils.py o analogiczną detekcję strony zamawiającego.

Jak użyć — pierwsze pięć minut

Dwa scenariusze, w zależności od stacku:

Python + pandas

import pandas as pd

# Pobierz parquet prosto z GitHuba (CDN, bez logowania)
url = "https://github.com/atlasprzetargow/polish-tenders-dataset/raw/main/data/tenders_2025.parquet"
df = pd.read_parquet(url)

print(f"{len(df):,} ogłoszeń w 2025")
print(df.groupby("province")["estimated_value"].sum().sort_values(ascending=False).head())

Pakiet pyarrow albo fastparquet musi być zainstalowany. Parquet jest kolumnowy, więc jeśli czytasz tylko kilka kolumn — pandas ściąga tylko to:

cols = ["id", "title", "buyer", "city", "province", "estimated_value"]
df = pd.read_parquet(url, columns=cols)  # pobiera znacznie mniej bajtów

DuckDB — SQL bez instalacji bazy

SELECT province, COUNT(*) AS n, SUM(estimated_value) AS total_pln
FROM 'https://github.com/atlasprzetargow/polish-tenders-dataset/raw/main/data/tenders_2025.parquet'
WHERE notice_type LIKE 'Contract%'
GROUP BY province
ORDER BY total_pln DESC;

DuckDB czyta Parquet bezpośrednio z HTTP — nie musisz niczego pobierać ręcznie, nie musisz mieć bazy. Poza pandas to najszybszy sposób na ad-hoc analizę — kilkadziesiąt milisekund na zapytanie, zero konfiguracji.

Schemat — najważniejsze kolumny

Tabela tenders ma 43 kolumny. Pełny opis jest w schema/tenders.md w repozytorium. Tu tylko najczęściej używane:

KolumnaTypOpis
idstringKlucz główny. Numer BZP (np. 2024/BZP 00123456/01) albo TED (123456-2024).
titlestringTytuł ogłoszenia dokładnie jak opublikowano.
buyer + buyer_nipstringZamawiający: nazwa i numer NIP (10 cyfr).
city + provincestringLokalizacja zamawiającego. Województwo w kodzie NUTS-2 (PL12 itd.).
cpv_codestringKody CPV oddzielone przecinkami. Pierwszy to główny.
notice_typestringTyp ogłoszenia: ContractNotice (otwarcie), TenderResultNotice (wynik) i wariantów w TED.
order_typestringRoboty budowlane / Dostawy / Usługi.
datedateData publikacji.
submitting_offers_datetimestampTermin składania ofert.
estimated_value + currencyfloat, stringWartość szacunkowa albo wygranej oferty. PLN dla BZP, EUR dla TED.
contractor_name + contractor_national_idstringWykonawca (dla ogłoszeń wynikowych). Anonimizowane dla osób fizycznych — patrz sekcja metodologii.
is_duplicateboolOgłoszenie TED duplikujące wpis BZP. Filtruj false do analiz zdedupliokowanych.
sourcestringbzp lub ted.

Przypadki użycia — z sufitu

  1. Ranking najbardziej aktywnych zamawiającychGROUP BY buyer_nip ORDER BY count DESC. W 2025 na szczycie jest zazwyczaj GDDKiA, duże szpitale kliniczne, kilka miast wojewódzkich.
  2. Mediany wartości per kategoria CPV — która branża ma największe kontrakty? Budownictwo drogowe (45233000) vs. aparatura medyczna (33100000) vs. oprogramowanie (72000000).
  3. Detekcja koncentracji rynku — jaki procent kontraktów w danym województwie wygrywa top 10% wykonawców? Klasyczny wskaźnik Herfindahla w analizach konkurencyjnych.
  4. Ranking unieważnień — w procedure_result mamy informacje o zakończeniu postępowania; filtruj „Unieważniono" i policz odsetek per zamawiający. Wysoki odsetek unieważnień bywa sygnałem problemów z planowaniem.
  5. ML: predykcja CPV z tytułu — 1,4 mln par (tytuł, kod CPV) to fajny dataset treningowy dla klasyfikatora. Baseline na fine-tuningu herberta albo prostszym TF-IDF + logistic regression.
  6. Analiza sezonowości — kiedy w roku zamawiający publikują najwięcej? Czwarty kwartał zawsze dominuje — z racji budżetu rocznego.

Cytowanie i licencja

Jeśli używasz datasetu w pracy akademickiej, raporcie albo publikacji — cytuj nas. Plik CITATION.cff w repozytorium zawiera strukturyzowane metadane w formacie Citation File Format. Zenodo, Zotero, Mendeley, Papers, Hayagriva (referencer Typst-a) — wszystkie go czytają automatycznie.

Forma ludzka, którą można wkleić do stopki artykułu:

Atlas Przetargów (2026). Polish Public Tenders Dataset (BZP + TED), wersja 2026.Q2. https://github.com/atlasprzetargow/polish-tenders-dataset

Licencja na dane: CC BY 4.0 — dowolne użycie, w tym komercyjne, redystrybucja, modyfikacja. Wymagane jest tylko przypisanie autorstwa. Licencja na kod: MIT — używaj jak chcesz, bez obowiązku atrybucji.

Co dalej

Dataset w obecnej formie jest minimalnym użytecznym (MVP). Planowane rozszerzenia:

  • Rozszerzenie zasięgu do 2018+ — pełna historia od momentu, kiedy BZP zaczął publikować w formacie OCDS. Kolejny release w tym kwartale.
  • Zenodo + DOI — równoległa publikacja na Zenodo dla stałego identyfikatora cytowania. DOI to standard w cytowaniach naukowych, niezależny od tego, czy repozytorium GitHub kiedykolwiek zniknie.
  • Kaggle — kopia na Kaggle jako zachęta do publikowania notebooków przez społeczność ML. Każdy publiczny notebook na Kaggle to backlink do naszego źródła.
  • Oddzielna kolekcja dokumentów — w planach drugi dataset z SWZ (Specyfikacja Warunków Zamówienia) i załącznikami, tam gdzie są publicznie dostępne. Oddzielnie, bo rzędu dziesiątek gigabajtów i wymaga Git LFS.

Znalazłeś błąd, zaskakujący rekord, false positive w anonimizacji? Zgłoś issue na GitHubie: github.com/atlasprzetargow/polish-tenders-dataset/issues. Jeśli zbudowałeś coś ciekawego na bazie tych danych (artykuł, notebook, wizualizacja) — napisz do nas na contact@atlasprzetargow.pl, chętnie nagłośnimy. Dostęp do świeżych, surowych danych w czasie rzeczywistym — REST API Atlas lub MCP Server dla Claude Desktop.

Zasoby powiązane

  • Repozytorium GitHub: github.com/atlasprzetargow/polish-tenders-dataset
  • Release v2026.Q2 (CSV gzipped): releases/tag/v2026.Q2
  • Dokumentacja schema: schema/*.md
  • Kod anonimizacji: pii_utils.py
  • MCP Server dla AI: premiera MCP Server
  • REST API Atlas: atlasprzetargow.pl/api
  • Słownik zamówień publicznych: atlasprzetargow.pl/slownik

Potrzebujesz głębszej analizy?

Atlas Przetargów to nie tylko blog. To narzędzie, które daje Ci przewagę informacyjną nad konkurencją.

Sprawdź demo narzędzia