# PagineGialle Italy Leads Scraper — Phone, Email, P.IVA (`scrapersdelight/paginegialle-italy-scraper`) Actor

Scrape Italian business leads from PagineGialle (paginegialle.it) by activity + city: company name, phone, email, full address with province, website, P.IVA, codice fiscale, geo and opening hours. Beats the site's AWS WAF wall automatically — no login. Export JSON/CSV/Excel.

- **URL**: https://apify.com/scrapersdelight/paginegialle-italy-scraper.md
- **Developed by:** [Scrapers Delight](https://apify.com/scrapersdelight) (community)
- **Categories:** Automation, Lead generation, Agents
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$3.00 / 1,000 lead scrapeds

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/platform/actors/running/actors-in-store#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

## 🇮🇹 PagineGialle Italy Leads Scraper — Phone, Email, Address, P.IVA

**Turn [paginegialle.it](https://www.paginegialle.it) — the Italian Yellow Pages — into a clean B2B lead list.**
Search any activity in any Italian city and get one row per business with the contact data agencies,
call-centers and SaaS teams actually prospect on: **name, phone, email (when published), full address
with CAP and province, website, and the legal identifiers P.IVA (VAT number) / codice fiscale** —
plus geo coordinates, opening hours, services and ratings.

**Measured on real runs, not estimated: phone fill was 100% on every sample we pulled** — 75 Milano
restaurants across 3 result pages, 50 Matera plumbers (mostly free listings), 25 Torino lawyers. A
Milano restaurant search alone is "più di 200 risultati" at 25 businesses per page, and the Actor
walks page after page for as long as you let it; PagineGialle covers every category and comune in
Italy. At **$3 per 1,000 leads**, no login, no setup — paste an activity + a city and run.

This is the Italian sibling of our
[PagesJaunes France Leads Scraper](https://apify.com/scrapersdelight/pagesjaunes-france-scraper) —
same input, same row shape, same pricing, so one pipeline can prospect both markets.

***

### 💡 What does this Actor do?

PagineGialle (Italiaonline) is Italy's largest public business directory. This Actor searches it
like a real Chrome browser, walks the result pages (25 businesses each), opens each business's
detail page, and returns a structured lead record. It beats PagineGialle's AWS WAF rate-wall
automatically by rotating proxy sessions — and self-escalates to Italian residential IPs if the
cheap rung ever gets challenged. You never see any of that; you just get rows.

You give it two things:

- **What** — an activity or category **in Italian** (the site's *cosa* box): `ristoranti`,
  `idraulici`, `avvocati`, `parrucchieri`, `elettricisti`…
- **Where** — an Italian city, town, or postal code (the site's *dove* box): `milano`, `roma`,
  `napoli`, `matera`, `20159`…

### 📤 What you get — one row per business

| Field | Description |
|---|---|
| `name` | Business / trade name |
| `legal_name` | Registered legal name from the business's structured data |
| `phone` | First phone in E.164, e.g. `+390236571817` (Italian numbers keep the leading 0) |
| `phone_display` | Phone as shown on PagineGialle, e.g. `02 36571817` |
| `phones` | Every displayed number (many businesses list landline + mobile) |
| `email` | Public contact email, **when the business publishes one — see fill table below** |
| `address` | Full address line, e.g. `Via Menabrea, 33 - 20159 Milano (MI)` |
| `street` / `postal_code` / `city` / `province` | Address split: street, 5-digit CAP, comune, 2-letter province (`MI`, `RM`…) |
| `region` | Region, e.g. `Lombardia` (from the detail page) |
| `category` | Activity as listed on PagineGialle |
| `services` | The services/features the business advertises (e.g. `da asporto`, `forno a legna`) |
| `website` | The business's own website |
| `vat_number` | **P.IVA** — the 11-digit Italian VAT number |
| `tax_code` | Codice fiscale (often = P.IVA for companies; personal format for sole traders) |
| `rating` / `review_count` | PagineGialle rating out of 5 + review count, when reviews exist |
| `description` | The business's own description text |
| `opening_hours` | `{ "mon": ["12:00 - 15:00", "19:00 - 23:00"], … }` — where shown |
| `latitude` / `longitude` | Geo coordinates (from the detail page) |
| `listing_type` | `paid` or `free` PagineGialle listing — free rows carry fewer optional fields |
| `detail_url` | The PagineGialle page for the business |
| `merchant_id` / `scraped_at` | Stable PagineGialle id + ISO scrape timestamp |

Rows without at least a **name + phone or address** are dropped, so you never pay for empty leads.

### 📊 Field fill — measured on real rows, not estimated

Live samples. The middle column is the largest and most recent one (75 rows, 3 result pages, all
counted by the audit re-run of 2026-08-19); the right-hand column is a small-town trade search from
2026-08-15. Sorted by fill; the sparse fields are bolded so you can't miss them.

| Field | Milano ristoranti (75 rows / 3 pages, paid listings) | Matera idraulici (50 rows, 61/73 cards free) |
|---|---|---|
| `phone` / `phones` | **100%** | **100%** |
| `address` / `city` / `province` / `postal_code` | 100% | 100% |
| `name` / `category` / `description` / `detail_url` | 100% | see below |
| `latitude` / `longitude` | 99% | 100% |
| `vat_number` (P.IVA) | 99% | 98% |
| `legal_name` / `region` | 99% | — |
| `opening_hours` | 96% | **16%** |
| `services` | 91% | — |
| **`email`** | **87%** | **14%** |
| `tax_code` (codice fiscale) | 79% | — |
| `description` | 100% | **20%** |
| **`website`** | **37%** | **6%** |
| **`rating` / `review_count`** | **36%** | **2%** |

**Why `website` and `rating` are sparse — and why no scraper can fix it.** Both come from the
business's own structured data on its PagineGialle page: `website` is only present when the business
has registered one (`sameAs`), and `rating` only exists once the business has at least one review
(`aggregateRating`). We re-probed this during the 2026-08-19 audit by fetching detail pages directly:
on a business with no `website` value, the page carries **no external business link at all** — the
data is not hidden behind a click, it was never published. `email` is the same story, and it still
lands at 87% on big-city paid listings.

**Read the two columns before you plan an email campaign.** Big-city searches are dominated by
*paid* PagineGialle listings, which publish rich profiles — there email fill measured 80%. Small-town
and trade searches are mostly *free* listings, which usually publish only name, address, phone and
P.IVA — there email measured 14%. Every row tells you which kind it is in `listing_type`, and every
run prints its own measured fill in the log. **Phone and P.IVA are the reliable columns; email is a
bonus, not a promise.**

### ⚙️ Example input

```json
{
  "what": "ristoranti",
  "where": "milano",
  "maxItems": 20,
  "maxPages": 1,
  "includeCompanyDetails": true
}
```

That block is literally the input the Actor ships with — click **Try for free**, hit **Start**, and
it costs $0.06. Scale up with `maxItems` / `maxPages` when you like what you see.

#### Fast mode — 25 leads per request

```json
{ "what": "avvocati", "where": "torino", "maxItems": 100, "maxPages": 4, "includeCompanyDetails": false }
```

With **Fetch company details** off, the Actor reads only the search pages — 25 businesses per
HTTP request, so 100 leads ≈ 4 requests. You keep name, category, address/CAP/province and — for
most listings — inline phone, description and hours. You lose email, website, P.IVA, codice
fiscale, region, geo and rating (they live on the detail page). Measured on 25 Torino lawyers:
phone 100%, address 100%, hours 72%, description 100%. Same price per lead either way.

### 📈 Example output (one real item)

A real row from an actual run (2026-08-15):

```json
{
  "merchant_id": "878bb75c-b04a-4491-959a-0944ef863033",
  "name": "Pizzeria Ristorante L' Angolo di Napoli",
  "legal_name": "Pizzeria Ristorante L' Angolo di Napoli",
  "phone": "+390236571817",
  "phone_display": "02 36571817",
  "phones": ["02 36571817", "393 205633828"],
  "email": "ipartenopeisrl@gmail.com",
  "address": "Via Luigi Federico Menabrea, 33 - 20159 Milano (MI)",
  "street": "Via Luigi Federico Menabrea, 33",
  "postal_code": "20159",
  "city": "Milano",
  "province": "MI",
  "region": "Lombardia",
  "category": "ristoranti e trattorie",
  "services": ["da asporto", "pizzeria con forno a legna", "locali climatizzati"],
  "website": "https://www.angolodinapoli.com",
  "vat_number": "11288360966",
  "tax_code": "11288360966",
  "rating": 5,
  "review_count": 3,
  "description": "Stavi cercando un'ottima Pizza napoletana…",
  "opening_hours": { "mon": ["12:00 - 15:00", "19:00 - 23:00"], "sun": ["12:00 - 15:00", "19:00 - 23:00"] },
  "latitude": 45.49689,
  "longitude": 9.18526,
  "listing_type": "paid",
  "detail_url": "https://www.paginegialle.it/pizzeria-ristorante-l-angolo-di-napoli-milano",
  "scraped_at": "2026-08-15T04:20:51.060Z"
}
```

(`services`, `description` and `opening_hours` truncated here for length; full in the dataset.)

### 💰 Pricing — pay per result

**$3.00 per 1,000 leads** (`$0.003` per lead), charged on the `lead-scraped` event. You are charged
only for leads actually returned — nothing for empty pages, blocked requests, or duplicate cards.
Rows are deduplicated on the stable PagineGialle business id **before** they are pushed or billed,
and the run asserts uniqueness before it is allowed to succeed. The shipped default run (20 leads)
costs **$0.06**.

### 🧱 How it beats the bot-wall — measured, not guessed

paginegialle.it sits behind CloudFront + **AWS WAF with a rate rule**: each IP gets roughly 6
requests per window, then the WAF answers HTTP 202 with a JavaScript challenge
(`x-amzn-waf-action: challenge`), and flagged IPs briefly serve CloudFront 403. It is a rate
limiter, not a hard wall — so this Actor leases each proxy session for at most 4 requests and
rotates, staying under the bucket. Transport ladder, measured through Apify on 2026-08-15:

| Rung | Result |
|---|---|
| Direct curl (home IP) | ~6 requests, then challenged — the reason naive scrapers die |
| Apify datacenter, one session pushed | exactly 6 requests, then HTTP 202 challenge (4× reproduced) |
| **Apify datacenter + rotate every ≤4 (the default)** | starts every run here; holds for the whole run on some runs, gets challenged on others |
| Apify RESIDENTIAL + country IT (the automatic fallback) | **32/32 contiguous list pages across 4 cities, 0 challenges** |

If datacenter sessions keep getting burned mid-run, the Actor **self-escalates to RESIDENTIAL +
Italy** and finishes the job (unless you pinned proxy groups yourself in the Proxy input, in which
case your choice is respected and no escalation happens).

**How often that actually happens — measured, not assumed.** Across **11 completed platform runs**
(2026-08-15 → 2026-08-19), the datacenter pool was challenged 0–14 times per run and the Actor
**escalated to RESIDENTIAL + IT on 7 of the 11**, typically 5–57 s into the run. Every one of the 11
still returned its full row count — the escalation is the mechanism that makes the default reliable,
not a failure. Practical consequence for your bill: **expect a run to use some Apify residential
bandwidth.** Measured on a full 75-lead / 3-page run: $0.045 total platform usage, of which $0.030
was residential transfer — about **$0.0006 per lead** on top of the $0.003 per-lead price. Pin
datacenter-only groups in the Proxy input if you would rather trade reliability for zero residential
spend.

`robots.txt`: paginegialle.it does not serve one — the path returns a CloudFront 403 "Request
blocked" (verified 2026-08-15), so there are no crawl-delay or disallow lines to follow. Defaults
stay gentle anyway: concurrency 4, list pages carry 25 rows per request, and sessions are rotated
rather than hammered.

### 🔁 Duplicates & pagination — measured

Contiguous page walks measured 2026-08-15: `ristoranti/milano` p1→p4 = 100 cards, 100 unique
(**0.0% dupes**); p1→p8 in Roma, Napoli, Firenze and Bologna = 25 unique cards on every page, no
cross-page repeats. PagineGialle does not pin promoted cards to every page the way some directories
do. The Actor still dedupes on `merchant_id` before enriching or billing, and **asserts** uniqueness
across everything it pushed before the run may succeed. Past-the-end pages return HTTP 404 or an
empty listing — the Actor treats that as "listing exhausted", logs it, and stops cleanly.

### ❗ When a run fails (on purpose)

A silent empty dataset is worse than an error, so zero rows always **throws**, with diagnostics:

- **Unknown activity term** → PagineGialle refuses non-Italian / unknown terms with a persistent
  403/404 (verified: a garbage term 403s while real terms on the same session return 200). The
  error says so and suggests Italian category names.
- **Real term, zero results** → the error quotes the site's own result count and reminds you the
  site wants Italian city names (`milano`, not `Milan`).
- **Wall trouble** → if every rotated session was challenged, the error says that and tells you to
  re-run with the Proxy input set to Apify RESIDENTIAL, country IT.
- **Duplicate leak** → the uniqueness assertion fails the run rather than billing a dirty dataset.

### ❓ FAQ

**Do I need a PagineGialle account or API key?** No. It reads the public directory.

**Why is `email` sometimes empty?** Because most *free* PagineGialle listings don't publish one
(measured: 14% on a free-heavy search, 80% on a paid-heavy one). When a public email exists it is
captured; otherwise the field is `null`. Phone is the reliable contact column at 100% measured fill.

**Where do P.IVA and codice fiscale come from?** From the business's own structured data on its
detail page (fill measured 98–100%). Keep **Fetch company details** on to collect them.

**Can I search a whole province or postal code?** Postal codes work in **Where** (verified:
`20159` returns the CAP's businesses). For a province, run its comuni — the row's `province` field
lets you merge and filter afterwards.

**How many results per search?** 25 per page. Milano restaurants alone shows "più di 200
risultati" — raise **Max result pages** and **Max leads** to go deeper.

**Does it deduplicate?** Yes — by the PagineGialle business id within a run, asserted before the
run can succeed. Across runs, dedupe on `merchant_id` or `vat_number` in your pipeline.

**What formats can I export?** JSON, CSV, Excel, HTML or an API feed — like any Apify dataset. It
also works from Zapier, Make, n8n, webhooks and MCP like any other Actor.

**Can I schedule it?** Yes — save your input as a Task and attach an Apify Schedule for fresh
leads on a cadence.

**Is it fast?** Yes — no browser, pure HTTP. List-only mode is 25 leads per request; detail mode
adds one request per business, run concurrently.

### 🇫🇷🇮🇹 The family

Same input shape, same row shape, same price — one pipeline, two markets:

| Actor | Market | The legal-identifier wedge |
|---|---|---|
| **This Actor** | Italy (paginegialle.it) | **P.IVA + codice fiscale** on ~98–100% of detailed rows |
| [PagesJaunes France Leads Scraper](https://apify.com/scrapersdelight/pagesjaunes-france-scraper) | France (pagesjaunes.fr) | SIRET / SIREN / NAF on detailed rows |

### ⚖️ Legal & fair use

This Actor collects data from a **public** business directory. **You are responsible for how you
use it** and for complying with PagineGialle's / Italiaonline's Terms of Service.

**GDPR note:** Italian business listings can contain personal data (a sole trader's name, a direct
phone, a personal codice fiscale). Under the **GDPR** you need a lawful basis to process personal
data, must respect data-subject rights, and — for B2B prospecting in Italy — the Garante's rules on
unsolicited marketing. Use the output for legitimate B2B outreach, honor opt-outs and deletion
requests, and do not spam. This is guidance, not legal advice — consult your own counsel.

PagineGialle® is a trademark of Italiaonline S.p.A. This Actor is not affiliated with, endorsed
by, or connected to Italiaonline.

# Actor input Schema

## `what` (type: `string`):

What to search on PagineGialle — an activity or category in Italian (the site's 'cosa' box). Examples: "ristoranti", "idraulici", "avvocati", "parrucchieri". Unknown/English terms are rejected by the site.

## `where` (type: `string`):

Where to search (the site's 'dove' box) — an Italian city or town name. Examples: "milano", "roma", "napoli", "matera".

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

Stop after this many leads (cost/speed guard). Set 0 for every result across all pages up to the safety cap.

## `maxPages` (type: `integer`):

How many search-result pages to walk (25 businesses per page). Raise it to go deeper into a city/activity.

## `includeCompanyDetails` (type: `boolean`):

Open each business's detail page to add email (when published), website, P.IVA (VAT number), codice fiscale, region, geo coordinates, rating and services. Also the ONLY phone source for free listings — with this off, only paying listings carry a phone.

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

Default: Apify proxy (datacenter, the cheap pool) with automatic session rotation every 4 requests. PagineGialle's AWS WAF challenges datacenter IPs on some runs (measured: 7 of 11 platform runs), and the actor then self-escalates to RESIDENTIAL, country IT for the rest of the run — so expect a run to use some residential bandwidth (~$0.0006/lead). Pin RESIDENTIAL + IT yourself to force the reliable rung from the start, or pin datacenter-only groups to guarantee zero residential spend (your choice is never overridden).

## `requestConcurrency` (type: `integer`):

Max parallel requests while enriching leads. Keep modest — PagineGialle rate-limits per IP (~6 requests per window), and each worker rotates its session automatically.

## Actor input object example

```json
{
  "what": "ristoranti",
  "where": "milano",
  "maxItems": 20,
  "maxPages": 1,
  "includeCompanyDetails": true,
  "proxyConfiguration": {
    "useApifyProxy": true
  },
  "requestConcurrency": 4
}
```

# Actor output Schema

## `records` (type: `string`):

The dataset of scraped PagineGialle business leads (one item per business).

# 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 = {
    "what": "ristoranti",
    "where": "milano",
    "maxItems": 20,
    "maxPages": 1,
    "includeCompanyDetails": true,
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("scrapersdelight/paginegialle-italy-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 = {
    "what": "ristoranti",
    "where": "milano",
    "maxItems": 20,
    "maxPages": 1,
    "includeCompanyDetails": True,
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("scrapersdelight/paginegialle-italy-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 '{
  "what": "ristoranti",
  "where": "milano",
  "maxItems": 20,
  "maxPages": 1,
  "includeCompanyDetails": true,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call scrapersdelight/paginegialle-italy-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,scrapersdelight/paginegialle-italy-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/mdfhOPLdKTRoRTBfF/builds/aZ76g6tbK1GOLypOM/openapi.json
