---
seo_title: API per Convertire un Intero Sito Web in PDF | EnConvert
meta_desc: Esegui il crawling di un sito web e converti ogni pagina in PDF con POST /v1/convert/website-to-pdf. Sitemap o crawl completo, output ZIP, async con webhook.
keywords: convertire intero sito web in pdf api, sito web in pdf api, archiviare un sito web come pdf, convertire tutte le pagine di un sito in pdf, sitemap to pdf converter api, crawl completo di un sito in pdf, api conversione pagine web in blocco, salvare un sito web come pdf via api
---

# API Sito Web in PDF

L'endpoint `POST /v1/convert/website-to-pdf` esegue il crawling di un intero sito web (tramite parsing della sitemap o un crawl completo breadth-first), converte ogni pagina rilevata in un PDF ad alta fedeltà e raggruppa i risultati in un unico archivio ZIP. I job vengono sempre eseguiti in modo asincrono: l'API restituisce HTTP 202 con un `batch_id` immediatamente, il completamento viene segnalato tramite polling dello stato del batch, un callback webhook o una notifica email, e la risposta dello stato del batch include un URL di download presigned per lo ZIP completato. Richiede un piano a pagamento e una chiave API privata.

---

## Endpoint

```
POST /v1/convert/website-to-pdf
```

**Content-Type:** `application/json`

**Formato di output:** archivio ZIP contenente un PDF per ogni pagina rilevata.

**Modalità:** Sempre asincrona. Restituisce HTTP 202 immediatamente.

---

## Autenticazione

Questo endpoint richiede una **chiave API privata**. Le chiavi pubbliche non sono supportate per la cattura di siti web.

```
X-API-Key: sk_your_private_key
```

---

## Parametri della Richiesta

### Parametri di Scoperta del Sito Web

