---
seo_title: API Site Web vers PDF — Crawler un Site Entier | EnConvert
meta_desc: Explorez un site web entier et convertissez chaque page en PDF via POST /v1/convert/website-to-pdf. Sitemap ou crawl complet, sortie ZIP, webhooks asynchrones.
keywords: crawler un site web entier en pdf api, convertir tout un site web en pdf, archiver un site web complet en pdf api, sitemap vers pdf api, exporter toutes les pages d'un site en pdf, convertir un site entier en pdf par programmation, api crawl site web vers pdf en masse, sauvegarder un site web entier en pdf api
---

# API Site Web vers PDF

L'endpoint `POST /v1/convert/website-to-pdf` explore un site web entier — via l'analyse du sitemap ou un crawl complet en largeur d'abord — convertit chaque page découverte en un PDF haute fidélité, et regroupe les résultats dans une seule archive ZIP. Les jobs s'exécutent toujours de façon asynchrone : l'API renvoie immédiatement HTTP 202 avec un `batch_id`, la fin du job est signalée via le polling du statut du batch, un callback webhook, ou une notification par e-mail, et la réponse de statut du batch inclut une URL de téléchargement présignée pour le ZIP terminé. Nécessite un forfait payant et une clé API privée.

---

## Endpoint

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

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

**Format de sortie :** archive ZIP contenant un PDF par page découverte.

**Mode :** toujours asynchrone. Renvoie immédiatement HTTP 202.

---

## Authentification

Cet endpoint nécessite une **clé API privée**. Les clés publiques ne sont pas prises en charge pour la capture de site web.

```
X-API-Key: sk_live_your_private_key
```

---

## Paramètres de la requête

### Paramètres de découverte du site web

| Paramètre | Type | Requis | Défaut | Description | Restriction de forfait |
|-----------|------|----------|---------|-------------|-------------|
| `url` | `string` | Oui | -- | L'URL de base du site web (par ex. `https://example.com`). Utilisée comme racine pour la découverte des pages. | -- |
| `crawl_mode` | `string` | Non | `"auto"` | Méthode de découverte des URL. L'une de `"auto"`, `"sitemap"` ou `"full"`. Voir [Modes de crawl](#crawl-modes) ci-dessous. | Sitemap nécessite Starter+, Full nécessite Pro+ |
| `include_patterns` | `string[]` | Non | `null` | Motifs regex pour mettre en liste blanche les URL découvertes. **Utilisé uniquement en mode de crawl `full`.** | -- |
| `exclude_patterns` | `string[]` | Non | Valeurs par défaut du système | Motifs regex pour mettre en liste noire les URL. **Utilisé uniquement en mode de crawl `full`.** Lorsqu'omis, utilise des valeurs par défaut intégrées qui excluent les ressources statiques, les pages de connexion/admin/panier, et la pagination profonde. | -- |

### Paramètres de notification

| Paramètre | Type | Requis | Défaut | Description | Restriction de forfait |
|-----------|------|----------|---------|-------------|-------------|
| `output_filename` | `string` | Non | Généré automatiquement | Nom de base personnalisé pour le fichier ZIP de sortie. Un horodatage est ajouté automatiquement. | -- |
| `notification_email` | `string` | Non | E-mail du propriétaire du projet | Adresse e-mail à notifier lorsque le job se termine. | -- |
| `callback_url` | `string` | Non | -- | URL de webhook recevant une requête POST à la fin. | Nécessite l'accès aux webhooks |

### Paramètres du navigateur et du rendu

Ces réglages s'appliquent à la conversion de chaque page individuelle au sein du site web.

