# ANAC Italy Public Procurement Scraper - Tenders & Awards (`parseforge/anac-italy-procurement-scraper`) Actor

Italian public tenders (CIG), awards, bidders, subcontracts, contract lifecycle, 48k contracting authorities, SOA certificates and OCDS releases from ANAC open data.

- **URL**: https://apify.com/parseforge/anac-italy-procurement-scraper.md
- **Developed by:** [ParseForge](https://apify.com/parseforge) (community)
- **Categories:** Business, Developer tools, Other
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per event

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

[![ParseForge](https://raw.githubusercontent.com/ParseForge/apify-assets/main/banner.jpg)](https://apify.com/parseforge?fpr=vmoqkp)

### ANAC Italy Public Procurement Scraper - Tenders & Awards

**Scrape the whole ANAC open-data portal — every Italian public tender, award, bidder and contract event since 2007.** 31 tables behind one input: tenders (CIG), SmartCIG, awards, winners, losing bidders, subcontracts, the full contract lifecycle, 48,000 contracting authorities, SOA qualification certificates and OCDS 1.1 releases. No login, no API key. Export to CSV, JSON, Excel or XML.

`dati.anticorruzione.it` publishes Italy's entire public-procurement record, and publishes it as downloadable archives with no query API — CKAN's datastore extension is not installed, so `datastore_search` answers *"Nome di azione non nota"*. Getting one region's works contracts out of it means downloading a whole month's ZIP and parsing 61 columns of `SCREAMING_SNAKE` mixed with `snake_case`. This Actor reads the archives straight off the wire, filters while they stream, and stops the transfer the moment your row cap is met — 20,000 tenders came back in 13 seconds having pulled 3.7 MB of a 22.7 MB archive.

| Who uses it | What they pull from ANAC |
|---|---|
| Procurement intelligence & bid platforms | Live tender feed with CPV, value, buyer and deadline — the input to a bid-alert product |
| Anti-corruption researchers & journalists | Single-bid awards, zero-discount awards, contract variations and subcontract chains |
| B2B sales & lead generation | 48,000 contracting authorities with fiscal codes and addresses; which firms win what, from whom |
| Credit risk & supplier due diligence | A company's full award history, its subcontracting network and its SOA qualification classes |
| Construction & works contractors | Works tenders by SOA category and amount class, filtered by region and CPV |
| Public-spending analysts & academics | PNRR-funded contracts, funding-source breakdowns, progress instalments and cost overruns |
| Open-contracting toolchains | OCDS 1.1 releases, ready to join against other OCDS publishers with no mapping work |

### What it does

Pick one or more **collections**; the Actor returns one row per record, typed, with English aliases on top of ANAC's own Italian column names. Every row carries `collection`, `sourceDataset`, `sourceUrl` and `period`, so a multi-collection run stays separable.

- 📄 **Tenders (CIG)** — 84 fields per lot: subject, value, procedure, CPV, buyer, cost centre, region, publication and outcome dates, PNRR flag, urgency grounds, delegation chain. Monthly archives back to **2007**.
- ⚡ **SmartCIG** — the simplified route for low-value contracts, back to 2011. Roughly as many rows again.
- 🏅 **Awards, winners and losing bidders** — the awarded amount, the discount off the base price, how many firms bid, and every firm that did. This is the half most scrapers stop before.
- 🔗 **The full contract lifecycle** — start, end, suspensions, acceptance testing, progress instalments (SAL), variations, subcontracts, economic framework and funding sources. All keyed on `cig`.
- 🏛️ **48,040 contracting authorities and 158,645 cost centres** — AUSA code, fiscal code, VAT, legal nature, address, in-house and state-owned flags.
- 📜 **SOA certificates** — which firms may legally bid for works over EUR 150,000, with the exact categories and classes granted.
- 🌍 **OCDS 1.1 releases** — the same processes in the international standard, streamed out of bulk files up to **3.2 GB** each.
- 🗂️ **The catalogue itself** — all 70 datasets and 1,703 distributions with DCAT-AP\_IT metadata, plus the reference code tables that decode the numeric fields on tender rows.

### Collections

| Collection | What one row is | Coverage |
|---|---|---|
| `tenders` | A tender lot with its CIG, 84 fields | Monthly, 2007 → today |
| `smart-tenders` | A SmartCIG low-value contract | Monthly, 2011 → today |
| `awards` | Award decision: amount, discount, bid counts | Full table + monthly deltas |
| `winners` | A firm that won a CIG | Full table + monthly deltas |
| `participants` | A firm that bid, losers included | Full table + monthly deltas |
| `contract-start` | Signature, enforceability, effective start | Full table + monthly deltas |
| `contract-end` | Completion, early termination, extensions | Full table + monthly deltas |
| `testing` | Acceptance testing, reserves, litigation value | Full table + monthly deltas |
| `suspensions` | Suspension with reason and resumption date | Full table + monthly deltas |
| `progress-reports` | One SAL instalment with amount and slippage | Full table + monthly deltas |
| `variants` | An approved variation and its legal ground | Full table + monthly deltas |
| `subcontracts` | Subject, category, and both firms | Full table + monthly deltas |
| `economic-framework` | Value split into works/services/supplies/design | Full table + monthly deltas |
| `funding-sources` | State, regional, local, EU, private, loans | Full table + monthly deltas |
| `publications` | Register, GURI, OJEU, regional bulletin dates | Full table + monthly deltas |
| `work-types` | Type of work under a CIG | Full table + monthly deltas |
| `work-categories` | SOA category (OG/OS) and amount class | Full table + monthly deltas |
| `dpcm-categories` | DPCM aggregation category and derogation | Full table + monthly deltas |
| `contracting-authorities` | A registered buyer | 48,040 rows |
| `cost-centres` | A purchasing unit inside a buyer | 158,645 rows |
| `cup-links` | CIG → CUP investment-project link | 7,167,369 rows |
| `pnrr-indicators` | Recovery-plan female and youth quotas | 381,833 rows |
| `soa-certificates` | A SOA qualification certificate | 472,916 rows |
| `soa-categories` | A category and class the certificate grants | 1,774,956 rows |
| `ocds-releases` | An OCDS 1.1 contracting process | Monthly, 2018 → today |
| `datasets` | A CKAN dataset with DCAT-AP\_IT metadata | 70 rows |
| `resources` | A distribution with its direct URL and size | 1,703 rows |
| `organizations` | The publishing organisation | 1 row |
| `groups` | A DCAT theme group | 13 rows |
| `tags` | A tag or controlled-vocabulary term | Tags + 6 vocabularies |
| `reference-codes` | A procedure, delivery mode, SmartCIG type or licence | 4 lookup tables |

June 2025 alone holds **123,121 tenders**. A whole year of `tenders` is over a million rows, which is why **Max rows** matters more here than on most Actors.

### What you can do with ANAC data

**🏛️ Build a bid-alert feed.**

`government contracts` runs **18,100 searches a month** in the US at a $5.33 CPC, and `public procurement` 480 at $15.35 — this is a market that pays for leads. Set `collections: ["tenders"]`, the current year and month, a CPV prefix and a minimum amount, and you have every matching Italian lot with its buyer, its value and its offer deadline.

**🚩 Find the single-bid awards.**

The `awards` collection carries `bidsReceived` and a derived `singleBid` flag, and **Only single-bid awards** turns it into a filter. One bidder on an open procedure is the first red flag in every procurement-integrity methodology there is; `procurement red flags` and `open contracting` are both live search terms. Cross it with `discountPct: 0` and you have the classic pair.

**🔍 Assemble one contract end to end.**

Put a CIG in **CIG codes**, select `awards`, `winners`, `participants`, `contract-start`, `progress-reports`, `variants` and `subcontracts`, and you get the whole life of that contract in one dataset — who bid, who won, at what discount, when work started, every instalment paid, every variation approved and every firm subcontracted. No other ANAC Actor on the Store joins those tables.

**🏢 Profile a supplier.**

Put a company's *partita IVA* in **Fiscal codes** and run `winners`, `participants` and `subcontracts`: every contract it won, every one it bid for and lost, and every job it took as a subcontractor. Add `soa-certificates` and `soa-categories` for what it is legally qualified to bid on. That is a due-diligence file most credit-risk vendors sell by subscription.

**📇 Export the buyer list.**

`contracting-authorities` is 48,040 Italian public buyers with fiscal code, VAT number, AUSA code, legal nature and street address, plus the in-house and state-owned flags — `stazioni appaltanti` is a 390-a-month Italian search on its own. One run, no filters, done.

**💶 Follow the recovery-plan money.**

**Only PNRR / PNC contracts** keeps just the tenders funded by Italy's Recovery and Resilience Plan — 5,414 of June 2025's 123,121. Add `funding-sources` for the state / regional / local / EU split, and `pnrr-indicators` for the female and youth employment quotas each one carries.

**🌍 Skip the OCDS mapping.**

`ocds-releases` hands you Open Contracting Data Standard 1.1 objects with `ocid`, awards, parties and CPV classification already in place, plus the CIGs of every lot so the rows join straight back to the Italian tables. ANAC publishes these as monthly JSON files of up to 3.2 GB; the Actor streams them and stops when your cap is met.

**🇮🇹 Cover the Italian search demand.**

`anac` alone is **110,000 searches a month** in Italy, `cig anac` and `subappalti` 4,400 each, `appalti pubblici` 2,400, `gare d'appalto` 1,300, `bandi di gara` 590 and `aggiudicazioni appalti` 110. Every one of those is a collection in this Actor.

### Why choose this scraper

| | What you get |
|---|---|
| **31 collections, one Actor** | Every ANAC table, not just the tender list. Awards, bidders, subcontracts, lifecycle, buyers, SOA and OCDS. |
| **The bidders, not just the winners** | `participants` carries the firms that lost, which is the only way to measure who competes against whom. |
| **Streaming with early abort** | 20,000 tenders came back having transferred 3.7 MB of the 22.7 MB archive that holds all 123,121 rows for that month. You are not billed for bytes you never needed. |
| **Filters that cut before billing** | Keyword, CIG, fiscal code, CPV, region, province, sector, procedure, amount, date, PNRR and single-bid, all applied while the file streams. |
| **Typed, not raw strings** | Amounts as numbers, dates as ISO 8601, `flag_*` columns as real booleans, with English aliases on top of ANAC's Italian names. |
| **History back to 2007** | Every month of every year, picked with a year and month selector rather than a URL you have to guess. |
| **No login, no API key** | Every surface is public and anonymous. Apify's own datacenter proxy is on by default because the portal blocks the platform's IP range — it costs nothing extra and needs no setup. |
| **Only the columns that exist** | Columns ANAC ships in the header but never fills are not shipped as always-null fields. See the traps below. |

#### Six traps this Actor handles for you

**The portal rejects unknown clients, per path.** `dati.anticorruzione.it` sits behind an F5 ASM WAF. A default `curl` gets `Request Rejected` with a support ID — and it is path-sensitive: the root rejects while `/opendata` answers. The Actor presents a browser client and reads only the paths that serve data.

**The same WAF blocks Apify's own IP range.** The identical request that passes from a workstation on a datacenter IP comes back HTTP 403 from an Apify container. It is the exit IP, not a rate limit, so waiting it out achieves nothing — and about two in three Apify datacenter proxy exits are refused as well. The Actor routes through Apify's datacenter proxy by default, rotates to a fresh session on every 403, and falls back to a direct connection when no proxy is available (which is what makes it work outside Apify). Residential is never requested: datacenter clears this WAF, so paying for residential would buy nothing. A run that is refused everywhere **fails loudly** rather than reporting success with an empty dataset.

**Long downloads get cut off, so they resume.** Reading these archives through a proxy means long-lived connections, and long-lived connections die: an OCDS read that ran to 33 MB from a workstation was killed at ~11 MB on the platform with `other side closed`. The portal answers `accept-ranges: bytes`, so a dropped transfer reopens with a Range request at the exact byte it stopped and the inflater and parser state carry straight on — they never see the seam. A trailing partial row is discarded rather than shipped as data.

**`stato` is a constant, and the cancellation columns are empty.** Every tender ANAC publishes carries `stato: ATTIVO` — 123,121 out of 123,121 in June 2025. The three cancellation columns (`COD_MOTIVO_CANCELLAZIONE`, `MOTIVO_CANCELLAZIONE`, `DATA_CANCELLAZIONE`) are empty in every row of 2015, 2020, 2023 and 2025 that we measured — about 245,000 rows. A revoked tender is recorded through `ESITO` instead, as *"ANNULLATA/REVOCATA ..."*. So the empty columns are not shipped, `outcome` is lifted to the top of the row, and **Exclude cancelled records** reads the outcome. Filtering on `stato` would have silently returned everything.

**The full snapshot does not exist for tenders.** ANAC ships three shapes in the same catalogue: a full snapshot (`aggiudicazioni_csv.zip`), monthly incrementals (`20260801-aggiudicazioni_csv.zip`) and per-year monthly partitions (`cig_csv_2025_06.zip`). `cig` and `smartcig` publish **only** incrementals — their history lives in 19 separate per-year datasets. The Actor resolves the right shape per collection from the live catalogue instead of hard-coding URLs, and says in the log which files it chose.

**A 3.2 GB JSON file will not fit in memory.** The OCDS bulk files are single JSON documents up to 3.2 GB, and one framework-agreement release inside them can be 4 MB on its own. The Actor scans the byte stream incrementally and yields releases as they close, so peak memory stays flat regardless of file size. Releases over 512 KB come back as their lifted summary with `releaseTruncated: true` rather than blowing past the dataset item limit.

### How it compares

Five other Actors scrape ANAC. All five are single-table tender scrapers; none of them publishes a README, so this table compares what their Store listing and pricing state.

| Feature | ParseForge (this) | ayrtondavoli97 | promptica | fortuitous\_pirate | monkey.d.scraping | apexforge |
|---|---|---|---|---|---|---|
| Collections | **31** | 1 (tenders) | 1 (tenders) | 1 | 3 (tenders, awards, suppliers) | 1 (tenders) |
| Losing bidders | **Yes** | No | No | No | No | No |
| Contract lifecycle (SAL, variations, suspensions) | **Yes** | No | No | No | No | No |
| Subcontracts | **Yes** | No | No | No | No | No |
| Contracting-authority registry | **48,040** | No | No | No | No | No |
| SOA qualification certificates | **Yes** | No | No | No | No | No |
| OCDS 1.1 output | **Yes** | No | No | No | No | No |
| History | **2007 →** | Not stated | Not stated | Not stated | 2007 → | Not stated |
| Run-start fee | **$0.007** | $0.10 | $0.05 | $0.05 | $0.02 | $0.005 |
| Price per 1,000 tender rows | $5.00 | $2.00 | $2.00 | $3.50 | $1.00 | $8.00 |
| **1,000 tenders, all in** | **$5.01** | $2.10 | $2.05 | $3.55 | $1.02 | $8.01 |

**We are the expensive option on a plain tender list, and that is deliberate.** If all you need is 1,000 Italian tenders with CPV and value, `monkey.d.scraping` or `promptica` will do it for a third of the price and you should buy that. This Actor is priced for the 30 tables the others do not have: the losing bidders, the SAL instalments, the subcontract chain, the SOA classes, the 48,000-buyer registry and the OCDS output. The run-start fee is 7x to 14x below the two closest competitors, so small exploratory runs land cheaper here even at the higher row rate.

**Why $5 per 1,000.** This Actor is HTTP-only — no browser, no residential IPs, and the datacenter proxy it does use is included in every Apify plan. Measured on the platform 2026-08-27, its own running cost is **$0.0054 per 1,000 rows** (a 1,000-row tender run billed $0.005406 of compute, storage and transfer). The house floor is $5 per 1,000 and the ceiling is $7; because the competition is already below the floor, this sits at the floor rather than the target. The reference and classification tables are cheaper still ($2 to $4 per 1,000), and only the OCDS release — read out of multi-gigabyte files and carrying a nested object — costs more at $9.

### What a row looks like

**Tender** (`collections: ["tenders"]`) — 84 fields, abbreviated here:

```json
{
  "collection": "tenders",
  "title": "INTERVENTO DI RECUPERO DEL CONTESTO PAESAGGISTICO DELLA FRAZIONE DI TRASSILICO",
  "amount": 38754.65,
  "currency": "EUR",
  "buyerName": "COMUNE DI GALLICANO",
  "buyerFiscalCode": "81000160465",
  "procedureType": "AFFIDAMENTO DIRETTO",
  "cpvCode": "45112000-5",
  "cpvDescription": "LAVORI DI SCAVO E MOVIMENTO TERRA",
  "sector": "LAVORI",
  "region": "TOSCANA",
  "province": "LUCCA",
  "publishedDate": "2025-01-24",
  "deadlineDate": "2025-01-23",
  "status": "ATTIVO",
  "outcome": "AGGIUDICATA",
  "outcomeDate": "2025-01-23",
  "isPnrr": false,
  "cig": "B552431AE5",
  "numeroGara": "start_17/01/2025 13:13:24_001700/2025",
  "importoComplessivoGara": 38754.65,
  "nLottiComponenti": 1,
  "importoLotto": 38754.65,
  "settore": "SETTORI ORDINARI",
  "luogoIstat": "046015",
  "codTipoSceltaContraente": "24",
  "modalitaRealizzazione": "CONTRATTO D'APPALTO",
  "codiceAusa": "0000239369",
  "sezioneRegionale": "SEZIONE REGIONALE TOSCANA",
  "idCentroCosto": "D4EC5B46-8752-49C6-B930-57944FB92EC9",
  "denominazioneCentroCosto": "SETTORE LAVORI PUBBLICI",
  "flagUrgenza": true,
  "motivoUrgenza": "NON APPLICABILE",
  "importoSicurezza": 1093.52,
  "flagPnrrPnc": false,
  "sourceDataset": "cig-2025",
  "sourceUrl": "https://dati.anticorruzione.it/opendata/download/dataset/cig-2025/filesystem/cig_csv_2025_01.zip",
  "period": "2025-01",
  "scrapedAt": "2026-08-27T14:11:55.650Z"
}
```

**Award** (`collections: ["awards"]`):

```json
{
  "collection": "awards",
  "cig": "B30B340E7A",
  "awardAmount": 10400,
  "currency": "EUR",
  "discountPct": 0,
  "bidsReceived": 1,
  "singleBid": true,
  "awardedDate": "2024-09-13",
  "outcome": "AGGIUDICATA",
  "flagSubappalto": false,
  "astaElettronica": false,
  "idAggiudicazione": "22043751",
  "period": "20260801"
}
```

**Contracting authority** (`collections: ["contracting-authorities"]`):

```json
{
  "collection": "contracting-authorities",
  "buyerName": "SCUOLA SECONDARIA STATALE DI I GRADO GIOVANNI XXIII",
  "buyerFiscalCode": "81002210649",
  "vatNumber": "00000000000",
  "ausaCode": "0000324784",
  "legalNature": "ENTI PUBBLICI NON ECONOMICI",
  "province": "AVELLINO",
  "city": "GROTTAMINARDA",
  "indirizzoOdonimo": "VIA ALCIDE DE GASPERI 23",
  "cap": "83035",
  "flagInHouse": false,
  "flagPartecipata": false,
  "status": "ATTIVO",
  "dataInizio": "2013-07-09T19:16:34Z"
}
```

**SOA certificate** (`collections: ["soa-certificates"]`):

```json
{
  "collection": "soa-certificates",
  "supplierName": "FERRETTI S.R.L.",
  "supplierFiscalCode": "01838640991",
  "certifierName": "SOA RINA SPA",
  "certificateNumber": "7019",
  "status": "SCADUTO_5_ANNI",
  "province": "GENOVA",
  "validUntil": "2014-07-19",
  "ataAttestatoDataScadenzaValiditaTriennale": "2012-07-19",
  "ataAttestatoDataRilascioAttestazioneOriginaria": "2009-07-20"
}
```

**OCDS release** (`collections: ["ocds-releases"]`) — the full `release` object rides along under `release`:

```json
{
  "collection": "ocds-releases",
  "ocid": "ocds-hu01ve-CONSIP_ORDINE_8709430",
  "releaseId": "ocds-hu01ve-CONSIP_ORDINE_8709430-01",
  "title": "GASOLIO EXTRA RETE",
  "date": "2025-09-19T12:00:00Z",
  "awardAmount": 35000,
  "currency": "EUR",
  "buyerName": "FORMIA RIFIUTI ZERO SOCIETA' A RESPONSABILITA' LIMITATA",
  "buyerFiscalCode": "02796960595",
  "buyerAusaCode": "0000404831",
  "buyerLocality": "FORMIA",
  "supplierNames": ["Q8 QUASER S.R.L."],
  "supplierFiscalCodes": ["00295420632"],
  "cigs": ["B84F8F3C82"],
  "cpvCodes": ["09134100-8"],
  "cpvDescriptions": ["CARBURANTE DIESEL"],
  "awardCount": 1,
  "partyCount": 3,
  "releaseTruncated": false
}
```

### Configure the run

| Setting | What it does |
|---|---|
| **Collections** | Which of the 31 tables to pull. Each bills its own per-row event. |
| **Which files to read** | *Selected period* reads the per-month archives (the only way to reach history); *Latest monthly update* reads the newest incremental; *Full snapshot* reads the complete table. |
| **Years / Months** | Which months to read. Tenders go back to 2007, SmartCIG to 2011, OCDS to 2018. The current year appears only once ANAC creates its dataset — use *Latest monthly update* for the freshest records. |
| **Max rows / Max rows per collection** | Hard caps. The download stops the moment the cap is met. |
| **Keywords** | Substring match on subject, supplier and buyer names. |
| **CIG codes** | Pin to specific tenders. Works across every procurement table — this is how you assemble one contract's lifecycle. |
| **Fiscal codes / VAT numbers** | Match any party: buyer, winner, bidder, subcontractor. Punctuation and country prefixes ignored. |
| **CPV codes** | Prefix match. `45` keeps all construction; `45112000-5` keeps just that class. |
| **Regions / Provinces** | `LOMBARDIA`, `SICILIA`; `MILANO` or `IT-MI`. |
| **Contract sector** | Works, services or supplies. |
| **Procedure types** | By name (`AFFIDAMENTO DIRETTO`) or by ANAC code (`24`). Run `reference-codes` for the full table. |
| **Status** | Substring match, including the award outcome. `AGGIUDICATA`, `SCADUTO`. |
| **Min / Max amount** | On the contract or award value, in EUR. |
| **Date from / to** | On the row's own publication, communication or award date. |
| **Only PNRR / PNC** | Recovery-plan contracts only. |
| **Only single-bid awards** | Awards that drew exactly one bidder. `awards` only — nothing else carries a bid count. |
| **Exclude cancelled records** | Drops outcomes marked annulled or revoked. |
| **Catalogue search / themes / formats** | CKAN full-text and facet filters, for the `datasets` and `resources` collections. |
| **Include untouched source columns** | Adds a `raw` object with every column exactly as ANAC wrote it. Roughly doubles row size. |
| **Include the full OCDS release** | Keeps the complete release object. On by default; dropped automatically below 512 MB of run memory. |
| **Proxy** | On by default, and it must stay on for runs on Apify — the portal blocks the platform's IP range. Apify's datacenter proxy is enough and is included in your plan. |

#### How filtering actually works

ANAC gives no server-side query, so filters run **client-side while the archive streams**. Two consequences worth knowing:

- **A narrow filter costs time, not money.** Filtered-out rows are never written and never billed, but the file still has to stream past. Pinning one CIG in the `awards` table scanned 208,070 rows and pulled 5.4 MB before finding its match. That is the price of a source with no index.
- **OCDS is the slow one.** The bulk files are uncompressed JSON (the server offers no gzip) and have to be read sequentially. Measured on the platform: 600 releases, 33.9 MB, 312 seconds. Budget roughly 2 releases a second and raise **Scraping time budget** for a large pull. Every other collection is 10x to 100x faster — 20,000 tenders came back in 13 seconds.
- **A filter naming a column this table does not have returns nothing, not everything.** That is deliberate. A filter that silently matched every row would look exactly like a table with no matches, and you would only find out after paying for the rows.

To keep a filtered run bounded, set **Scraping time budget** (under Advanced). The Actor flushes what it has and exits cleanly when it expires, rather than being killed mid-write.

### Pricing

Pay-per-event. You are billed per row written, plus a $0.007 run-start fee. Filtered-out rows cost nothing, and neither do the bytes streamed past them.

| Event | Price | Fires for |
|---|---|---|
| Run start | $0.007 | Once per run |
| Tender (CIG) | $0.005 | `tenders` |
| Simplified tender (SmartCIG) | $0.005 | `smart-tenders` |
| Award | $0.005 | `awards` |
| Winning operator | $0.005 | `winners` |
| Bidder | $0.005 | `participants` |
| Contract milestone | $0.005 | `contract-start`, `contract-end`, `testing`, `suspensions` |
| Progress report (SAL) | $0.005 | `progress-reports` |
| Contract variation | $0.005 | `variants` |
| Subcontract | $0.005 | `subcontracts` |
| Economic framework line | $0.005 | `economic-framework` |
| Funding source | $0.005 | `funding-sources` |
| Publication record | $0.005 | `publications` |
| Cost centre | $0.005 | `cost-centres` |
| Contracting authority | $0.006 | `contracting-authorities` |
| SOA certificate | $0.006 | `soa-certificates`, `soa-categories` |
| Work classification | $0.004 | `work-types`, `work-categories`, `dpcm-categories` |
| Tender attribute | $0.004 | `cup-links`, `pnrr-indicators` |
| Catalogue record | $0.002 | `datasets`, `resources`, `organizations`, `groups`, `tags`, `reference-codes` |
| OCDS release | $0.009 | `ocds-releases` |

The run-start fee is exactly one event: `maxMemoryMbytes` is pinned to 1024 in `actor.json`, and Apify bills one start event per GB with a minimum of one.

| Rows collected | Tenders | OCDS releases | Catalogue |
|---|---|---|---|
| 100 | $0.51 | $0.91 | $0.21 |
| 1,000 | $5.01 | $9.01 | $2.01 |
| 10,000 | $50.01 | $90.01 | $20.01 |

Measured platform cost for comparison: a 1,000-row tender run consumed **$0.0054** of Apify compute, storage and transfer; a 600-release OCDS run, which streams for five minutes, consumed **$0.0147**.

### Free users

Apify's free plan caps what any paid Actor can bill in a month. If a run stops early with a usage message, that is the plan cap, not this Actor.

Separately, Apify gives every run a maximum cost derived from your remaining allowance and aborts the run when it reaches it — the run's status message names the figure. A year of `tenders` is over a million rows, which is more than enough to hit that. Set **Max rows** to keep a run inside a budget you choose, and it will finish cleanly instead of being aborted.

### Run it

1. Pick your **Collections**. Start with `tenders`.
2. Choose **Which files to read** — *Selected period* plus a **Year** and **Month** for history, or *Latest monthly update* for the freshest delta.
3. Narrow with CPV, region, amount or keywords if you want.
4. Set **Max rows** for a hard ceiling on cost.
5. Run, then export as CSV, JSON, Excel or XML — or pull it from the API.

The default input — 1,000 tenders from January 2025 — returns in a few seconds having transferred well under a megabyte.

### Troubleshooting

**The run failed with "the ANAC portal refused every request".** The proxy was turned off. Re-enable it — on Apify the portal blocks the platform's IP range outright. This is a hard failure by design, so it can never be mistaken for "ANAC has no data".

**No rows came back.** Check the log line for the collection: it prints how many rows it scanned and how many it kept. `scanned` high and `kept` zero means your filters excluded everything — start by dropping one. `scanned` zero means the file itself was empty or missing for that period.

**"ANAC has not published cig for 2026 yet."** The per-year tender datasets are created only once ANAC starts publishing that year, so the current year is usually missing for months — as of this writing `cig-2026` and `smartcig-2026` do not exist, while `ocds-appalti-ordinari-2026` has January to March. The log names the years that do exist. For the newest tenders, set **Which files to read** to *Latest monthly update*, which reads ANAC's dated incremental and does carry the current year.

**"No CSV distribution matched the requested period."** That collection is not partitioned by month, so it ignores **Years** and **Months** — only `tenders`, `smart-tenders` and `ocds-releases` are. Switch **Which files to read** to *Latest monthly update* or *Full snapshot*.

**`Full snapshot` gave me the latest update instead.** ANAC publishes no full snapshot for `cig` or `smartcig`; their history is in the per-year datasets. The Actor warns and falls back. Use *Selected period* with a year to read history for those two.

**Filtering by CIG is slow.** There is no index — the archive has to stream past until the match is found. Narrow the period first, or raise **Scraping time budget**.

**The OCDS `release` object is missing.** Either the run has under 512 MB of memory, in which case the log says so and the summary fields are still there, or that release was over 512 KB and `releaseTruncated` is `true`.

**A field I expected is `null`.** ANAC leaves many columns sparse. `codModalitaIndizioneServizi` is filled on 0.035% of tenders, `cuiProgramma` on 1.8%. The columns that were empty on *every* row measured — the three cancellation fields, and seven DCAT slots on the catalogue — are not shipped at all, so a `null` you see is a genuine gap in one record rather than a dead column.

**The run stopped before the last collection.** **Max rows** is a global cap. Set **Max rows per collection** as well so one large table cannot consume the whole budget.

### FAQ

**Does it need a login or an API key?** No. Every surface used here is public and anonymous.

**Does it need a proxy?** On Apify, yes — the portal's WAF returns HTTP 403 to the platform's own IP range. Apify's **datacenter** proxy clears it, is on by default and is included in every plan, so there is nothing to configure and no extra cost. Residential is not needed. Outside Apify the Actor falls back to a direct connection on its own.

**How far back does it go?** Tenders to 2007, SmartCIG to 2011, OCDS releases to 2018. The lifecycle tables are cumulative and reach back as far as ANAC's records do — the oldest suspension in a sample was from 2014.

**Can I get the whole of 2025?** Yes: select all twelve months and raise **Max rows**. That is over a million tender rows, so decide the budget first.

**Why is the OCDS collection so much slower?** Its source is a single uncompressed JSON file of up to 3.2 GB that has to be read from the start, and the server offers no gzip. It works — 600 releases in about five minutes — but raise **Scraping time budget** before asking for tens of thousands.

**How current is it?** ANAC publishes monthly. The lifecycle tables get a dated incremental on the 1st, and *Latest monthly update* always reads the newest one published. The per-year tender datasets lag: `cig-YYYY` is created only once ANAC starts publishing that year, so early in a year the current year has no per-month archive and *Latest monthly update* is the way to reach it.

**Why is `stato` always `ATTIVO`?** Because that is what ANAC publishes on every tender. The column that says how the procedure ended is `esito`, lifted to `outcome` on every row.

**What is a CIG?** The *Codice Identificativo Gara* — the identifier Italy assigns to every public tender lot. It is the join key for every procurement collection here, and it also appears inside the OCDS rows as `cigs`.

**Is the data licensed for reuse?** ANAC publishes the catalogue under CC BY-SA 4.0. The `datasets` collection carries the licence on every row.

**Why is `misurepremiali-pnrrpnc` not a collection?** ANAC publishes that file as a bare header with zero rows — 43 bytes. It is left out rather than offered as an option that could only ever return nothing.

### Related actors

- [European TED Tenders Scraper](https://apify.com/parseforge/european-ted-tenders-scraper?fpr=vmoqkp) — EU-wide tender notices from Tenders Electronic Daily
- [GAO Bid Protest Docket Scraper](https://apify.com/parseforge/gao-bid-protest-docket-scraper?fpr=vmoqkp) — US federal bid protests and decisions
- [Semantic Scholar Scraper](https://apify.com/parseforge/semantic-scholar-scraper?fpr=vmoqkp) — research literature with citation graphs

# Actor input Schema

## `collections` (type: `array`):

Which ANAC tables to pull. Tenders (CIG) is the core one — every other procurement table joins to it on the `cig` field. Each collection bills its own event, so pick only what you need.

## `snapshot` (type: `string`):

**Selected period** reads the per-month archives for the years below — the only way to reach history. **Latest monthly update** reads the newest incremental file ANAC published. **Full snapshot** reads the complete table (large; not published for tenders and SmartCIG, which fall back to the latest update).

## `years` (type: `array`):

Years to read when the mode above is *Selected period*. Tenders go back to 2007, SmartCIG to 2011, OCDS releases to 2018.

## `months` (type: `array`):

Months to read within each year. Leave empty for all twelve. Only tenders, SmartCIG and OCDS are split by month; the other tables ignore this.

## `maxItems` (type: `integer`):

Hard cap on rows written across every collection. The download stops the moment the cap is met, so a small cap costs a small transfer even on a 200 MB archive.

## `maxItemsPerCollection` (type: `integer`):

Optional per-collection cap, so one huge table cannot eat the whole budget when several are selected.

## `searchTerms` (type: `array`):

Keep rows whose subject, supplier or buyer name contains any of these (case-insensitive substring). Example: `fotovoltaico`, `ambulanza`.

## `cigs` (type: `array`):

Keep only these tender identifiers. Works across every procurement table, which is how you assemble the full lifecycle of one contract.

## `fiscalCodes` (type: `array`):

Keep rows where any party — buyer, winner, bidder, subcontractor — carries one of these codice fiscale or partita IVA values. Punctuation and country prefixes are ignored.

## `cpvCodes` (type: `array`):

Common Procurement Vocabulary prefixes, matched from the left. `45` keeps all construction, `45112000-5` keeps just that class.

## `regions` (type: `array`):

Italian region names, matched against the ANAC regional section. Example: `LOMBARDIA`, `SICILIA`.

## `provinces` (type: `array`):

Province name or ISO code. Example: `MILANO`, `IT-MI`.

## `sectors` (type: `array`):

The main object of the contract.

## `procedureTypes` (type: `array`):

Award procedure, by ANAC code or by name. Example: `AFFIDAMENTO DIRETTO`, `PROCEDURA APERTA`, or the numeric code `24`. Read the `reference-codes` collection for the full table.

## `statuses` (type: `array`):

Record status, matched as a substring. Example: `ATTIVO`, `SCADUTO`.

## `minAmount` (type: `integer`):

Drops rows whose contract or award value is below this. Rows with no monetary field are dropped too.

## `maxAmount` (type: `integer`):

Drops rows whose contract or award value is above this.

## `dateFrom` (type: `string`):

ISO date. Filters on the row's own publication, communication or award date.

## `dateTo` (type: `string`):

ISO date, inclusive.

## `onlyPnrr` (type: `boolean`):

Keep only tenders funded by Italy's Recovery and Resilience Plan.

## `onlySingleBid` (type: `boolean`):

Keep only awards that drew exactly one bidder — the standard first red flag in competition analysis.

## `excludeCancelled` (type: `boolean`):

Drop tenders carrying a cancellation date or reason.

## `catalogQuery` (type: `string`):

Full-text query passed to CKAN when scraping the catalogue collections (datasets, distributions). Solr syntax works.

## `catalogGroups` (type: `array`):

Restrict catalogue results to these DCAT theme groups.

## `catalogFormats` (type: `array`):

Restrict catalogue results to datasets publishing these distribution formats.

## `includeRawColumns` (type: `boolean`):

Adds a `raw` object holding every column exactly as ANAC wrote it, alongside the typed fields. Roughly doubles row size.

## `includeOcdsRelease` (type: `boolean`):

Keeps the complete OCDS 1.1 release under `release`. Turn off for a flat summary row. Releases over 512 KB are dropped either way and flagged with `releaseTruncated`.

## `proxyConfiguration` (type: `object`):

**Keep this on.** The ANAC portal's WAF blocks the Apify platform's own IP range with HTTP 403, so a run without a proxy returns nothing. Apify's datacenter proxy is enough — residential is not needed.

## `timeoutSecs` (type: `integer`):

Seconds the actor may spend fetching before it flushes and exits cleanly. Kept under the run timeout so a long run still returns what it has.

## Actor input object example

```json
{
  "collections": [
    "tenders"
  ],
  "snapshot": "period",
  "years": [
    "2025"
  ],
  "months": [
    "1"
  ],
  "maxItems": 1000,
  "onlyPnrr": false,
  "onlySingleBid": false,
  "excludeCancelled": false,
  "includeRawColumns": false,
  "includeOcdsRelease": true,
  "proxyConfiguration": {
    "useApifyProxy": true
  },
  "timeoutSecs": 1500
}
```

# Actor output Schema

## `results` (type: `string`):

No description

## `summary` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "collections": [
        "tenders"
    ],
    "snapshot": "period",
    "years": [
        "2025"
    ],
    "months": [
        "1"
    ],
    "maxItems": 1000,
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("parseforge/anac-italy-procurement-scraper").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "collections": ["tenders"],
    "snapshot": "period",
    "years": ["2025"],
    "months": ["1"],
    "maxItems": 1000,
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("parseforge/anac-italy-procurement-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "collections": [
    "tenders"
  ],
  "snapshot": "period",
  "years": [
    "2025"
  ],
  "months": [
    "1"
  ],
  "maxItems": 1000,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call parseforge/anac-italy-procurement-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,parseforge/anac-italy-procurement-scraper"
        }
    }
}

```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/38jPa5HK845SEfCGd/builds/TP266mNidmN9OkBnh/openapi.json
