# Romanian public procurement (SEAP/SICAP) > Romanian public procurement as it is published day by day in SEAP/SICAP: the finalised direct awards, the contract notices that open a tender and the award notices that close one. One fact row is one award or one notice, with the contracting authority and the supplier split into a fiscal code and a name, the CPV code split into code, label and division, and every amount kept as an exact decimal. The source caps any query at 2000 rows, so a day is split recursively by CPV category, contract type and acquisition state until each piece fits — and the coverage table records exactly what was reached and what was not. - Dataset id: `sicap-procurement` - Update cadence: daily - Upstream source: https://e-licitatie.ro/pub - Tags: romania, procurement, public-spending, contracts, tenders, government, transparency, anap, sicap, seap, cpv - Current version: ingest `01M1NPXYGK52W3FF5HG9V5BVPR`, snapshot 2026-09-04, generated 2026-09-04T08:03:23.388Z ## Licence and attribution - Licence: Romanian official acts and public-interest information — outside copyright (`ro-official-acts-public-domain`) - Licence text: https://legislatie.just.ro/Public/DetaliiDocument/7816 - Required attribution: Date privind achizițiile publice publicate în SEAP/SICAP de ANAP — e-licitatie.ro - Notes: Legea 8/1996 privind dreptul de autor, art. 9, excludes from copyright both official texts of an administrative nature and simple facts and data. A procurement notice is both: Legea 98/2016 and Legea 99/2016 REQUIRE contracting authorities to publish these notices in SEAP/SICAP, so the publication is an official act, and the contents — a code, a value, a date, the name of the winner — are facts. They are also public-interest information under Legea 544/2001 and re-usable public-sector information under Legea 179/2022, which transposes the EU Open Data Directive. Note honestly that 179/2022 grants a RIGHT of reuse rather than a copyright licence, and names no attribution form — so the attribution line above is this project’s own convention, not a form the law prescribes. HOWEVER, and a reuser must weigh this: ANAP/SICAP publishes NO dataset-specific licence. https://e-licitatie.ro/robots.txt returns 404, so nothing is disallowed to a crawler, and the site’s terms page is a client-rendered shell with no retrievable text — there is no grant to point at and no prohibition to point at either. This dataset therefore rests on the legal position above rather than on any permission from ANAP, and anyone republishing it should make their own assessment. About the source: the API is UNDOCUMENTED and reverse-engineered from the SICAP web client. It has no version header and no changelog, and field names can change without notice — which is why every field path in this connector is hard-coded, so a change breaks loudly instead of filling a column with NULL. What is published here: the notice and award metadata itself, re-typed into a canonical schema. What is deliberately NOT republished: the tender documentation, the attachments and the PDF renditions SICAP hosts alongside each procedure. One completeness caveat belongs in any reuse: the source truncates every query at 2000 rows, and the coverage table is where this dataset admits which slices hit that ceiling. Do not quote a daily total without checking it. Reproduce the attribution line verbatim anywhere you republish this data. ## Manifest - Start here for how this directory works: https://data.bunnytech.app/llms.txt - This dataset: https://data-directory.fsn1.your-objectstorage.com/public/sicap-procurement/manifest.json - All datasets: https://data-directory.fsn1.your-objectstorage.com/public/index.json - Human page: https://data.bunnytech.app/datasets/sicap-procurement The manifest is the contract: file URLs, row counts, column types, min/max statistics and example queries all live there. Re-read it to discover a new version. ## Fastest path — Parquet exports Immutable, physically sorted snapshot exports. No catalog fetch, best row-group pruning, safe to cache forever. Register one view per table: ```sql INSTALL httpfs; LOAD httpfs; CREATE OR REPLACE VIEW direct_acquisitions AS SELECT * FROM read_parquet('https://data-directory.fsn1.your-objectstorage.com/public/sicap-procurement/v/01M1NPXYGK52W3FF5HG9V5BVPR/direct_acquisitions.parquet'); CREATE OR REPLACE VIEW contract_award_notices AS SELECT * FROM read_parquet('https://data-directory.fsn1.your-objectstorage.com/public/sicap-procurement/v/01M1NPXYGK52W3FF5HG9V5BVPR/contract_award_notices.parquet'); CREATE OR REPLACE VIEW contract_notices AS SELECT * FROM read_parquet('https://data-directory.fsn1.your-objectstorage.com/public/sicap-procurement/v/01M1NPXYGK52W3FF5HG9V5BVPR/contract_notices.parquet'); CREATE OR REPLACE VIEW parties AS SELECT * FROM read_parquet('https://data-directory.fsn1.your-objectstorage.com/public/sicap-procurement/v/01M1NPXYGK52W3FF5HG9V5BVPR/parties.parquet'); CREATE OR REPLACE VIEW cpv_codes AS SELECT * FROM read_parquet('https://data-directory.fsn1.your-objectstorage.com/public/sicap-procurement/v/01M1NPXYGK52W3FF5HG9V5BVPR/cpv_codes.parquet'); CREATE OR REPLACE VIEW cpv_categories AS SELECT * FROM read_parquet('https://data-directory.fsn1.your-objectstorage.com/public/sicap-procurement/v/01M1NPXYGK52W3FF5HG9V5BVPR/cpv_categories.parquet'); CREATE OR REPLACE VIEW coverage AS SELECT * FROM read_parquet('https://data-directory.fsn1.your-objectstorage.com/public/sicap-procurement/v/01M1NPXYGK52W3FF5HG9V5BVPR/coverage.parquet'); ``` Example queries: -- The month’s direct awards, biggest first: The date range is what prunes: finalization_date leads the sort order, so this reads only the row groups that overlap the month. ```sql ATTACH IF NOT EXISTS 'ducklake:https://data-directory.fsn1.your-objectstorage.com/catalog/catalog.ducklake' AS "datadirectory" (READ_ONLY); SELECT finalization_date, name, supplier_name, authority_name, closing_value_ron FROM datadirectory.sicap_procurement.direct_acquisitions WHERE finalization_date BETWEEN DATE '2026-09-01' AND DATE '2026-09-30' ORDER BY closing_value_ron DESC LIMIT 25 ``` -- Who sold the most to the Romanian state in a month: The join to parties is what makes this a company-level answer instead of a text aggregation over inconsistently spelled names. ```sql ATTACH IF NOT EXISTS 'ducklake:https://data-directory.fsn1.your-objectstorage.com/catalog/catalog.ducklake' AS "datadirectory" (READ_ONLY); SELECT p.cui, p.name, count(*) AS awards, sum(d.closing_value_ron) AS total_ron FROM datadirectory.sicap_procurement.direct_acquisitions d JOIN datadirectory.sicap_procurement.parties p ON p.cui = d.supplier_cui WHERE d.finalization_date BETWEEN DATE '2026-09-01' AND DATE '2026-09-30' GROUP BY 1, 2 ORDER BY total_ron DESC LIMIT 20 ``` ## Live lake — DuckLake catalog with time travel Attach the published catalog read-only over plain https. This works in native DuckDB AND in duckdb-wasm in the browser — the reader catalog resolves its data files through public https URLs, so no credentials and no backend are involved. ```sql INSTALL ducklake; LOAD ducklake; ATTACH 'ducklake:https://data-directory.fsn1.your-objectstorage.com/catalog/sicap-procurement/catalog.ducklake' AS sicap_procurement (READ_ONLY); SELECT * FROM sicap_procurement.main.direct_acquisitions LIMIT 5; USE sicap_procurement; ``` - Latest catalog: https://data-directory.fsn1.your-objectstorage.com/catalog/sicap-procurement/catalog.ducklake - This exact version: https://data-directory.fsn1.your-objectstorage.com/catalog/sicap-procurement/v/01M1NPXYGK52W3FF5HG9V5BVPR/catalog.ducklake (sha256 14a349ccdf89dcc152166936937cb25e3825ee8a0250bb0e7e61cdf6213d6bd4) - Written by DuckDB v1.5.5 - Time travel: this ingest is snapshot 9 — e.g. `SELECT * FROM sicap_procurement.direct_acquisitions AT (VERSION => 9)` ## Wholesale — S3 access For bulk downloads, read the bucket prefix directly over S3 with httpfs: ```sql INSTALL httpfs; LOAD httpfs; CREATE SECRET hetzner ( TYPE s3, ENDPOINT 'fsn1.your-objectstorage.com', URL_STYLE 'path', KEY_ID '', SECRET '' ); SELECT * FROM read_parquet('s3://data-directory/public/sicap-procurement/v/01M1NPXYGK52W3FF5HG9V5BVPR/**/*.parquet'); ``` ## Schema ### direct_acquisitions One row per finalised direct award (achiziție directă) — the low-value purchases Romanian public bodies may make without running a tender, published in SICAP as they are concluded. This is the highest-volume table in the dataset: an ordinary weekday carries eight to nine thousand awards. The row is the award as SICAP published it: who bought, who sold, what CPV code it was filed under, the estimated value and the value it closed at. - 8,516 rows · 749 KB · write mode `snapshot_history` - Physically sorted by: `finalization_date`, `cpv_division`, `direct_acquisition_id` - Primary key: `direct_acquisition_id` - Text search columns: `name`, `supplier_name`, `authority_name` - Files: 1 - https://data-directory.fsn1.your-objectstorage.com/public/sicap-procurement/v/01M1NPXYGK52W3FF5HG9V5BVPR/direct_acquisitions.parquet (8,516 rows) Columns: - `direct_acquisition_id` BIGINT NOT NULL — SICAP's own internal id for the award. Stable, and the primary key of this table. (range: "121920285" … "122930204") (~7540 distinct) - `unique_identification_code` VARCHAR — The public "DA…" code SICAP shows on the award screen. This is the code a human quotes; direct_acquisition_id is the one the API uses. - `finalization_date` DATE NOT NULL — The calendar day the award was finalised, exactly as the source writes it (Europe/Bucharest, the zone every SICAP timestamp carries). This is the snapshot key: re-running a day replaces precisely that day, which is what makes corrections and backfills idempotent. It is also the field the crawl filters on, so a row whose day disagrees with the day it was fetched for is skipped rather than published. (range: "2026-09-03" … "2026-09-03") (~1 distinct) - `finalization_at` TIMESTAMPTZ — The full instant the award was finalised. TIMESTAMPTZ, so it is an absolute point in time: SICAP states it in Europe/Bucharest local time with the offset attached (+03:00 in summer, +02:00 in winter), and nothing is converted or guessed here. - `publication_at` TIMESTAMPTZ — When the award was first published in SICAP, as an absolute instant — stated by the source in Europe/Bucharest local time with its offset attached. Routinely earlier than finalization_at, and often on the previous day, which is why this connector slices on the finalization date and not on this one. - `ca_decision_deadline_at` TIMESTAMPTZ — Deadline by which the contracting authority must decide on the offer, as an absolute instant — stated by the source in Europe/Bucharest local time with its offset attached. - `supplier_decision_deadline_at` TIMESTAMPTZ — Deadline by which the supplier must respond, as an absolute instant — stated by the source in Europe/Bucharest local time with its offset attached. - `name` TEXT NOT NULL — What was bought, in the contracting authority’s own words. - `name_lc` VARCHAR NOT NULL — name, lowercased and accent-folded (NFD, combining marks stripped), so a case- and diacritic-insensitive search is a plain string comparison rather than a function of the column — which would defeat row-group statistics entirely. - `cpv_code` VARCHAR — The CPV code split off the combined string the API sends, including its check digit. Joins to cpv_codes.cpv_code. - `cpv_label` TEXT — The CPV label, the remainder of the same combined string. - `cpv_division` VARCHAR — The first two digits of the CPV code — the CPV division, i.e. the broad sector. Split out as its own column and placed second in the sort order so "everything in division 45 (construction works)" is a range predicate that prunes row groups, instead of a LIKE over a free-text field that cannot prune at all. (range: "03" … "98") - `state_id` INTEGER — sysDirectAcquisitionState.id — the award’s lifecycle state. Also one of the axes the crawl splits a truncated day along; see the coverage table. - `state_label` VARCHAR — The state’s Romanian label as SICAP writes it. - `supplier_cui` VARCHAR — Fiscal identification code (CUI/CIF) of the supplier, taken from the leading token of the supplier string. Stored as TEXT and never as a number: leading zeros occur and are meaningful, and the source writes the fiscal prefix on some rows and not on others — the current VAT form ("RO15151621"), the pre-2007 form ("R1890420") and the same with a space ("R 10625635") all occur. The prefix is stripped so every spelling of one company lands on one code. NULL when the string carries no recognisable Romanian code at all, which is also where a foreign VAT number ("SE559406327201") ends up on purpose. - `supplier_name` TEXT — The supplier’s name, the remainder of the supplier string. - `supplier_name_lc` VARCHAR — supplier_name, lowercased and accent-folded. - `authority_cui` VARCHAR — Fiscal identification code of the contracting authority, parsed exactly like supplier_cui. Joins to parties.cui. - `authority_name` TEXT — The contracting authority’s name. - `authority_name_lc` VARCHAR — authority_name, lowercased and accent-folded. - `estimated_value_ron` DECIMAL(18,2) [RON] — The value the authority estimated, in Romanian lei. Exact decimal, never a float. - `estimated_value_other_currency` DECIMAL(18,2) — The same estimate in the other currency SICAP displays alongside it — in practice euro, converted at the rate of the day. The source does not name the currency in the payload, so this column does not claim one. - `closing_value_ron` DECIMAL(18,2) [RON] — The value the award actually closed at, in Romanian lei. Equal to the estimate on most rows, because a direct award is usually accepted at the offered price. - `is_open_for_correction` BOOLEAN — Whether the award was still open for correction when the row was captured. The source sends null far more often than true or false. - `is_open_for_contract_correction` BOOLEAN — Whether the resulting contract was still open for correction when the row was captured. - `snapshot_date` DATE NOT NULL — The ingest run’s own snapshot day (UTC) — when this copy was taken, as opposed to finalization_date, which is when the award happened. - `ingested_at` TIMESTAMP NOT NULL — When this row was produced by the ingest run (UTC). ### contract_award_notices One row per contract award notice (anunț de atribuire) published in SICAP — the notice a contracting authority publishes once a tender has been awarded, stating who won and for how much. These are the large contracts: values in the hundreds of millions of lei are ordinary here, where direct_acquisitions is capped by law at the direct-award threshold. One row is one notice as it stood when the run captured it, keyed on the notice id. - 258 rows · 49 KB · write mode `snapshot_history` - Physically sorted by: `publication_date`, `cpv_division`, `ca_notice_id` - Primary key: `ca_notice_id` - Text search columns: `contract_title`, `authority_name` - Files: 1 - https://data-directory.fsn1.your-objectstorage.com/public/sicap-procurement/v/01M1NPXYGK52W3FF5HG9V5BVPR/contract_award_notices.parquet (258 rows) Columns: - `ca_notice_id` BIGINT NOT NULL — SICAP’s internal id for the award notice. Primary key. (range: "100609884" … "100653209") (~221 distinct) - `notice_id` BIGINT — The id of the underlying notice document, shared with the versions and errata of the same notice. - `procedure_id` BIGINT — The id of the procurement procedure this notice belongs to. Joins an award notice to the contract notice that opened the same procedure. - `notice_no` VARCHAR — The public notice number a human quotes ("CAN…", "SCNA…"). - `publication_date` DATE NOT NULL — The Europe/Bucharest calendar day the notice was published — the source’s own day, not a UTC day. It comes from the crawl window rather than from the payload, because the list endpoint filters on publication date but does not return it, and that filter is what defines which day a notice belongs to. This is the snapshot key: re-running a day replaces exactly that day. (range: "2026-09-03" … "2026-09-03") (~1 distinct) - `notice_type_id` INTEGER — sysNoticeTypeId — which kind of award notice this is. The crawl asks for types 3, 13, 18, 16 and 8, the same set the SICAP web client requests for its award-notice screen. - `notice_state_id` INTEGER — sysNoticeState.id — the notice’s own publication state. - `notice_state_label` VARCHAR — The notice state’s Romanian label. - `procedure_state_id` INTEGER — sysProcedureState.id — the state of the procedure behind the notice. - `procedure_state_label` VARCHAR — The procedure state’s Romanian label. - `authority_cui` VARCHAR — Fiscal code (CUI/CIF) of the contracting authority, split off the combined "CUI - name" string. The fiscal prefix is stripped, so "RO1590120", "R1590120" and "1590120" are one code. Joins to parties.cui. NULL when the string carries no recognisable code. - `authority_name` TEXT — The contracting authority’s name. - `authority_name_lc` VARCHAR — authority_name, lowercased and accent-folded. - `contract_title` TEXT — The contract title, in the authority’s own words. - `contract_title_lc` VARCHAR — contract_title, lowercased and accent-folded. - `contract_type_id` INTEGER — sysAcquisitionContractType.id: 1 Furnizare (supplies), 2 Servicii (services), 3 Lucrări (works). - `contract_type_label` VARCHAR — The contract type’s Romanian label. - `procedure_type_id` INTEGER — sysProcedureType.id — which procurement procedure was run (open tender, restricted, negotiated, …). - `procedure_type_label` VARCHAR — The procedure type’s Romanian label. - `contract_assignment_type_id` INTEGER — sysContractAssigmentType.id — whether the award is a plain public contract, a framework agreement, and so on. The misspelling is the source’s; the column name is not. - `contract_assignment_type_label` VARCHAR — The assignment type’s Romanian label. - `cpv_code` VARCHAR — The CPV code split off cpvCodeAndName. Joins to cpv_codes.cpv_code. - `cpv_label` TEXT — The CPV label, the remainder of the same string. - `cpv_division` VARCHAR — The first two digits of the CPV code — the sector. Second in the sort order so a sector cut inside a day is a contiguous range rather than a scan. (range: "03" … "98") - `ron_contract_value` DECIMAL(18,2) [RON] — The awarded contract value in Romanian lei. DECIMAL(18,2) rather than the platform’s usual DECIMAL(12,4) because a single day of this table carried 2,149,454,481.75 RON, which does not fit in twelve digits. - `highest_offer_value` DECIMAL(18,2) [RON] — The highest offer received for the contract. 0 when the source published none. - `lowest_offer_value` DECIMAL(18,2) [RON] — The lowest offer received for the contract. 0 when the source published none. - `currency_code` VARCHAR — The currency the notice states its values in; RON in practice. - `estimated_value_export` VARCHAR — The value as SICAP renders it for display, amount and currency in one string ("366296 RON"). Kept verbatim because it is what a reader sees on the site; use ron_contract_value for arithmetic. - `is_online` BOOLEAN — Whether the procedure was run online. - `is_utility` BOOLEAN — Whether the authority is a utility (sectorial) contracting entity under Legea 99/2016 rather than a classic one under Legea 98/2016. - `is_ppp` BOOLEAN — Whether the award is a public-private partnership. - `has_subsequent_contracts` BOOLEAN — Whether the award carries subsequent contracts — the mark of a framework agreement being drawn down. - `notice_state_at` TIMESTAMPTZ — When the notice last entered its current state, as an absolute instant — stated by the source in Europe/Bucharest local time with its offset attached. The closest thing the payload carries to a publication instant. - `errata_no` INTEGER — How many errata have been published against this notice; 0 for none. - `notice_version_id` INTEGER — sysNoticeVersionId — which version of the notice this row describes. - `version_no` VARCHAR — versionNo as the source sends it, coerced to text. Every row observed so far carries null; the column is kept so a value the source starts sending is captured rather than discarded. - `min_tender_receipt_deadline_at` TIMESTAMPTZ — Earliest tender receipt deadline of the procedure, as an absolute instant — stated by the source in Europe/Bucharest local time with its offset attached. Usually null on an award notice, because the deadline has long passed by the time the award is published. - `max_tender_receipt_deadline_at` TIMESTAMPTZ — Latest tender receipt deadline, as an absolute instant — stated by the source in Europe/Bucharest local time with its offset attached. - `tender_receipt_deadline_export` VARCHAR — The tender deadline as SICAP renders it for display ("08.10.2026 15:00"), day-first and in Bucharest local time. Kept verbatim; use the timestamptz columns for arithmetic. - `snapshot_date` DATE NOT NULL — The ingest run’s own snapshot day (UTC). - `ingested_at` TIMESTAMP NOT NULL — When this row was produced by the ingest run (UTC). ### contract_notices One row per contract notice (anunț de participare) published in SICAP — the notice that opens a tender and invites offers. Where contract_award_notices records the end of a procedure, this records its start, with the estimated value and the deadline for offers. Join the two on procedure_id to follow one procedure from invitation to award. - 105 rows · 29 KB · write mode `snapshot_history` - Physically sorted by: `publication_date`, `cpv_division`, `c_notice_id` - Primary key: `c_notice_id` - Text search columns: `contract_title`, `authority_name` - Files: 1 - https://data-directory.fsn1.your-objectstorage.com/public/sicap-procurement/v/01M1NPXYGK52W3FF5HG9V5BVPR/contract_notices.parquet (105 rows) Columns: - `c_notice_id` BIGINT NOT NULL — SICAP’s internal id for the contract notice. Primary key. (range: "100029144" … "100244354") (~120 distinct) - `notice_id` BIGINT — The id of the underlying notice document. - `procedure_id` BIGINT — The id of the procurement procedure. This is the join key to contract_award_notices.procedure_id — the same procedure seen from the other end. - `notice_no` VARCHAR — The public notice number a human quotes ("CN…"). - `publication_date` DATE NOT NULL — The Europe/Bucharest calendar day the notice was published — the source’s own day, not a UTC day — taken from the crawl window rather than from the payload, because the list endpoint filters on publication date but does not return it. The snapshot key. (range: "2026-09-03" … "2026-09-03") (~1 distinct) - `notice_type_id` INTEGER — sysNoticeTypeId — which kind of contract notice this is. - `notice_state_id` INTEGER — sysNoticeState.id — the notice’s publication state. - `notice_state_label` VARCHAR — The notice state’s Romanian label. - `procedure_state_id` INTEGER — sysProcedureState.id — the state of the procedure. - `procedure_state_label` VARCHAR — The procedure state’s Romanian label. - `authority_cui` VARCHAR — Fiscal code (CUI/CIF) of the contracting authority, split off the combined "CUI - name" string with any fiscal prefix ("RO", "R") stripped. Joins to parties.cui. - `authority_name` TEXT — The contracting authority’s name. - `authority_name_lc` VARCHAR — authority_name, lowercased and accent-folded. - `contract_title` TEXT — The contract title. SICAP allows newlines inside it and they are preserved verbatim. - `contract_title_lc` VARCHAR — contract_title, lowercased and accent-folded. - `contract_type_id` INTEGER — sysAcquisitionContractType.id: 1 Furnizare, 2 Servicii, 3 Lucrări. - `contract_type_label` VARCHAR — The contract type’s Romanian label. - `procedure_type_id` INTEGER — sysProcedureType.id — which procurement procedure is being run. - `procedure_type_label` VARCHAR — The procedure type’s Romanian label. - `contract_assignment_type_id` INTEGER — sysContractAssigmentType.id — public contract, framework agreement, and so on. - `contract_assignment_type_label` VARCHAR — The assignment type’s Romanian label. - `cpv_code` VARCHAR — The CPV code split off cpvCodeAndName. Joins to cpv_codes.cpv_code. - `cpv_label` TEXT — The CPV label, the remainder of the same string. - `cpv_division` VARCHAR — The first two digits of the CPV code — the sector, and the second sort column. (range: "03" … "90") - `estimated_value_ron` DECIMAL(18,2) [RON] — The estimated value of the contract in Romanian lei, as stated in the notice. DECIMAL(18,2) for the same reason as in contract_award_notices: national-scale tenders do not fit in twelve digits. - `estimated_value_export` VARCHAR — The estimated value as SICAP renders it for display, amount and currency in one string. - `is_online` BOOLEAN — Whether the procedure is being run online. - `has_lots` BOOLEAN — Whether the tender is divided into lots. - `has_appeal` BOOLEAN — Whether an appeal (contestație) has been lodged against the procedure. - `notice_state_at` TIMESTAMPTZ — When the notice last entered its current state, as an absolute instant — stated by the source in Europe/Bucharest local time with its offset attached. - `errata_no` INTEGER — How many errata have been published against this notice; 0 for none. - `notice_version_id` INTEGER — sysNoticeVersionId — which version of the notice this row is. - `min_tender_receipt_deadline_at` TIMESTAMPTZ — Earliest instant an offer may be received — stated by the source in Europe/Bucharest local time with its offset attached. This is the deadline that matters on a live tender. - `max_tender_receipt_deadline_at` TIMESTAMPTZ — Latest instant an offer may be received — stated by the source in Europe/Bucharest local time with its offset attached. - `tender_receipt_deadline_export` VARCHAR — The tender deadline as SICAP renders it for display ("08.10.2026 15:00"), day-first and in Bucharest local time. - `sad_id` BIGINT — sadId — the dynamic purchasing system (sistem de achiziție dinamic) the notice belongs to, when it belongs to one. Null on an ordinary tender. - `snapshot_date` DATE NOT NULL — The ingest run’s own snapshot day (UTC). - `ingested_at` TIMESTAMP NOT NULL — When this row was produced by the ingest run (UTC). ### parties One row per fiscal code (CUI/CIF) this dataset has ever seen, whether as a supplier winning direct awards or as a contracting authority publishing them. This is the company bridge: it is what turns "who sold to the Romanian state" into a join rather than a text search, and what connects this dataset to any other keyed on a Romanian CUI. A code that appears in both roles gets one row with both flags set. first_seen_date and last_seen_date are maintained by the framework across runs, so they describe the whole history rather than the last run. - 7,599 rows · 176 KB · write mode `upsert` - Physically sorted by: `name_lc` - Primary key: `cui` - Text search columns: `name` - Files: 1 - https://data-directory.fsn1.your-objectstorage.com/public/sicap-procurement/v/01M1NPXYGK52W3FF5HG9V5BVPR/parties.parquet (7,599 rows) Columns: - `cui` VARCHAR NOT NULL — The fiscal identification code, digits only. TEXT rather than a number because leading zeros occur and are meaningful, and because the source writes the fiscal prefix on some rows and not on others — "RO15151621", "R1890420" and "R 10625635" all occur alongside the bare digits. The prefix is stripped here so every spelling of one company is one row. A party string carrying a FOREIGN VAT number instead is deliberately not given a row: it is not a Romanian fiscal code, and forcing one would corrupt the join key. (~8266 distinct) - `name` TEXT NOT NULL — The party’s name. SICAP spells the same company several ways across rows; when a run sees more than one spelling for a code, the LONGEST is kept, on the reasoning that the longer string is the one that was not truncated. Later runs overwrite it the same way. - `name_lc` VARCHAR NOT NULL — name, lowercased and accent-folded. This is the sort column: the dimension grows past a single row group, so a prefix search ("name_lc >= 'alpha' AND name_lc < 'alphb'") is a range predicate that prunes, where lower(name) would be a function of the column and prune nothing. (range: "\"climax\"" … "”centrul cultural municipal ramnicu valcea”") - `is_supplier` BOOLEAN — True if this code has ever appeared as the supplier on a direct award. - `is_authority` BOOLEAN — True if this code has ever appeared as the contracting authority on an award or a notice. - `first_seen_date` DATE — The earliest crawled day this code was seen on, as a Europe/Bucharest calendar day — the same day the fact tables are keyed on, not a UTC day. Maintained by the framework: an upsert takes the LEAST of the stored and the incoming value, so it only ever moves backwards. - `last_seen_date` DATE — The latest crawled day this code was seen on, as a Europe/Bucharest calendar day. Maintained by the framework as the GREATEST of the stored and the incoming value. A party that stops appearing keeps the date it was last seen rather than being deleted. ### cpv_codes The Common Procurement Vocabulary dictionary as SICAP itself serves it — 9,455 codes with their Romanian labels. This is the lookup table for cpv_code on every fact table, and the way to turn a division number into a sector a human recognises. Reloaded whole on every run, because it is small reference data with no history worth keeping. - 9,455 rows · 212 KB · write mode `replace` - Physically sorted by: `cpv_code` - Primary key: `cpv_id` - Text search columns: `label` - Files: 1 - https://data-directory.fsn1.your-objectstorage.com/public/sicap-procurement/v/01M1NPXYGK52W3FF5HG9V5BVPR/cpv_codes.parquet (9,455 rows) Columns: - `cpv_id` INTEGER NOT NULL — SICAP’s own internal id for the code. Stable, and the primary key — the CPV code itself is the join key, but the id is what the API addresses. (~7464 distinct) - `cpv_code` VARCHAR NOT NULL — The CPV code including its check digit, exactly as the EU vocabulary writes it. Joins to cpv_code on every fact table. (range: "00000000" … "98910000-5") - `label` TEXT — The code’s Romanian label, including the vocabulary revision marker. - `label_lc` VARCHAR — label, lowercased and accent-folded, for case-insensitive lookup. - `division` VARCHAR — The first two digits of the code — the CPV division, i.e. the broad sector. The same value the fact tables carry as cpv_division. ### cpv_categories The twelve broad sectors SICAP groups CPV codes into on its own search screens ("AGRICULTURA SI ALIMENTATIE", "CONSTRUCTII", …). They are not part of the EU vocabulary — they are SICAP’s own grouping — and they matter here for a second reason: they are the first axis this connector splits a truncated day along, so coverage.cpv_category_id points at this table. - 12 rows · 1.1 KB · write mode `replace` - Physically sorted by: `category_id` - Primary key: `category_id` - Files: 1 - https://data-directory.fsn1.your-objectstorage.com/public/sicap-procurement/v/01M1NPXYGK52W3FF5HG9V5BVPR/cpv_categories.parquet (12 rows) Columns: - `category_id` INTEGER NOT NULL — SICAP’s id for the sector, 1 to 12. Primary key. (range: 1 … 12) (~13 distinct) - `label` VARCHAR NOT NULL — The sector name exactly as SICAP writes it — upper case and without diacritics, which is the source’s own spelling and is preserved rather than tidied. - `label_lc` VARCHAR NOT NULL — label, lowercased and accent-folded. ### coverage One row per crawl slice whose rows were actually fetched — the audit trail of the API’s 2000-row cap. Every SICAP list endpoint truncates a result set at 2000 rows and flags it as searchTooLong, so this connector splits a day by CPV category, then by contract type, then by acquisition state until each piece fits. This table records the pieces: what was asked for, what the server said the total was, how many rows came back, and whether the piece was STILL truncated after all three axes were exhausted. A run with no truncated = true row covered its days completely; one with such a row did not, and says exactly where. Only fetched slices are recorded — an intermediate probe that was split rather than fetched is a log line, not a row, so the totals here sum to the rows published. The second thing this table exists for is the shortfall column: a slice that quietly handed back fewer rows than the server said it had is the most dangerous outcome available to a paginated crawl, and it is published here as a number rather than left in a log nobody reads. - 26 rows · 2.3 KB · write mode `snapshot_history` - Physically sorted by: `slice_date`, `endpoint`, `cpv_category_id` - Primary key: `slice_date`, `endpoint`, `cpv_category_id`, `contract_type_id`, `state_id` - Files: 1 - https://data-directory.fsn1.your-objectstorage.com/public/sicap-procurement/v/01M1NPXYGK52W3FF5HG9V5BVPR/coverage.parquet (26 rows) Columns: - `slice_date` DATE NOT NULL — The Europe/Bucharest calendar day the slice covers — the finalization day for direct acquisitions, the publication day for notices, in the source’s own zone rather than in UTC. On the "cpv_codes" row, which covers no day at all, it is the last day of the run’s crawl window, so that re-running that day replaces that record too. The snapshot key: re-running a day replaces its whole coverage record. (range: "2026-09-03" … "2026-09-03") (~1 distinct) - `endpoint` VARCHAR NOT NULL — Which read the slice belongs to: "direct_acquisitions", "contract_award_notices" or "contract_notices" for the three day-sliced list endpoints, and "cpv_codes" for the one row per run that records the CPV dictionary read. The dictionary is not a day slice, but it is paginated and it is a replace table, so a short read would shrink the dimension silently — it is held to the same accounting as everything else. (range: "contract_award_notices" … "direct_acquisitions") (~3 distinct) - `cpv_category_id` INTEGER NOT NULL — The CPV category the slice was narrowed to, joining to cpv_categories.category_id. 0 is a sentinel meaning "not split on this axis" — it is not a category id, and it is used rather than NULL because this column is part of the primary key. (range: 0 … 12) (~14 distinct) - `contract_type_id` INTEGER NOT NULL — The contract type the slice was narrowed to: 1 Furnizare, 2 Servicii, 3 Lucrări. 0 means "not split on this axis". (~4 distinct) - `state_id` INTEGER NOT NULL — The acquisition state the slice was narrowed to (sysDirectAcquisitionStateId). 0 means "not split on this axis", and it is always 0 for the two notice endpoints, which have no state axis. (~10 distinct) - `reported_total` INTEGER — The total the server reported for the slice. Trustworthy only when truncated is false; when it is true this is exactly 2000, which is the cap rather than a count. - `rows_fetched` INTEGER — How many rows the crawl actually read for the slice, before any were skipped as unusable. Equal to reported_total for a healthy, untruncated slice. - `shortfall` INTEGER — Rows the server said the slice had that never actually arrived — min(reported_total, 2000) minus rows_fetched, floored at zero. 0 on a healthy slice, and it is 0 on a truncated one too: truncation is the cap being reached, which this column deliberately does not confuse with a short read. Anything above zero means a page came back with fewer rows than the server’s own total implied, which is the quiet failure this dataset refuses to hide: it is published as data rather than left in a log line. - `truncated` BOOLEAN NOT NULL — True when the server still answered searchTooLong for this slice after every splitting axis had been used — i.e. rows exist that this run could not reach. This is the flag that makes the cap visible instead of silent; a reader aggregating a day should check it before quoting a total. - `snapshot_date` DATE NOT NULL — The ingest run’s own snapshot day (UTC). - `ingested_at` TIMESTAMP NOT NULL — When this row was produced by the ingest run (UTC). ## Performance notes - There are no indexes in a lakehouse. The physical sort order plus Parquet row-group min/max statistics ARE the index. Predicates on the leading sort columns prune whole row groups; everything else is a full column scan. - `direct_acquisitions` prunes on `finalization_date` → `cpv_division` → `direct_acquisition_id`. - `contract_award_notices` prunes on `publication_date` → `cpv_division` → `ca_notice_id`. - `contract_notices` prunes on `publication_date` → `cpv_division` → `c_notice_id`. - `parties` prunes on `name_lc`. - `cpv_codes` prunes on `cpv_code`. - `cpv_categories` prunes on `category_id`. - `coverage` prunes on `slice_date` → `endpoint` → `cpv_category_id`. - `ILIKE '%needle%'` cannot prune: it decompresses that whole column. Prefer prefix predicates on the lowercased search companion columns where they exist. - Every file URL under `/v//` is immutable. Cache it as aggressively as you like. - Files are never rewritten in place — a new ingest writes a new version path. Re-read the manifest to pick up updates rather than polling the file URLs. - Pull only the columns you need. Parquet is columnar and the reader range-GETs what it reads. ## Do not - Do not scrape https://data.bunnytech.app HTML. Every number on those pages comes from the manifest above. - Do not hit the upstream source (https://e-licitatie.ro/pub) in bulk. This mirror exists so you do not have to. - Do not read DuckLake internal data files with a plain Parquet reader. They come with positional delete files, and ignoring those silently returns deleted rows. Use the ducklake extension, or use the published exports above. - Do not republish without the attribution line, and do not imply endorsement.