---
seo_title: API conversion URL vers Markdown pour LLM | EnConvert
meta_desc: Convertissez des URLs en Markdown GitHub-Flavored propre via POST /v1/convert/url-to-markdown. Extraction Readability et frontmatter YAML pour pipelines LLM.
keywords: convertir url en markdown api, convertir page web en markdown pour llm, api html vers markdown, convertisseur url vers markdown, extraire article web en markdown api, url vers markdown pour pipeline rag, conversion en masse url vers markdown api, api extraction contenu readability
---

# API URL vers Markdown

L'endpoint `POST /v1/convert/url-to-markdown` convertit toute page web accessible publiquement en Markdown GitHub-Flavored propre, avec un bloc de métadonnées frontmatter YAML. Chaque page est rendue dans un vrai navigateur, puis passée dans un extracteur de lisibilité qui supprime le superflu (navigation, pieds de page, asides, scripts, formulaires, boutons), avant d'être sérialisée en Markdown avec des liens normalisés, des blocs de code délimités, et les URLs relatives résolues en URLs absolues. C'est exactement ce dont les pipelines d'ingestion LLM et RAG ont besoin, à la place du HTML brut. Les conversions s'exécutent en mode synchrone ou asynchrone par lot, et les résultats sont renvoyés sous forme d'octets Markdown bruts ou d'une URL de téléchargement présignée.

---

## Endpoint

```
POST /v1/convert/url-to-markdown
```

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

**Format de sortie :** Markdown (`.md`, UTF-8) avec un bloc frontmatter YAML en haut du fichier contenant les métadonnées de la page. Le format de sortie n'est pas configurable. Du Markdown avec frontmatter YAML est toujours produit.

---

## Authentification

Cet endpoint prend en charge l'authentification par clé privée et par clé publique.

### Clé privée

Incluez votre clé secrète dans l'en-tête `X-API-Key`. Utilisez cette méthode pour les appels serveur à serveur, où la clé n'est jamais exposée au client.

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

### Clé publique avec JWT

Pour une utilisation côté client, générez d'abord un token JWT avec votre clé publique, puis passez-le en tant que token Bearer.

**Étape 1. Obtenir un token :**

```
POST /v1/auth/token
X-API-Key: pk_your_public_key
```

**Étape 2. Utiliser le token :**

```
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
```

<div class="alert alert-info">
<strong>Remarque :</strong> Les requêtes avec clé publique sont limitées à une seule URL, au mode synchrone et au téléchargement direct. Le mode asynchrone, le traitement par lots, les webhooks et les emails de notification ne sont pas disponibles avec les clés publiques.
</div>

---

## Paramètres de requête

### Paramètres de premier niveau

| Parameter | Type | Required | Default | Description | Plan Gating |
|-----------|------|----------|---------|-------------|-------------|
| `url` | `string` or `string[]` | Oui | -- | Une chaîne URL unique ou un tableau d'URLs à convertir. Plusieurs URLs nécessitent le mode asynchrone. | -- |
| `async_mode` | `boolean` | Non | `false` | Exécute la conversion de manière asynchrone. Renvoie immédiatement un `batch_id` pour l'interrogation. Requis pour le traitement par lots (plusieurs URLs). | Nécessite l'accès au mode asynchrone |
| `direct_download` | `boolean` | Non | `false` | Renvoie les octets Markdown bruts dans le corps de la réponse au lieu d'une réponse JSON avec une URL présignée. Forcé à `true` pour les clés publiques. Incompatible avec `async_mode` et plusieurs URLs. | -- |
| `output_format` | `boolean` | Non | `false` | Quand `true` avec plusieurs URLs, regroupe tous les fichiers Markdown de sortie dans une archive ZIP unique. Nécessite plusieurs URLs. | Nécessite l'accès à la sortie ZIP |
| `output_filename` | `string` | Non | Généré automatiquement | Nom de fichier personnalisé pour le fichier de sortie. L'extension `.md` est ajoutée automatiquement. Format par défaut : `{domain}_{timestamp}.md`. | -- |
| `job_id` | `string` | Non | -- | ID de tâche fourni par le client pour la reprise après timeout. **Clés publiques uniquement.** Quand une conversion synchrone dépasse les limites de timeout du reverse proxy, le client peut interroger `GET /v1/convert/status/{job_id}` pour récupérer le résultat. Ignoré pour les clés privées. | -- |
| `notification_email` | `string` | Non | Email du propriétaire du projet | Adresse email à notifier lorsqu'une tâche asynchrone se termine. Clés privées uniquement. | -- |
| `callback_url` | `string` | Non | -- | URL webhook qui recevra une requête POST lorsque la conversion se termine. Clés privées uniquement. | Nécessite l'accès aux webhooks |