| Parametro | Tipo | Obbligatorio | Predefinito | Descrizione | Limitazioni per Piano |
|-----------|------|----------|---------|-------------|-------------|
| `url` | `string` | Sì | -- | L'URL base del sito web (es. `https://example.com`). Usato come radice per il rilevamento delle pagine. | -- |
| `crawl_mode` | `string` | No | `"auto"` | Metodo di rilevamento degli URL. Uno tra `"auto"`, `"sitemap"` o `"full"`. Vedi [Modalità di Crawl](#modalita-di-crawl) di seguito. | Sitemap richiede Indie+, Full richiede Studio+ |
| `include_patterns` | `string[]` | No | `null` | Pattern regex per inserire in whitelist gli URL rilevati. **Usato solo in modalità di crawl `full`.** | -- |
| `exclude_patterns` | `string[]` | No | Valori predefiniti di sistema | Pattern regex per inserire in blacklist gli URL. **Usato solo in modalità di crawl `full`.** Se omesso, usa i valori predefiniti integrati che escludono asset statici, pagine di login/admin/carrello e paginazione profonda. | -- |

### Parametri di Notifica

| Parametro | Tipo | Obbligatorio | Predefinito | Descrizione | Limitazioni per Piano |
|-----------|------|----------|---------|-------------|-------------|
| `output_filename` | `string` | No | Generato automaticamente | Nome base personalizzato per il file ZIP di output. Il timestamp viene aggiunto automaticamente. | -- |
| `notification_email` | `string` | No | Email del proprietario del progetto | Indirizzo email da notificare al completamento del job. | -- |
| `callback_url` | `string` | No | -- | URL webhook che riceve una richiesta POST al completamento. | Richiede accesso webhook |

### Parametri Browser e Rendering

Queste impostazioni si applicano alla conversione di ogni singola pagina all'interno del sito web.

| Parametro | Tipo | Obbligatorio | Predefinito | Descrizione | Limitazioni per Piano |
|-----------|------|----------|---------|-------------|-------------|
| `viewport_width` | `integer` | No | `1920` | Larghezza del viewport del browser in pixel. | -- |
| `viewport_height` | `integer` | No | `1080` | Altezza del viewport del browser in pixel. | -- |
| `single_page` | `boolean` | No | `true` | `true` genera ogni pagina come un'unica pagina PDF continua. `false` produce un output paginato usando la dimensione pagina di `pdf_options`. | -- |
| `load_media` | `boolean` | No | `true` | Attende che tutte le immagini e i video siano completamente caricati prima della conversione. | -- |
| `enable_scroll` | `boolean` | No | `true` | Scorre ogni pagina per attivare i contenuti a caricamento lazy. | -- |
| `handle_sticky_header` | `boolean` | No | `true` | Rileva header sticky/fissi e li gestisce prima della cattura. | -- |
| `handle_cookies` | `boolean` | No | `true` | Chiude automaticamente i banner di consenso cookie. | -- |
| `wait_for_images` | `boolean` | No | `true` | Attende che tutti gli elementi `<img>` finiscano di caricarsi. | -- |
| `wait_for_selector` | `string` | No | `null` | Selettore CSS da attendere prima della cattura, applicato a ogni pagina. Restituisce `422` se non compare mai entro `wait_for_selector_timeout`. Utile per le SPA che idratano il contenuto dopo il caricamento. | -- |
| `wait_for_selector_timeout` | `integer` | No | `10000` | Millisecondi di attesa per `wait_for_selector` (massimo `60000`). | -- |
| `block_ads` | `boolean` | No | `false` | Interrompe le richieste ai domini noti di pubblicità/tracker in modo che non vengano mai caricati, renderizzati o rallentino la cattura. | -- |
| `block_media` | `boolean` | No | `false` | Interrompe completamente le richieste di immagini e audio/video per un rendering più veloce e leggero. A differenza di `load_media` (che controlla solo l'attesa), questo impedisce del tutto il download dei media. | -- |

### Autenticazione e Richieste Personalizzate

| Parametro | Tipo | Obbligatorio | Predefinito | Descrizione | Limitazioni per Piano |
|-----------|------|----------|---------|-------------|-------------|
| `auth` | `object` | No | `null` | Credenziali HTTP Basic Auth applicate a ogni pagina. Formato: `{"username": "...", "password": "..."}`. | Richiede accesso basic auth |
| `cookies` | `array` | No | `null` | Array di oggetti cookie iniettati prima di ogni caricamento pagina. Massimo 50 cookie. | Richiede accesso basic auth |
| `headers` | `object` | No | `null` | Header HTTP personalizzati inviati con ogni richiesta. Massimo 20 header. | Richiede accesso basic auth |

### Opzioni PDF

Passa questi parametri all'interno di un oggetto `pdf_options`. Si applicano a ogni pagina del sito web.

| Parametro | Tipo | Predefinito | Descrizione |
|-----------|------|---------|-------------|
| `page_size` | `string` | `"A4"` | Dimensione pagina predefinita per nome. Ignorato quando sono impostati sia `page_width` che `page_height`. |
| `page_width` | `float` | `null` | Larghezza pagina personalizzata in millimetri. `page_width` e `page_height` devono essere impostati insieme. |
| `page_height` | `float` | `null` | Altezza pagina personalizzata in millimetri. |
| `orientation` | `string` | `"portrait"` | `"portrait"` o `"landscape"`. |
| `margins` | `object` | `{"top": 10, "bottom": 10, "left": 10, "right": 10}` | Margini della pagina in millimetri. |
| `scale` | `float` | `1.0` | Fattore di scala del contenuto. Intervallo: da `0.1` a `2.0`. Solo in modalità paginata. |
| `grayscale` | `boolean` | `false` | Converte ogni pagina PDF in scala di grigi. |
| `header` | `object` | `null` | Intestazione di pagina per la modalità paginata. Formato: `{"content": "<html>", "height": 15}`. Supporta variabili template: `{{page}}`, `{{total_pages}}`, `{{date}}`, `{{title}}`, `{{url}}`. |
| `footer` | `object` | `null` | Piè di pagina per la modalità paginata. Stesso formato dell'intestazione. |

**Dimensioni pagina supportate:** `A0`, `A1`, `A2`, `A3`, `A4`, `A5`, `A6`, `B0`, `B1`, `B2`, `B3`, `B4`, `B5`, `Letter`, `Legal`, `Tabloid`, `Ledger`

---

## Modalità di Crawl

### `"auto"` (predefinito)

Usa la modalità di crawl più alta consentita dal tuo piano. Se il tuo piano supporta il crawl completo, esegue un crawl completo. Se il tuo piano supporta solo la sitemap, esegue il rilevamento tramite sitemap.

### `"sitemap"`

Rileva le pagine effettuando il parsing del file `sitemap.xml` del sito web:

1. Recupera `{base_url}/sitemap.xml` (timeout di 30 secondi)
2. Se l'elemento radice è `<sitemapindex>`, recupera ricorsivamente ogni sitemap figlia
3. Estrae tutte le voci `<url><loc>` dagli elementi `<urlset>`
4. Restituisce l'elenco completo degli URL rilevati

Restituisce un errore se la sitemap è mancante, restituisce uno status diverso da 200, contiene XML non valido o non contiene URL.

### `"full"`

Esegue un crawl completo in due fasi:

**Fase 1 -- Rilevamento dei seed:**

1. Analizza `robots.txt` per direttive sitemap e regole di crawl
2. Controlla i percorsi sitemap standard (`/sitemap.xml`, `/wp-sitemap.xml`, `/sitemap_index.xml`, ecc.)
3. Rileva i feed RSS/Atom dai tag `<link>` e dai percorsi feed comuni
4. Estrae gli URL seed da tutte le fonti rilevate

**Fase 2 -- Crawl dei link breadth-first:**

1. Parte dall'URL base più tutti gli URL seed
2. Visita ogni pagina e accoda i link dello stesso dominio
3. Applica `include_patterns` e `exclude_patterns` per filtrare i link
4. Rispetta le regole di `robots.txt`
5. Rileva ed evita trappole URL infinite (pagine calendario, filtri sfaccettati, ecc.)
6. Deduplica gli URL normalizzando schema, host, parametri di query e rimuovendo i parametri di tracciamento (`utm_*`, `fbclid`, `gclid`, ecc.)

**Pattern di esclusione predefiniti** (quando `exclude_patterns` non è fornito):

- Asset statici: `*.pdf`, `*.zip`, `*.jpg`, `*.png`, `*.gif`, `*.svg`, `*.css`, `*.js`, `*.xml`, `*.json`, `*.mp4`, `*.webm`, `*.woff`, `*.woff2`
- Percorsi protetti: `/login`, `/admin`, `/cart`, `/checkout`
- Paginazione profonda: URL con parametri `page=` che superano 3 cifre

---

## Risposta

### 202 Accepted (immediato)

```json
{
    "status": "processing",
    "batch_id": "550e8400-e29b-41d4-a716-446655440000",
    "url_count": 42,
    "total_discovered": 42,
    "discovery_method": "sitemap",
    "output_format": "zip"
}
```

| Campo | Descrizione |
|-------|-------------|
| `batch_id` | UUID per tracciare il job tramite polling dello stato del batch o webhook. |
| `url_count` | Numero di pagine che verranno convertite. |
| `total_discovered` | Numero totale di pagine rilevate dal crawl. |
| `discovery_method` | `"sitemap"` o `"full_crawl"` a seconda della modalità di crawl effettiva. |

### Polling dello Stato del Batch

Effettua il polling con il `batch_id` ricevuto nella risposta 202:

```
GET /v1/convert/batch/{batch_id}
X-API-Key: sk_your_private_key
```

Restituisce lo stato aggregato, gli stati per singolo URL e un URL di download presigned per lo ZIP al completamento. Vedi [Polling dello Stato del Batch](/it/docs/endpoints/convert/web-pages/url-to-pdf.md#batch-status-polling-private-keys-only) per lo schema completo della risposta.

### Payload del Callback Webhook

Quando viene fornito `callback_url`, EnConvert invia una richiesta POST al completamento:

```json
{
    "job_id": "batch-uuid",
    "status": "success",
    "batch_id": "550e8400-e29b-41d4-a716-446655440000",
    "gcs_uri": "env/files/{project_id}/url-to-pdf/website_20260405_123456789.zip",
    "filename": "website_20260405_123456789.zip",
    "file_size": 12345678,
    "total_tasks": 42,
    "successful_tasks": 40,
    "failed_tasks": 2,
    "tasks": [
        {"url": "https://example.com/", "status": "success", "filename": "example_20260405_001.pdf"},
        {"url": "https://example.com/about", "status": "success", "filename": "example_20260405_002.pdf"},
        {"url": "https://example.com/broken", "status": "failed", "error": "Timeout"}
    ]
}
```

### Notifica Email

Un'email di completamento viene inviata a `notification_email` (o all'email del proprietario del progetto per impostazione predefinita) al termine del job, indipendentemente dal successo o dal fallimento.

---

## Limitazioni per Piano di Abbonamento

| Funzionalità | Founding | Indie | Studio | Enterprise |
|---------|------|---------|-----|------------|
| Cattura sito web | No | Sì | Sì | Sì |
| Modalità crawl sitemap | No | Sì | Sì | Sì |
| Modalità crawl completo | No | No | Sì | Sì |
| Callback webhook | No | No | Sì | Sì |
| HTTP Basic Auth | No | Sì | Sì | Sì |
| Iniezione cookie | No | Sì | Sì | Sì |
| Header personalizzati | No | Sì | Sì | Sì |
| Limite dimensione batch | 0 | In base al piano | In base al piano | Illimitato |
| Conversioni mensili | 100 | In base al piano | In base al piano | Illimitato |

<div class="alert alert-warning">
<strong>Piano Founding:</strong> la cattura di siti web non è disponibile sul piano free. Tentare di usare questo endpoint restituisce <code>403 Forbidden</code>.
</div>

---

## Esempi di Codice

### Python (Chiave Privata)

```python
import requests
import time

# Start the website capture
response = requests.post(
    "https://api.enconvert.com/v1/convert/website-to-pdf",
    headers={"X-API-Key": "sk_your_private_key"},
    json={
        "url": "https://example.com",
        "crawl_mode": "sitemap",
        "output_filename": "example-website",
        "pdf_options": {
            "page_size": "A4",
            "orientation": "portrait"
        }
    }
)

data = response.json()
print(f"Batch ID: {data['batch_id']}")
print(f"Pages found: {data['url_count']}")

# Poll for completion
batch_id = data["batch_id"]
while True:
    status = requests.get(
        f"https://api.enconvert.com/v1/convert/batch/{batch_id}",
        headers={"X-API-Key": "sk_your_private_key"}
    ).json()

    print(f"Status: {status['status']} ({status['completed']}/{status['total']})")

    if status["status"] in ("completed", "partial", "failed"):
        if status.get("zip_download_url"):
            print(f"Download: {status['zip_download_url']}")
        break

    time.sleep(5)
```

### PHP (Chiave Privata)

```php
$ch = curl_init("https://api.enconvert.com/v1/convert/website-to-pdf");
curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        "Content-Type: application/json",
        "X-API-Key: sk_your_private_key"
    ],
    CURLOPT_POSTFIELDS => json_encode([
        "url" => "https://example.com",
        "crawl_mode" => "sitemap",
        "output_filename" => "example-website",
        "pdf_options" => [
            "page_size" => "A4",
            "orientation" => "portrait"
        ]
    ])
]);

$response = json_decode(curl_exec($ch), true);
curl_close($ch);

echo "Batch ID: " . $response["batch_id"] . "\n";
echo "Pages found: " . $response["url_count"] . "\n";
```

### Node.js (Chiave Privata)

```javascript
const response = await fetch("https://api.enconvert.com/v1/convert/website-to-pdf", {
    method: "POST",
    headers: {
        "Content-Type": "application/json",
        "X-API-Key": "sk_your_private_key"
    },
    body: JSON.stringify({
        url: "https://example.com",
        crawl_mode: "sitemap",
        output_filename: "example-website",
        pdf_options: {
            page_size: "A4",
            orientation: "portrait"
        }
    })
});

const data = await response.json();
console.log(`Batch ID: ${data.batch_id}`);
console.log(`Pages found: ${data.url_count}`);

// Poll for completion
const poll = async () => {
    const status = await fetch(
        `https://api.enconvert.com/v1/convert/batch/${data.batch_id}`,
        { headers: { "X-API-Key": "sk_your_private_key" } }
    ).then(r => r.json());

    console.log(`Status: ${status.status} (${status.completed}/${status.total})`);

    if (["completed", "partial", "failed"].includes(status.status)) {
        if (status.zip_download_url) console.log(`Download: ${status.zip_download_url}`);
        return;
    }
    setTimeout(poll, 5000);
};
poll();
```

### Go (Chiave Privata)

```go
package main

