# Italy IPA Directory – Public Bodies, PEC & Tax Codes (`nobler_zirconia/italy-pa-directory`) Actor

Search and export 23,000+ Italian public bodies from the official IPA registry (AgID): municipalities, regions, health authorities, schools, universities, agencies. Clean PEC, website, tax code, category, comune, province and region. Official CC BY 4.0 open data.

- **URL**: https://apify.com/nobler_zirconia/italy-pa-directory.md
- **Developed by:** [Federico Rossi](https://apify.com/nobler_zirconia) (community)
- **Categories:** Lead generation, Open source, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$2.00 / 1,000 results

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?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
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.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

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

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Italy IPA Directory – Public Bodies, PEC & Tax Codes

Search and export records of **public administrations and other entities registered in IPA** (*Indice dei domicili digitali della Pubblica Amministrazione*, run by AgID): about **23,700 entities, including ~7,900 municipalities**, regions, local health authorities (ASL), hospitals, state schools, universities, chambers of commerce, ministries and agencies, plus companies registered only for e-invoicing or procurement.

Each record: name, **codice fiscale**, IPA code, category, address, comune, province, region, ISTAT and NUTS-3 codes, **certified e-mail (PEC)**, other official e-mails, website and social links. No login, no API key, no scraping: it reads the official IPA open-data API (**CC BY 4.0**) live at every run.

*Cerchi l'elenco PEC degli enti pubblici, gli indirizzi PEC dei comuni o il codice IPA di un ente? Questo Actor esporta i dati ufficiali IPA in CSV, Excel o JSON.*

### Use cases

- **B2G sales & lead lists**: all municipalities of a region, all ASL in Italy, all universities, with official PEC and website.
- **Procurement teams**: map contracting authorities by region and category.
- **CRM / ERP enrichment and checks**: paste a list of tax codes or IPA codes, get the official record and a status for every code.
- **Research & journalism**: a clean master list of the Italian public sector, **joinable on ISTAT municipality codes, codice fiscale and IPA code** with ANAC, OpenCoesione or ISTAT data. (Records are enriched with ISTAT/NUTS codes; no coordinates are included.)
- **Scheduled refresh**: with `updatedSince`, fetch only records updated since your last run. This selects by update date and does not tell you which fields changed.

### Two ways to run it

**1. Search** (no codes given): name words, categories, regions, provinces, comuni, `updatedSince`. All filters combine with AND.

**2. Exact lookup** (any `fiscalCodes` or `ipaCodes` given): only the codes are used; search, category, geography and date filters are **ignored** (a warning is logged). Every requested code gets a free status row (`resultType: "LOOKUP_STATUS"`):

| status | meaning |
|---|---|
| `FOUND` | returned and billed |
| `FOUND_NOT_RETURNED` | exists in IPA, but your Max results or maximum cost stopped it |
| `NOT_FOUND` | the official query completed with no match |
| `INVALID_FORMAT_NOT_FOUND` | not an 11-digit codice fiscale, and no match |

A code is never reported as missing unless it was actually checked. The run's `LOOKUP_REPORT` record has the counts.

**Name search** is word-prefix and accent/case-insensitive: `universit` matches "Università" and "Universitario", `umbria` matches "dell'Umbria", `asl` matches "ASL Asti" but not "Caslino".

### Example tasks

Lombardy municipalities (the prefilled demo):

```json
{ "categories": ["L6"], "regions": ["Lombardia"], "maxResults": 2000 }
```

Tax-code / IPA-code lookup:

```json
{ "fiscalCodes": ["01199250158", "80143490581"], "ipaCodes": ["c_h501"] }
```

Local health authorities of one region:

```json
{ "categories": ["L7"], "regions": ["Piemonte"] }
```

#### Use it from code

**curl** (runs the Actor and returns the results in one call):

```bash
curl -X POST "https://api.apify.com/v2/acts/nobler_zirconia~italy-pa-directory/run-sync-get-dataset-items?token=$APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"categories": ["L6"], "regions": ["Lombardia"], "maxResults": 20}'
```

**JavaScript** (`npm i apify-client`):

```js
import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('nobler_zirconia/italy-pa-directory').call({"categories": ["L6"], "regions": ["Lombardia"], "maxResults": 20});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
```

**Python** (`pip install apify-client`):

```python
from apify_client import ApifyClient
client = ApifyClient(os.environ["APIFY_TOKEN"])
run = client.actor("nobler_zirconia/italy-pa-directory").call(run_input={"categories": ["L6"], "regions": ["Lombardia"], "maxResults": 20})
items = client.dataset(run["defaultDatasetId"]).list_items().items
```

You can also use it from Make, Zapier, n8n, schedules, webhooks or the Apify MCP server for AI agents.

### Output

A public body (`resultType: "PUBLIC_BODY"`, billed):

```json
{
  "resultType": "PUBLIC_BODY",
  "ipaCode": "c_f205",
  "name": "Comune di Milano",
  "acronym": null,
  "fiscalCode": "01199250158",
  "categoryCode": "L6",
  "type": "Pubbliche Amministrazioni",
  "legalNatureCode": "2430",
  "atecoCode": "84.11.10",
  "inLiquidation": false,
  "istatEntityCode": "800000013",
  "miurCode": null,
  "address": "Piazza Della Scala, 2",
  "postalCode": "20121",
  "comune": "Milano",
  "comuneIstatCode": "015146",
  "cadastralCode": "F205",
  "province": "MI",
  "region": "Lombardia",
  "nuts3": "ITC4C",
  "pec": [
    "protocollo@postacert.comune.milano.it"
  ],
  "primaryPec": "protocollo@postacert.comune.milano.it",
  "otherEmails": [
    "protocollo@comune.milano.it"
  ],
  "website": "https://www.comune.milano.it",
  "facebook": null,
  "linkedin": null,
  "twitter": null,
  "youtube": null,
  "lastUpdated": "2026-09-14",
  "source": "IPA – Indice dei domicili digitali della Pubblica Amministrazione e dei gestori di pubblici servizi (AgID)",
  "sourceUrl": "https://indicepa.gov.it/ipa-dati/",
  "license": "CC BY 4.0",
  "licenseUrl": "https://creativecommons.org/licenses/by/4.0/",
  "attribution": "Fonte: IPA – AgID (CC BY 4.0); comuni: ISTAT (CC BY 4.0). Not endorsed by AgID or the Italian government.",
  "retrievedAt": "2026-10-15T09:28:12.959Z"
}
```

A lookup status row (free):

```json
{
  "resultType": "LOOKUP_STATUS",
  "lookupType": "fiscalCode",
  "requestedValue": "01199250158",
  "validFormat": true,
  "status": "FOUND",
  "checked": true,
  "billable": false,
  "source": "IPA – Indice dei domicili digitali della Pubblica Amministrazione e dei gestori di pubblici servizi (AgID)",
  "sourceUrl": "https://indicepa.gov.it/ipa-dati/",
  "license": "CC BY 4.0",
  "licenseUrl": "https://creativecommons.org/licenses/by/4.0/",
  "attribution": "Fonte: IPA – AgID (CC BY 4.0); comuni: ISTAT (CC BY 4.0). Not endorsed by AgID or the Italian government.",
  "retrievedAt": "2026-10-15T09:28:12.959Z"
}
```

| Field | Description |
|---|---|
| `ipaCode` | Unique IPA code |
| `name`, `acronym` | Official name and acronym |
| `fiscalCode` | **Codice fiscale** of the entity, as published in IPA. Often numerically equal to the VAT number, but IPA does not publish it as a partita IVA. |
| `categoryCode`, `type` | IPA category and macro-type |
| `legalNatureCode`, `atecoCode`, `istatEntityCode`, `miurCode` | Classification codes (nullable) |
| `inLiquidation` | `true` if the entity is being wound up |
| `address`, `postalCode`, `comune`, `comuneIstatCode`, `cadastralCode`, `province`, `region`, `nuts3` | Location, enriched with ISTAT codes (nullable when IPA has no comune) |
| `primaryPec`, `pec` | Certified e-mail addresses (legal digital domicile) |
| `otherEmails` | Other official e-mails published in IPA |
| `website`, `facebook`, `linkedin`, `twitter`, `youtube` | Official web presence (nullable) |
| `lastUpdated` | Last update of the record in IPA |
| `source`, `sourceUrl`, `license`, `licenseUrl`, `attribution`, `retrievedAt` | Provenance on every item: data source, licence (CC BY 4.0), required attribution, and the UTC time the data was retrieved |

Each run also stores a `SUMMARY` record: `complete`, `truncatedReason` (`MAX_RESULTS` / `MAX_COST` / null), `recordsScanned`, `billableResults`, `datasetItemsWritten` and the lookup counts. A full country export also checks that every page of the source was read and fails otherwise.

### Pricing

**$0.002 per public body returned** (pay per event `result`, $2 per 1,000), plus Apify's tiny run-start event. Lookup status rows are free. Zero matches cost nothing beyond the start event. All ~1,500 municipalities of Lombardy cost about $3. If the source is down or returns broken data, the run fails instead of finishing with an empty "success".

### Data source, licence and privacy

- Source: [IPA open data – AgID](https://indicepa.gov.it/ipa-dati/), dataset *Enti*, licence [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/). Attribution: "Fonte: IPA – Indice dei domicili digitali della Pubblica Amministrazione e dei gestori di pubblici servizi (AgID)". Municipality names and codes: ISTAT, CC BY 4.0.
- **Privacy by design:** IPA also publishes the names and titles of the officials in charge of each entity. They are **never** included in the output, and there is no option to include them. If you use official e-mails for outreach, you remain responsible for GDPR and Italian e-communications rules. PEC addresses are legal channels, not marketing channels.

### FAQ

**I need e-invoicing codes (Codice Univoco Ufficio).** Use the companion Actor [Italy PA e-Invoicing Codes – Codice Univoco Ufficio (SDI)](https://apify.com/nobler_zirconia/italy-pa-einvoice-codes).

**How do I keep only public administrations?** Turn off *Include contracting stations & public-service companies* (excludes categories SA, SAG, S01, S01G, L37). This applies in search mode only.

### Disclaimer

This Actor is an independent tool. It is **not affiliated with, endorsed by or sponsored by** AgID or any Italian government body. The data is reproduced from official open data under its licence. It is provided "as is", may contain errors or delays from the source, and is not legal or fiscal advice.

# Actor input Schema

## `search` (type: `string`):

Every word must match the beginning of a word in the body's name or acronym (accent- and case-insensitive): "asl" matches "ASL Asti" but not "Caslino"; "universit" matches "Università". Leave empty to list everything matching the other filters.

## `categories` (type: `array`):

IPA categories (e.g. L6 municipalities, L7 local health authorities, L33 state schools, L17 universities). Empty = all.

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

Empty = all of Italy.

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

Two-letter codes, e.g. MI, RM, BG.

## `comuni` (type: `array`):

Names, ISTAT codes (e.g. 015146) or cadastral codes (e.g. F205) of the municipality where the body is located.

## `updatedSince` (type: `string`):

Only bodies whose IPA record was last updated on or after this date (YYYY-MM-DD). Handy for scheduled runs. Note: this selects by update date; it does not tell you which fields changed.

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

11-digit codice fiscale of the body ("IT" prefix and spaces are removed). When any tax or IPA code is given, the run is an exact lookup: search, category, geography and date filters are ignored. Every code gets a status row: FOUND, FOUND_NOT_RETURNED (cap reached), NOT_FOUND or INVALID_FORMAT_NOT_FOUND.

## `ipaCodes` (type: `array`):

e.g. c_f205 (Municipality of Milan), r_lazio.

## `includeContractingStations` (type: `boolean`):

IPA also lists companies registered only for e-invoicing or procurement (categories SA, SAG, S01, S01G, L37). Turn off to keep public administrations only. Search mode only.

## `includeInLiquidation` (type: `boolean`):

Search mode only.

## `maxResults` (type: `integer`):

Maximum number of bodies returned and billed. Lookup status rows are free and not counted.

## Actor input object example

```json
{
  "categories": [
    "L6"
  ],
  "regions": [
    "Lombardia"
  ],
  "includeContractingStations": true,
  "includeInLiquidation": true,
  "maxResults": 20
}
```

# Actor output Schema

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

Default dataset, overview table view. Each item includes source, licence, attribution and retrievedAt.

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

SUMMARY record: result count, warnings, finish time.

## `notFound` (type: `string`):

NOT_FOUND record (only written when some requested codes were not found).

## `lookupStatus` (type: `string`):

Free LOOKUP_STATUS rows, one per requested code.

## `lookupReport` (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 = {
    "categories": [
        "L6"
    ],
    "regions": [
        "Lombardia"
    ],
    "maxResults": 20
};

// Run the Actor and wait for it to finish
const run = await client.actor("nobler_zirconia/italy-pa-directory").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 = {
    "categories": ["L6"],
    "regions": ["Lombardia"],
    "maxResults": 20,
}

# Run the Actor and wait for it to finish
run = client.actor("nobler_zirconia/italy-pa-directory").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 '{
  "categories": [
    "L6"
  ],
  "regions": [
    "Lombardia"
  ],
  "maxResults": 20
}' |
apify call nobler_zirconia/italy-pa-directory --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,nobler_zirconia/italy-pa-directory"
        }
    }
}
```

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/arFLlQ1rykReL1kHg/builds/Da55L4SjVfHLeOvTw/openapi.json