### Paramètres du navigateur et du rendu

| Parameter | Type | Required | Default | Description | Plan Gating |
|-----------|------|----------|---------|-------------|-------------|
| `viewport_width` | `integer` | Non | `1920` | Largeur de la fenêtre d'affichage du navigateur en pixels. Affecte le contenu responsive et la variante de mise en page capturée avant l'extraction. | -- |
| `viewport_height` | `integer` | Non | `1080` | Hauteur de la fenêtre d'affichage du navigateur en pixels. Utilisée comme référence pour le rendu et le calcul des unités de viewport. | -- |
| `load_media` | `boolean` | Non | `true` | Attend que toutes les images et vidéos soient entièrement chargées avant l'extraction. Quand la valeur est `false`, l'extraction est plus rapide mais les images en chargement différé (lazy-loaded) peuvent conserver des valeurs `src` de substitution dans la sortie Markdown. | -- |
| `enable_scroll` | `boolean` | Non | `true` | Fait défiler la page de haut en bas pour déclencher le chargement du contenu différé (chargeurs basés sur IntersectionObserver). | -- |
| `handle_sticky_header` | `boolean` | Non | `true` | Détecte les en-têtes collants/fixes et fait défiler la page vers le haut avant l'extraction afin de préserver correctement l'ordre du contenu. | -- |
| `handle_cookies` | `boolean` | Non | `true` | Ferme automatiquement les bannières de consentement aux cookies (OneTrust, Cookiebot, Didomi, Usercentrics, et bannières génériques) avant l'extraction. | -- |
| `wait_for_images` | `boolean` | Non | `true` | Attend que tous les éléments `<img>` terminent de charger (timeout de 5 secondes par image) afin que le texte `alt` et les valeurs `src` finales soient capturés correctement. | -- |
| `wait_for_selector` | `string` | Non | `null` | Sélecteur CSS à attendre avant l'extraction. 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 et ne ralentissent pas l'extraction. | -- |
| `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

| Parameter | Type | Required | Default | Description | Plan Gating |
|-----------|------|----------|---------|-------------|-------------|
| `auth` | `object` | Non | `null` | Identifiants HTTP Basic Auth pour l'URL cible. Format : `{"username": "...", "password": "..."}`. Ne peut pas être utilisé en même temps qu'un en-tête personnalisé `Authorization`. | Nécessite l'accès à l'authentification de base |
| `cookies` | `array` | Non | `null` | Tableau d'objets cookie à injecter avant la navigation. Maximum 50 cookies. Chaque cookie doit avoir `name`, `value`, et soit `domain` soit `url`. | Nécessite l'accès à l'authentification de base |
| `headers` | `object` | Non | `null` | Dictionnaire d'en-têtes HTTP personnalisés envoyés avec chaque requête vers l'URL cible. Maximum 20 en-têtes. En-têtes bloqués : `host`, `content-length`, `transfer-encoding`, `connection`, `upgrade`, `te`, `trailer`. | Nécessite l'accès à l'authentification de base |

<div class="alert alert-warning">
<strong>Non pris en charge :</strong> Les paramètres <code>single_page</code> et <code>pdf_options</code> de l'endpoint <a href="/fr/docs/endpoints/convert/web-pages/url-to-pdf">url-to-pdf</a> sont acceptés par souci de cohérence de forme des requêtes, mais n'ont aucun effet sur la sortie Markdown. Markdown n'a pas de notion de pages, de marges ou d'orientation.
</div>

---

## Schéma de l'objet cookie

Chaque élément du tableau `cookies` doit respecter cette structure :

| Field | Type | Required | Default | Description |
|-------|------|----------|---------|-------------|
| `name` | `string` | Oui | -- | Nom du cookie. |
| `value` | `string` | Oui | -- | Valeur du cookie. |
| `domain` | `string` | Conditionnel | -- | Domaine du cookie. `domain` ou `url` doit être fourni. |
| `url` | `string` | Conditionnel | -- | URL à associer au cookie. `domain` ou `url` doit être fourni. |
| `path` | `string` | Non | `"/"` | Chemin du cookie. Par défaut `"/"` quand `domain` est défini. |

---

## Réponse

### Synchrone avec téléchargement direct (`direct_download=true`)

**Clé privée** renvoie les octets Markdown bruts :

```
HTTP 200 OK
Content-Type: text/markdown; charset=utf-8
Content-Disposition: inline; filename="example_20260421_123456789.md"
X-Object-Key: env/files/{project_id}/url-to-markdown/example_20260421_123456789.md
X-File-Size: 8421
X-Conversion-Time: 6.3
X-Filename: example_20260421_123456789.md