| Paramètre | Type | Requis | Défaut | Description | Restriction de forfait |
|-----------|------|----------|---------|-------------|-------------|
| `viewport_width` | `integer` | Non | `1920` | Largeur du viewport du navigateur en pixels. | -- |
| `viewport_height` | `integer` | Non | `1080` | Hauteur du viewport du navigateur en pixels. | -- |
| `single_page` | `boolean` | Non | `true` | `true` restitue chaque page comme une seule page PDF continue. `false` produit une sortie paginée utilisant la taille de page de `pdf_options`. | -- |
| `load_media` | `boolean` | Non | `true` | Attend que toutes les images et vidéos soient entièrement chargées avant la conversion. | -- |
| `enable_scroll` | `boolean` | Non | `true` | Fait défiler chaque page pour déclencher le contenu à chargement différé. | -- |
| `handle_sticky_header` | `boolean` | Non | `true` | Détecte les en-têtes collants/fixes et les gère avant la capture. | -- |
| `handle_cookies` | `boolean` | Non | `true` | Ferme automatiquement les bannières de consentement aux cookies. | -- |
| `wait_for_images` | `boolean` | Non | `true` | Attend que tous les éléments `<img>` finissent de charger. | -- |
| `wait_for_selector` | `string` | Non | `null` | Sélecteur CSS à attendre avant la capture, appliqué à chaque page. Renvoie `422` s'il n'apparaît jamais dans le délai `wait_for_selector_timeout`. Utile pour les SPA qui hydratent le contenu après le chargement. | -- |
| `wait_for_selector_timeout` | `integer` | Non | `10000` | Millisecondes d'attente pour `wait_for_selector` (maximum `60000`). | -- |
| `block_ads` | `boolean` | Non | `false` | Interrompt les requêtes vers les domaines connus de publicité/traçage afin qu'ils ne se chargent jamais, ne s'affichent jamais et ne ralentissent pas la capture. | -- |
| `block_media` | `boolean` | Non | `false` | Interrompt entièrement les requêtes d'images et audio/vidéo pour un rendu plus rapide et plus léger. Contrairement à `load_media` (qui contrôle uniquement l'attente), cela empêche totalement le téléchargement des médias. | -- |

### Authentification et requêtes personnalisées

| Paramètre | Type | Requis | Défaut | Description | Restriction de forfait |
|-----------|------|----------|---------|-------------|-------------|
| `auth` | `object` | Non | `null` | Identifiants HTTP Basic Auth appliqués à chaque page. Format : `{"username": "...", "password": "..."}`. | Nécessite l'accès basic auth |
| `cookies` | `array` | Non | `null` | Tableau d'objets cookie injectés avant le chargement de chaque page. Maximum 50 cookies. | Nécessite l'accès basic auth |
| `headers` | `object` | Non | `null` | En-têtes HTTP personnalisés envoyés avec chaque requête. Maximum 20 en-têtes. | Nécessite l'accès basic auth |

### Options PDF

Passez-les dans un objet `pdf_options`. Elles s'appliquent à chaque page du site web.

| Paramètre | Type | Défaut | Description |
|-----------|------|---------|-------------|
| `page_size` | `string` | `"A4"` | Taille de page nommée. Ignorée lorsque `page_width` et `page_height` sont tous deux définis. |
| `page_width` | `float` | `null` | Largeur de page personnalisée en millimètres. `page_width` et `page_height` doivent être définis ensemble. |
| `page_height` | `float` | `null` | Hauteur de page personnalisée en millimètres. |
| `orientation` | `string` | `"portrait"` | `"portrait"` ou `"landscape"`. |
| `margins` | `object` | `{"top": 10, "bottom": 10, "left": 10, "right": 10}` | Marges de page en millimètres. |
| `scale` | `float` | `1.0` | Facteur d'échelle du contenu. Plage : `0.1` à `2.0`. Mode paginé uniquement. |
| `grayscale` | `boolean` | `false` | Convertit chaque page PDF en niveaux de gris. |
| `header` | `object` | `null` | En-tête de page pour le mode paginé. Format : `{"content": "<html>", "height": 15}`. Prend en charge les variables de modèle : `{{page}}`, `{{total_pages}}`, `{{date}}`, `{{title}}`, `{{url}}`. |
| `footer` | `object` | `null` | Pied de page pour le mode paginé. Même format que l'en-tête. |

**Tailles de page prises en charge :** `A0`, `A1`, `A2`, `A3`, `A4`, `A5`, `A6`, `B0`, `B1`, `B2`, `B3`, `B4`, `B5`, `Letter`, `Legal`, `Tabloid`, `Ledger`

---

## Modes de crawl

### `"auto"` (par défaut)

Utilise le mode de crawl le plus élevé que votre forfait autorise. Si votre forfait prend en charge le crawl complet, il exécute un crawl complet. Si votre forfait prend en charge uniquement le sitemap, il exécute la découverte par sitemap.

### `"sitemap"`

Découvre les pages en analysant le `sitemap.xml` du site web :

1. Récupère `{base_url}/sitemap.xml` (délai de 30 secondes)
2. Si l'élément racine est `<sitemapindex>`, récupère récursivement chaque sitemap enfant
3. Extrait toutes les entrées `<url><loc>` des éléments `<urlset>`
4. Renvoie la liste complète des URL découvertes