import (
    "bytes"
    "encoding/json"
    "fmt"
    "io"
    "net/http"
)

func main() {
    body, _ := json.Marshal(map[string]interface{}{
        "url":             "https://example.com",
        "crawl_mode":      "sitemap",
        "output_filename": "example-website",
        "pdf_options": map[string]interface{}{
            "page_size":   "A4",
            "orientation": "portrait",
        },
    })

    req, _ := http.NewRequest("POST", "https://api.enconvert.com/v1/convert/website-to-pdf", bytes.NewBuffer(body))
    req.Header.Set("Content-Type", "application/json")
    req.Header.Set("X-API-Key", "sk_your_private_key")

    resp, _ := http.DefaultClient.Do(req)
    defer resp.Body.Close()

    respBody, _ := io.ReadAll(resp.Body)
    fmt.Println(string(respBody))
}
```

### Con Callback Webhook

```json
{
    "url": "https://example.com",
    "crawl_mode": "full",
    "callback_url": "https://your-server.com/webhook/enconvert",
    "output_filename": "example-full-site",
    "include_patterns": [".*\\/blog\\/.*", ".*\\/docs\\/.*"],
    "pdf_options": {
        "page_size": "Letter",
        "margins": {"top": 20, "bottom": 20, "left": 15, "right": 15}
    }
}
```

### Con Autenticazione (Sito Protetto da Password)

```json
{
    "url": "https://staging.example.com",
    "crawl_mode": "sitemap",
    "auth": {
        "username": "admin",
        "password": "staging-password"
    },
    "cookies": [
        {"name": "session_token", "value": "abc123", "domain": "staging.example.com"}
    ]
}
```

---

## Risposte di Errore

| Status | Condizione |
|--------|-----------|
| `400 Bad Request` | Parametro `url` mancante o vuoto |
| `400 Bad Request` | Nessun URL trovato nella sitemap |
| `400 Bad Request` | Timeout durante il recupero della sitemap (limite di 30 secondi) |
| `400 Bad Request` | Risposta diversa da 200 dall'URL della sitemap |
| `400 Bad Request` | XML non valido nella sitemap |
| `400 Bad Request` | Formato sitemap non riconosciuto |
| `400 Bad Request` | Nessuna pagina rilevata (il crawl completo non ha trovato URL) |
| `400 Bad Request` | Struttura `auth`, `cookies` o `headers` non valida |
| `402 Payment Required` | Quota mensile di ops superata dal numero di pagine rilevate |
| `402 Payment Required` | Limite di archiviazione raggiunto |
| `403 Forbidden` | Crawling del sito web non disponibile sul piano attuale (piano Founding) |
| `403 Forbidden` | La modalità crawl completo richiede il piano Studio o superiore |
| `403 Forbidden` | Il numero di pagine rilevate supera il limite di dimensione del batch |
| `403 Forbidden` | Funzionalità non disponibile sul piano (webhook, basic auth) |
| `500 Internal Server Error` | Errore di crawl o conversione |

---

## Limiti

| Limite | Valore |
|-------|-------|
| Timeout recupero sitemap | 30 secondi |
| Timeout globale del crawl (modalità full) | 10 minuti |
| Profondità massima del crawl (modalità full) | 10 livelli |
| Timeout crawl per pagina (modalità full) | 30 secondi |
| Limite di memoria del crawler | 512 MB |
| Soglia trappola infinita | 20 URL per pattern URL |
| Timeout recupero robots.txt | 10 secondi |
| Pagine massime per crawl | Limite di dimensione batch del piano |
| Cookie massimi per richiesta | 50 |
| Header personalizzati massimi per richiesta | 20 |
| Timeout di consegna webhook | 30 secondi |
| Conversioni mensili | Dipende dal piano |
| Conservazione dei file | Dipende dal piano |

---

## Domande frequenti

### Come converto un intero sito web in PDF con un'API?

Invia una richiesta `POST` a `/v1/convert/website-to-pdf` con l'`url` base del sito e la tua chiave privata nell'header `X-API-Key`. L'API rileva ogni pagina (sitemap o crawl completo), converte ciascuna in PDF, le raggruppa in uno ZIP e restituisce HTTP `202` con un `batch_id` su cui puoi effettuare il polling per ottenere il link di download.

### Qual è la differenza tra la modalità sitemap e la modalità crawl completo?

`crawl_mode: "sitemap"` effettua il parsing del `sitemap.xml` del sito (inclusi gli indici di sitemap annidati) ed è disponibile dai piani Indie in su. `crawl_mode: "full"` esegue un crawl in due fasi: prima il rilevamento dei seed da `robots.txt`, sitemap e feed RSS/Atom, poi un crawl breadth-first dei link dello stesso dominio con filtro `include_patterns`/`exclude_patterns`. Questa modalità richiede Studio o superiore. Il valore predefinito `"auto"` usa la modalità più alta consentita dal tuo piano.

### Come faccio a sapere quando il mio job di conversione sito web in PDF è terminato?

Effettua il polling di `GET /v1/convert/batch/{batch_id}` con la tua chiave privata per ottenere lo stato aggregato, gli stati per singolo URL e un URL di download ZIP presigned, oppure passa un `callback_url` per ricevere un POST webhook al completamento. Un'email di completamento viene inviata anche a `notification_email` (o al proprietario del progetto per impostazione predefinita), indipendentemente dal successo o dal fallimento.

### Posso archiviare un sito protetto da password o di staging come PDF?

Sì, sui piani con accesso basic auth: passa `auth` con `username` e `password` per l'HTTP Basic Auth applicata a ogni pagina, inietta fino a 50 `cookies` di sessione, oppure invia fino a 20 `headers` personalizzati.

### Perché l'endpoint di conversione sito web in PDF restituisce 403 Forbidden?

Le cause più comuni: la cattura di siti web non è disponibile sul piano Founding, `crawl_mode: "full"` richiede Studio o superiore, il numero di pagine rilevate supera il limite di dimensione batch del tuo piano, oppure una funzionalità richiesta (webhook, basic auth) non è inclusa nel tuo piano.