(UTF-8 Markdown with YAML frontmatter)
```

**Clé publique** renvoie du JSON avec une URL présignée :

```json
{
    "presigned_url": "https://spaces.example.com/...",
    "object_key": "env/files/{project_id}/url-to-markdown/example_20260421_123456789.md",
    "filename": "example_20260421_123456789.md",
    "file_size": 8421,
    "conversion_time_seconds": 6.3,
    "job_id": "client-provided-id"
}
```

### Synchrone sans téléchargement direct (`direct_download=false`)

Disponible uniquement avec les clés privées.

```json
{
    "presigned_url": "https://spaces.example.com/...",
    "object_key": "env/files/{project_id}/url-to-markdown/example_20260421_123456789.md",
    "filename": "example_20260421_123456789.md",
    "file_size": 8421,
    "conversion_time_seconds": 6.3
}
```

### Mode asynchrone

Renvoie immédiatement un `batch_id` pour l'interrogation.

```
HTTP 202 Accepted
```

```json
{
    "status": "processing",
    "batch_id": "550e8400-e29b-41d4-a716-446655440000",
    "url_count": 5,
    "output_format": "individual"
}
```

Quand `output_format=true` (regroupement ZIP) :

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

### Interrogation du statut de la tâche (clés publiques uniquement)

Pour la reprise après timeout avec une clé publique :

```
GET /v1/convert/status/{job_id}
Authorization: Bearer <jwt_token>
```

| Status | Response |
|--------|----------|
| En cours | `{"status": "processing"}` |
| Succès | `{"status": "success", "presigned_url": "...", "object_key": "..."}` |
| Échec | `{"status": "failed", "error": "..."}` |

### Interrogation du statut du lot (clés privées uniquement)

Pour les tâches asynchrones par lot, interrogez avec le `batch_id` reçu dans la réponse 202 :

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

Renvoie le statut agrégé, les statuts par URL, ainsi que des URLs de téléchargement présignées. Voir [Interrogation du statut du lot](/fr/docs/endpoints/convert/web-pages/url-to-pdf.md#batch-status-polling-private-keys-only) pour le schéma complet de la réponse.

### Charge utile du callback webhook

Quand un `callback_url` est fourni, EnConvert envoie une requête POST à cette URL à la fin de la conversion.

**Tâche avec une seule URL :**

```json
{
    "job_id": "activity_id",
    "status": "success",
    "batch_id": "...",
    "gcs_uri": "object_key",
    "filename": "example_20260421_123456789.md",
    "file_size": 8421
}
```

**Tâche par lot :**

```json
{
    "job_id": "activity_id",
    "status": "success",
    "batch_id": "...",
    "total_tasks": 10,
    "successful_tasks": 8,
    "failed_tasks": 2,
    "tasks": [
        {"url": "https://example.com/page1", "status": "success", "filename": "page1.md"},
        {"url": "https://example.com/page2", "status": "failed", "filename": null}
    ]
}
```

---

## Format de sortie

Chaque fichier Markdown commence par un bloc frontmatter YAML contenant les métadonnées de la page, suivi du corps de l'article extrait.

```markdown
---
url: https://example.com/articles/my-post
title: My Post Title
description: A short summary from the meta description or og:description tag.
links:
  - url: https://example.com/related
    text: Related article
  - url: https://example.com/about
    text: About the author