Renvoie une erreur si le sitemap est manquant, renvoie un statut différent de 200, contient du XML invalide, ou n'a aucune URL.

### `"full"`

Effectue un crawl complet en deux phases :

**Phase 1 -- Découverte des seeds :**

1. Analyse `robots.txt` pour les directives de sitemap et les règles de crawl
2. Vérifie les chemins de sitemap standard (`/sitemap.xml`, `/wp-sitemap.xml`, `/sitemap_index.xml`, etc.)
3. Découvre les flux RSS/Atom à partir des balises `<link>` et des chemins de flux courants
4. Extrait les URL seed de toutes les sources découvertes

**Phase 2 -- Crawl de liens en largeur d'abord :**

1. Démarre depuis l'URL de base plus toutes les URL seed
2. Visite chaque page et met en file d'attente les liens du même domaine
3. Applique `include_patterns` et `exclude_patterns` pour filtrer les liens
4. Respecte les règles de `robots.txt`
5. Détecte et évite les pièges d'URL infinies (pages de calendrier, filtres à facettes, etc.)
6. Déduplique les URL en normalisant le schéma, l'hôte, les paramètres de requête, et en supprimant les paramètres de suivi (`utm_*`, `fbclid`, `gclid`, etc.)

**Motifs d'exclusion par défaut** (lorsque `exclude_patterns` n'est pas fourni) :

- Ressources statiques : `*.pdf`, `*.zip`, `*.jpg`, `*.png`, `*.gif`, `*.svg`, `*.css`, `*.js`, `*.xml`, `*.json`, `*.mp4`, `*.webm`, `*.woff`, `*.woff2`
- Chemins protégés : `/login`, `/admin`, `/cart`, `/checkout`
- Pagination profonde : URL avec des paramètres `page=` dépassant 3 chiffres

---

## Réponse

### 202 Accepted (immédiat)

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

| Champ | Description |
|-------|-------------|
| `batch_id` | UUID pour suivre le job via le polling du statut du batch ou le webhook. |
| `url_count` | Nombre de pages qui seront converties. |
| `total_discovered` | Nombre total de pages découvertes par le crawl. |
| `discovery_method` | `"sitemap"` ou `"full_crawl"` selon le mode de crawl effectif. |

### Polling du statut du batch

Effectuez le polling avec le `batch_id` de la réponse 202 :

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