images:
  - url: https://example.com/hero.jpg
    alt: Hero image alt text
  - url: https://example.com/diagram.png
    alt: Architecture diagram
---

# My Post Title

Opening paragraph of the article body, converted to GitHub-Flavored Markdown...

## A Section Heading

- List item one
- List item two

\`\`\`python
def example():
    return "code blocks are fenced with language hints"
\`\`\`

[A link in the body](https://example.com/linked-page)

![An image in the body](https://example.com/inline-image.png)
```

### Champs du frontmatter

| Field | Type | Description |
|-------|------|-------------|
| `url` | `string` | L'URL finale après redirections (ce n'est pas toujours l'URL que vous avez envoyée). |
| `title` | `string` | Le titre de la page issu de `<title>`, avec repli sur le titre court détecté par Readability. |
| `description` | `string` | La valeur de `<meta name="description">`, avec repli sur `<meta property="og:description">`. |
| `links` | `array` | Chaque `<a href>` trouvé sur la page, avec les URLs absolues et le texte d'ancrage visible. |
| `images` | `array` | Chaque `<img src>` trouvé sur la page, avec les URLs absolues et le texte `alt`. |

### Conventions Markdown

- **Style de titre :** ATX (`#`, `##`, `###`)
- **Puces de liste :** `-`
- **Emphase :** `*bold*`, `*italic*` avec échappement de `*` et `_` dans le texte littéral
- **Retours à la ligne souples :** deux espaces en fin de ligne (préservés dans la sortie)
- **Blocs de code :** délimités (` ``` `) avec indices de langage détectés depuis `class="language-xxx"`, `class="lang-xxx"`, `class="highlight-source-xxx"`, `data-lang`, et `data-language`
- **Liens :** `[text](url)` quand un texte d'ancrage est présent, forme autolink `<url>` quand l'ancrage est vide, les liens uniquement ancrés (`#foo`) et les liens `javascript:` sont convertis en texte brut
- **Images :** `![alt](src)`, avec `title` préservé quand présent, avec repli sur `data-src` quand `src` est absent (images en chargement différé)
- **Règles horizontales :** `---`

---

## Fonctionnalités

### Extraction propre du contenu

EnConvert utilise l'algorithme Readability (la même bibliothèque qui alimente le mode lecture de Firefox) pour isoler le contenu principal de l'article du reste de la page, puis applique une seconde passe de post-traitement pour produire du Markdown propre.

**Supprimé avant la conversion :**

- Navigation (`<nav>`), pieds de page (`<footer>`), asides (`<aside>`)
- Scripts (`<script>`, `<noscript>`), styles (`<style>`), iframes, formulaires, boutons
- SVG en ligne, canvas et éléments template
- Les attributs `style`, `class`, `id`, et tous les gestionnaires d'événements `on*`

**Préservé :**

- Titres, paragraphes, listes, tableaux, citations, blocs de code
- Liens avec leur `href` et leur texte d'ancrage (URLs absolues)
- Images avec `alt`, `title`, et `src` absolu
- Figures et figcaptions (les images en ligne y sont conservées)

### Mode de capture claire

Avant l'extraction, la page est rendue dans un vrai navigateur et nettoyée de la même manière que pour [url-to-pdf](/fr/docs/endpoints/convert/web-pages/url-to-pdf.md) :

- **Bannières de consentement aux cookies** : fermées automatiquement sur la page principale et les iframes (OneTrust, Cookiebot, Didomi, Usercentrics, et bannières génériques).
- **Fermeture des modales et popups** : les overlays sont fermés via la touche Échap, les boutons de fermeture ARIA, les boutons de fermeture basés sur des classes, et les boutons de dialogue basés sur des rôles.
- **Révélation des animations au défilement** : force la visibilité des éléments masqués par WOW.js, AOS, ScrollReveal, GSAP ScrollTrigger, et les classes d'animation génériques.
- **Gestion des en-têtes collants** : les en-têtes collants/fixes sont détectés et la page est ramenée en haut afin de préserver l'ordre du contenu.

### Résolution des URLs absolues

Chaque `href` et `src` relatif dans l'article extrait est résolu par rapport à l'URL finale de la page (après redirections), de sorte que la sortie Markdown contient toujours des liens absolus et cliquables, ce qui est utile pour les pipelines d'ingestion LLM qui verraient sinon des chemins relatifs cassés.

Les liens uniquement ancrés (`#section`), les liens `javascript:`, `mailto:`, et `tel:` ne sont pas réécrits. Les liens uniquement ancrés et les liens `javascript:` sont convertis en texte brut car ils n'ont aucun sens en dehors de la page d'origine.

### Détection du langage des blocs de code

Les blocs de code sont délimités avec un indice de langage détecté quand c'est possible :

```
<pre><code class="language-python">...</code></pre>    →   ```python
<pre data-lang="js">...</pre>                          →   ```js
<pre><code class="highlight-source-shell">...</code></pre> →   ```shell
```

Les classes correspondant à `language-*`, `lang-*`, `highlight-source-*`, et `brush:*` sont reconnues, ainsi que les attributs `data-lang` et `data-language` sur le `<pre>` et son `<code>` imbriqué. Si aucun indice n'est trouvé, le bloc est délimité sans étiquette de langage.

### HTTP Basic Auth

Passez `auth` avec `username` et `password` pour convertir des pages protégées par HTTP Basic Authentication.

```json
{
    "url": "https://staging.example.com/docs/article",
    "auth": {
        "username": "admin",
        "password": "secret"
    }
}
```

### Injection de cookies

Injectez jusqu'à 50 cookies avant le chargement de la page. Utile pour convertir des pages d'articles réservées aux membres ou spécifiques à une locale.

```json
{
    "url": "https://example.com/members/post",
    "cookies": [
        {"name": "session_id", "value": "abc123", "domain": "example.com"},
        {"name": "locale", "value": "en-US", "domain": "example.com"}
    ]
}
```

### En-têtes personnalisés

Envoyez jusqu'à 20 en-têtes HTTP personnalisés avec chaque requête vers la page cible.

```json
{
    "url": "https://example.com/api-docs",
    "headers": {
        "X-Custom-Token": "my-token-value",
        "Accept-Language": "en-US"
    }
}
```

### Chargement différé des images

Quand `load_media` et `enable_scroll` sont activés (tous deux à `true` par défaut), le convertisseur fait défiler la page lentement pour déclencher les chargeurs différés, puis attend que toutes les images terminent de charger avant de capturer le HTML final. Cela garantit que les valeurs `data-src` ont été promues en véritables valeurs `src` et que la liste `images` du frontmatter est complète.

Définissez `load_media=false` pour une extraction plus rapide quand vous n'avez besoin que du corps textuel. Des valeurs `src` de substitution peuvent alors subsister dans la sortie.

### Fonctionnalités de rendu supplémentaires

- **Normalisation des unités de viewport** : les unités CSS de viewport (`vh`, `svh`, `lvh`, `dvh`) sont converties en valeurs de pixels fixes avant l'extraction.
- **Mode furtif** : masquage de l'empreinte du navigateur pour éviter la détection de bot sur les pages protégées.
- **Interception des popups** : ferme automatiquement tout nouvel onglet ou popup déclenché par la page.
- **Contournement CSP** : gère les restrictions Content Security Policy et Trusted Types qui bloqueraient sinon la manipulation de la page.

---

## Restrictions liées au forfait d'abonnement

| Feature | Founding | Indie | Studio | Enterprise |
|---------|------|---------|-----|------------|
| Conversion basique (URL unique, synchrone) | Oui | Oui | Oui | Oui |
| Options de viewport et de rendu | Oui | Oui | Oui | Oui |
| Mode asynchrone | Non | Oui | Oui | Oui |
| Traitement par lots (plusieurs URLs) | Non | Oui | Oui | Oui |
| Regroupement de sortie ZIP | 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 |
| Conversions mensuelles | 100 | Selon le forfait | Selon le forfait | Illimité |
| Limite de taille de lot | 0 | Selon le forfait | Selon le forfait | Illimité |
| Rétention des fichiers | 1 heure | Selon le forfait | Selon le forfait | Selon le forfait |

---

## Mode asynchrone

Le mode asynchrone est utile pour les conversions longues ou lors de la conversion de plusieurs URLs.

### Fonctionnement

1. Envoyez une requête avec `async_mode=true` (ou passez plusieurs URLs, ce qui active automatiquement le mode asynchrone).
2. L'API renvoie immédiatement un HTTP 202 avec un `batch_id` et un `url_count`.
3. Chaque URL est convertie en arrière-plan, envoyée vers le stockage, et suivie individuellement.
4. Suivez l'achèvement via l'**interrogation du statut du lot**, la **notification par email**, ou le **callback webhook**.

### Notification par email

Par défaut, un email d'achèvement est envoyé à l'adresse email du propriétaire du projet. Remplacez-la avec `notification_email` :

```json
{
    "url": ["https://example.com/page1", "https://example.com/page2"],
    "async_mode": true,
    "notification_email": "team@example.com"
}
```

### Callback webhook

Fournissez un `callback_url` pour recevoir une notification POST automatique à l'achèvement :

```json
{
    "url": ["https://example.com/page1", "https://example.com/page2"],
    "async_mode": true,
    "callback_url": "https://your-server.com/webhook/enconvert"
}
```

Le webhook est envoyé avec un timeout de 30 secondes et considère les codes HTTP 200, 201, 202, et 204 comme une livraison réussie.

---

## Traitement par lots et en masse

Convertissez plusieurs URLs en une seule requête. Nécessite le mode asynchrone et une clé privée.

### Sortie individuelle (par défaut)

Chaque URL produit un fichier Markdown séparé :

```json
{
    "url": [
        "https://example.com/post-1",
        "https://example.com/post-2",
        "https://example.com/post-3"
    ],
    "async_mode": true
}
```

### Sortie en archive ZIP

Regroupez tous les fichiers Markdown dans une archive ZIP unique :

```json
{
    "url": [
        "https://example.com/post-1",
        "https://example.com/post-2",
        "https://example.com/post-3"
    ],
    "async_mode": true,
    "output_format": true,
    "output_filename": "blog-archive"
}
```

Le fichier ZIP est nommé `{output_filename}_{timestamp}.zip` ou `batch_{timestamp}.zip` si aucun nom personnalisé n'est fourni.

---

## Exemples de code

### Python (clé privée)

```python
import requests

response = requests.post(
    "https://api.enconvert.com/v1/convert/url-to-markdown",
    headers={"X-API-Key": "sk_your_private_key"},
    json={
        "url": "https://example.com/articles/my-post",
        "direct_download": True
    }
)

response.raise_for_status()
markdown_text = response.content.decode("utf-8")
print(markdown_text)
```

### PHP (clé privée)

```php
$ch = curl_init("https://api.enconvert.com/v1/convert/url-to-markdown");
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/articles/my-post"
    ])
]);

$response = curl_exec($ch);
curl_close($ch);

$data = json_decode($response, true);
echo $data["presigned_url"];
```

### Node.js (clé privée)

```javascript
const response = await fetch("https://api.enconvert.com/v1/convert/url-to-markdown", {
    method: "POST",
    headers: {
        "Content-Type": "application/json",
        "X-API-Key": "sk_your_private_key"
    },
    body: JSON.stringify({
        url: "https://example.com/articles/my-post"
    })
});

const data = await response.json();
console.log(data.presigned_url);
```

### 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/articles/my-post",
    })

    req, _ := http.NewRequest("POST", "https://api.enconvert.com/v1/convert/url-to-markdown", 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))
}
```

### JavaScript côté navigateur (clé publique)

```javascript
// Step 1: Get a JWT token
const tokenRes = await fetch("https://api.enconvert.com/v1/auth/token", {
    method: "POST",
    headers: { "X-API-Key": "pk_your_public_key" }
});
const { token } = await tokenRes.json();

// Step 2: Convert URL to Markdown (public keys receive raw bytes)
const convertRes = await fetch("https://api.enconvert.com/v1/convert/url-to-markdown", {
    method: "POST",
    headers: {
        "Content-Type": "application/json",
        "Authorization": `Bearer ${token}`
    },
    body: JSON.stringify({
        url: "https://example.com/articles/my-post"
    })
});

const markdown = await convertRes.text();
console.log(markdown);
```