Renvoie le statut agrégé, les statuts par URL, et une URL de téléchargement présignée pour le ZIP une fois terminé. Voir [Polling du statut du batch](/fr/docs/endpoints/web-pages/url-to-pdf#batch-status-polling-private-keys-only) pour le schéma complet de la réponse.

### Charge utile du callback webhook

Lorsque `callback_url` est fourni, Enconvert envoie une requête POST à la fin :

```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"}
    ]
}
```

### Notification par e-mail

Un e-mail de fin est envoyé à `notification_email` (ou par défaut à l'e-mail du propriétaire du projet) lorsque le job se termine, qu'il réussisse ou échoue.

---

## Restrictions par forfait d'abonnement

| Fonctionnalité | Free | Starter | Pro | Enterprise |
|---------|------|---------|-----|------------|
| Capture de site web | Non | Oui | Oui | Oui |
| Mode de crawl sitemap | Non | Oui | Oui | Oui |
| Mode de crawl complet | Non | Non | Oui | Oui |
| Callbacks webhook | Non | Non | Oui | Oui |
| HTTP Basic Auth | Non | Oui | Oui | Oui |
| Injection de cookies | Non | Oui | Oui | Oui |
| En-têtes personnalisés | Non | Oui | Oui | Oui |
| Limite de taille de batch | 0 | Selon le forfait | Selon le forfait | Illimité |
| Conversions mensuelles | 100 | Selon le forfait | Selon le forfait | Illimité |

<div class="alert alert-warning">
<strong>Forfait Free :</strong> la capture de site web n'est pas disponible sur le forfait gratuit. Toute tentative d'utiliser cet endpoint renvoie <code>403 Forbidden</code>.
</div>

---

## Exemples de code

### Python (clé privée)

```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_live_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_live_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 (clé privée)

```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_live_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 (clé privée)

```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_live_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_live_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 (clé privée)

```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_live_your_private_key")

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

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

### Avec 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}
    }
}
```

### Avec authentification (site protégé par mot de passe)

```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"}
    ]
}
```

---

## Réponses d'erreur

| Statut | Condition |
|--------|-----------|
| `400 Bad Request` | Paramètre `url` manquant ou vide |
| `400 Bad Request` | Aucune URL trouvée dans le sitemap |
| `400 Bad Request` | Délai dépassé lors de la récupération du sitemap (limite de 30 secondes) |
| `400 Bad Request` | Réponse différente de 200 depuis l'URL du sitemap |
| `400 Bad Request` | XML invalide dans le sitemap |
| `400 Bad Request` | Format de sitemap non reconnu |
| `400 Bad Request` | Aucune page découverte (le crawl complet a trouvé zéro URL) |
| `400 Bad Request` | Structure `auth`, `cookies` ou `headers` invalide |
| `402 Payment Required` | Limite de conversions mensuelles dépassée par le nombre de pages découvertes |
| `402 Payment Required` | Limite de stockage atteinte |
| `403 Forbidden` | Crawl de site web non disponible sur le forfait actuel (forfait Free) |
| `403 Forbidden` | Le mode de crawl complet nécessite le forfait Pro ou supérieur |
| `403 Forbidden` | Le nombre de pages découvertes dépasse la limite de taille de batch |
| `403 Forbidden` | Fonctionnalité non disponible sur le forfait (webhook, basic auth) |
| `500 Internal Server Error` | Échec du crawl ou de la conversion |

---

## Limites

| Limite | Valeur |
|-------|-------|
| Délai de récupération du sitemap | 30 secondes |
| Délai global du crawl (mode complet) | 10 minutes |
| Profondeur de crawl maximale (mode complet) | 10 niveaux |
| Délai de crawl par page (mode complet) | 30 secondes |
| Limite de mémoire du crawler | 512 MB |
| Seuil de piège infini | 20 URL par motif d'URL |
| Délai de récupération de robots.txt | 10 secondes |
| Nombre maximal de pages par crawl | Limite de taille de batch du forfait |
| Nombre maximal de cookies par requête | 50 |
| Nombre maximal d'en-têtes personnalisés par requête | 20 |
| Délai de livraison du webhook | 30 secondes |
| Conversions mensuelles | Dépend du forfait |
| Rétention des fichiers | Dépend du forfait |

---

## Questions fréquentes

### Comment convertir un site web entier en PDF avec une API ?

Envoyez une requête `POST` à `/v1/convert/website-to-pdf` avec l'`url` de base du site et votre clé privée dans l'en-tête `X-API-Key`. L'API découvre chaque page (sitemap ou crawl complet), convertit chacune en PDF, les regroupe dans un ZIP, et renvoie HTTP `202` avec un `batch_id` que vous pouvez interroger pour obtenir le lien de téléchargement.

### Quelle est la différence entre le mode de crawl sitemap et le mode de crawl complet ?

`crawl_mode: "sitemap"` analyse le `sitemap.xml` du site (y compris les index de sitemap imbriqués) et est disponible à partir des forfaits Starter. `crawl_mode: "full"` exécute un crawl en deux phases — découverte des seeds à partir de `robots.txt`, des sitemaps et des flux RSS/Atom, puis un crawl de liens en largeur d'abord sur le même domaine avec un filtrage `include_patterns`/`exclude_patterns` — et nécessite le forfait Pro ou supérieur. Le mode `"auto"` par défaut utilise le mode le plus élevé que votre forfait autorise.

### Comment savoir quand mon job de conversion de site web en PDF est terminé ?

Interrogez `GET /v1/convert/batch/{batch_id}` avec votre clé privée pour obtenir le statut agrégé, les statuts par URL, et une URL de téléchargement ZIP présignée, ou transmettez un `callback_url` pour recevoir un POST webhook à la fin. Un e-mail de fin est également envoyé à `notification_email` (ou au propriétaire du projet par défaut), que le job réussisse ou échoue.

### Puis-je archiver en PDF un site protégé par mot de passe ou un site de staging ?

Oui, sur les forfaits avec accès basic auth : transmettez `auth` avec `username` et `password` pour l'authentification HTTP Basic appliquée à chaque page, injectez jusqu'à 50 `cookies` de session, ou envoyez jusqu'à 20 `headers` personnalisés.

### Pourquoi l'endpoint de conversion de site web en PDF renvoie-t-il 403 Forbidden ?

Les causes les plus courantes : la capture de site web n'est pas disponible sur le forfait Free, `crawl_mode: "full"` nécessite le forfait Pro ou supérieur, le nombre de pages découvertes dépasse la limite de taille de batch de votre forfait, ou une fonctionnalité demandée (webhook, basic auth) n'est pas incluse dans votre forfait.