### React (clé publique)

```jsx
import { useState } from "react";

function UrlToMarkdown() {
    const [loading, setLoading] = useState(false);
    const [markdown, setMarkdown] = useState("");

    async function convertUrl() {
        setLoading(true);
        try {
            // Get JWT token
            const tokenRes = await fetch("https://api.enconvert.com/v1/auth/token", {
                method: "POST",
                headers: { "X-API-Key": "pk_your_public_key" }
            });
            const { token } = await tokenRes.json();

            // Convert
            const convertRes = await fetch("https://api.enconvert.com/v1/convert/url-to-markdown", {
                method: "POST",
                headers: {
                    "Content-Type": "application/json",
                    "Authorization": `Bearer ${token}`
                },
                body: JSON.stringify({ url: "https://example.com/articles/my-post" })
            });

            setMarkdown(await convertRes.text());
        } finally {
            setLoading(false);
        }
    }

    return (
        <div>
            <button onClick={convertUrl} disabled={loading}>
                {loading ? "Converting..." : "Convert to Markdown"}
            </button>
            {markdown && <pre>{markdown}</pre>}
        </div>
    );
}

export default UrlToMarkdown;
```

---

## Réponses d'erreur

| Status | Condition |
|--------|-----------|
| `400 Bad Request` | Paramètre `url` manquant ou vide |
| `400 Bad Request` | `output_format=true` avec une seule URL (plusieurs URLs requises) |
| `400 Bad Request` | `direct_download=true` avec plusieurs URLs |
| `400 Bad Request` | `direct_download=true` avec `async_mode=true` |
| `400 Bad Request` | Objet `auth` invalide (`username` ou `password` manquant) |
| `400 Bad Request` | `cookies` invalide (n'est pas un tableau, dépasse 50 entrées, champs requis manquants) |
| `400 Bad Request` | `headers` invalide (n'est pas un objet, dépasse 20 entrées, noms d'en-tête bloqués, valeurs non textuelles) |
| `400 Bad Request` | Conflit entre `auth` et l'en-tête personnalisé `Authorization` |
| `400 Bad Request` | Clé publique tentant plusieurs URLs |
| `401 Unauthorized` | Clé API / token JWT manquant ou invalide |
| `402 Payment Required` | Quota mensuel d'ops épuisé |
| `402 Payment Required` | Le lot dépasserait le quota mensuel d'ops restant |
| `402 Payment Required` | Limite de stockage atteinte |
| `403 Forbidden` | Endpoint non inclus dans les endpoints autorisés de la clé API |
| `403 Forbidden` | Fonctionnalité non disponible sur le forfait actuel (asynchrone, webhook, ZIP, basic auth) |
| `403 Forbidden` | La taille du lot dépasse la limite du forfait |
| `404 Not Found` | ID de tâche introuvable (lors de l'interrogation du statut) |
| `500 Internal Server Error` | Échec de la conversion (crash du navigateur, erreur de navigation, échec de l'extraction) |

---

## Limites

| Limit | Value |
|-------|-------|
| Timeout de navigation de page | 60 secondes |
| Timeout de chargement par image | 5 secondes |
| Timeout de fermeture de la bannière cookies | 3 secondes |
| Nombre maximum de cookies par requête | 50 |
| Nombre maximum d'en-têtes personnalisés par requête | 20 |
| Opérations mensuelles | Selon le forfait (Founding : 500) |
| Taille de lot | Selon le forfait (Founding : désactivé) |
| Rétention des fichiers | Selon le forfait (Founding : 1 heure) |
| Timeout de livraison webhook | 30 secondes |

---

## Questions fréquentes

### Comment convertir une page web en Markdown avec une API REST ?

Envoyez une requête `POST` à `/v1/convert/url-to-markdown` avec un `url` dans le corps JSON et votre clé dans l'en-tête `X-API-Key`. Vous recevez en retour une réponse JSON avec un `presigned_url` vers le fichier Markdown, ou les octets Markdown UTF-8 bruts si vous définissez `direct_download=true`.

### Puis-je convertir des pages web en Markdown pour des pipelines LLM et RAG ?

Oui. La sortie est conçue pour l'ingestion LLM. L'algorithme Readability (la même bibliothèque derrière le mode lecture de Firefox) isole l'article principal, le superflu comme `<nav>`, `<footer>`, les scripts et les formulaires est supprimé, chaque lien et URL d'image relatifs sont résolus en URL absolue, et un bloc frontmatter YAML porte les champs `url`, `title`, `description`, `links`, et `images` de la page.

### Puis-je convertir plusieurs URLs en Markdown en une seule requête API ?

Oui. Passez un tableau d'URLs dans `url` avec `async_mode=true` (nécessite une clé privée et un forfait avec accès au traitement par lots) ; l'API renvoie un HTTP `202` avec un `batch_id` que vous interrogez via `GET /v1/convert/batch/{batch_id}`. Définissez `output_format=true` pour regrouper tous les fichiers Markdown dans une archive ZIP unique.

### Pourquoi certaines images de ma sortie Markdown ont-elles des valeurs src de substitution ?

Cela se produit quand `load_media=false`. L'extraction est plus rapide mais les images en chargement différé peuvent conserver des valeurs `src` de substitution. Laissez `load_media` et `enable_scroll` à leur valeur par défaut `true` afin que la page défile pour déclencher les chargeurs différés et que chaque image termine de charger (timeout de 5 secondes par image) avant la capture.

### L'API URL vers Markdown fonctionne-t-elle sur des pages protégées par une connexion ?

Oui, sur les forfaits avec accès à l'authentification de base : passez `auth` avec `username` et `password` pour HTTP Basic Auth, injectez jusqu'à 50 `cookies` de session, ou envoyez jusqu'à 20 `headers` personnalisés, ce qui est utile pour les pages d'articles réservées aux membres ou en environnement de préproduction (staging).
