---
seo_title: API d'envoi de fichiers : multipart, URL, limites | EnConvert
meta_desc: Toutes les façons d'envoyer des octets à l'API EnConvert : upload multipart ou URL récupérée par l'API, avec plafonds de taille, gestion du 413 et règles de nom.
keywords: api upload de fichiers, envoyer un fichier en multipart form data api, erreur 413 payload too large api, taille maximale de fichier par plan, validation des octets magiques upload, nom du fichier de sortie api, convertir un fichier depuis une url api, upload de plusieurs fichiers api
---

# Ingestion de fichiers

Il y a deux façons d'envoyer des octets à EnConvert : téléverser le fichier vous-même en `multipart/form-data`, ou passer une `url` et laisser l'API récupérer la ressource. Celle que vous pouvez utiliser dépend de l'endpoint, pas de votre plan.

---

## Quel endpoint accepte quoi

| Famille d'endpoints | Comment arrivent les octets | Nom du champ |
|---|---|---|
| [Formats de données](/fr/docs/endpoints/convert/data-formats.md), [documents](/fr/docs/endpoints/convert/documents.md), [images](/fr/docs/endpoints/convert/images.md) | Téléversement `multipart/form-data`, un fichier par requête | `file` |
| [Pages web](/fr/docs/endpoints/convert/web-pages.md) (`url-to-pdf`, `url-to-screenshot`, `url-to-markdown`, `website-to-pdf`, `website-to-screenshot`) | Corps JSON, EnConvert récupère la page | `url` |
| [`POST /v2/ingest/files`](/fr/docs/endpoints/ingest.md) | `multipart/form-data`, plusieurs fichiers par job | `files` |
| [`POST /v2/perceive`](/fr/docs/endpoints/perceive.md), [`POST /v2/ingest`](/fr/docs/endpoints/ingest.md) | Corps JSON, EnConvert récupère la page | `url` |

Il n'y a pas de troisième voie. Les endpoints de téléversement ne récupèrent pas d'URL à votre place, y compris les routes fourre-tout [anything-to-pdf](/fr/docs/endpoints/convert/documents/anything-to-pdf.md) et [anything-to-markdown](/fr/docs/endpoints/convert/documents/anything-to-markdown.md). Pour transformer une page en ligne en PDF, appelez plutôt [url-to-pdf](/fr/docs/endpoints/convert/web-pages/url-to-pdf.md). Pour savoir quels formats accepte chaque endpoint, voir [formats pris en charge](/fr/docs/reference/supported-formats.md).

---

## Téléverser un fichier local

Le champ de formulaire s'appelle `file`, et chaque endpoint de téléversement V1 en accepte exactement un. Tout le reste du formulaire est facultatif.

| Champ de formulaire | Type | Description |
|-----------|------|-------------|
| `file` | file | Le fichier à convertir. Son extension doit être acceptée par l'endpoint. |
| `output_filename` | `string` | Nom de base personnalisé pour la sortie. L'extension cible est ajoutée pour vous. |
| `job_id` | `string` | ID de tâche fourni par le client pour la reprise après timeout. Interrogez `GET /v1/convert/status/{job_id}` si la connexion est coupée. |
| `pdf_options` | `string` | Chaîne JSON d'options PDF, sur les endpoints qui produisent un PDF. |
| `direct_download` | `boolean` | Accepté pour garder la même forme de requête que les endpoints URL. Sans effet ici : un téléversement répond toujours avec l'enveloppe JSON ci-dessous, quoi que vous envoyiez. |

### curl

```bash
curl -X POST https://api.enconvert.com/v1/convert/anything-to-pdf \
  -H "X-API-Key: sk_your_private_key" \
  -F "file=@quarterly-report.docx" \
  -F "direct_download=false"
```

La réponse est du JSON avec un lien de téléchargement pré-signé :

```json
{
    "presigned_url": "https://spaces.example.com/...signed...",
    "object_key": "env/files/{project_id}/anything-to-pdf/quarterly-report_20260714_101530123.pdf",
    "filename": "quarterly-report_20260714_101530123.pdf",
    "file_size": 51240,
    "conversion_time_seconds": 2.1,
    "job_id": null
}
```

Les mêmes valeurs sont reprises dans les en-têtes de réponse `X-Object-Key`, `X-File-Size`, `X-Conversion-Time` et `X-Filename`, vous pouvez donc les lire sans analyser le corps. Récupérez le fichier rapidement : le lien est de courte durée, et [URLs signées](/fr/docs/concepts/signed-urls.md) précise exactement à quel point.

### Python

```python
import requests

with open("quarterly-report.docx", "rb") as f:
    response = requests.post(
        "https://api.enconvert.com/v1/convert/anything-to-pdf",
        headers={"X-API-Key": "sk_your_private_key"},
        files={"file": ("quarterly-report.docx", f)},
        data={"direct_download": "false"},
    )

response.raise_for_status()
result = response.json()

# Download the PDF from the pre-signed URL.
pdf = requests.get(result["presigned_url"]).content
with open("quarterly-report.pdf", "wb") as out:
    out.write(pdf)
```

### Node.js

```javascript
import { readFile, writeFile } from "node:fs/promises";

const form = new FormData();
form.append(
    "file",
    new Blob([await readFile("quarterly-report.docx")]),
    "quarterly-report.docx"
);
form.append("direct_download", "false");

const response = await fetch(
    "https://api.enconvert.com/v1/convert/anything-to-pdf",
    { method: "POST", headers: { "X-API-Key": "sk_your_private_key" }, body: form }
);

const result = await response.json();
const pdf = await fetch(result.presigned_url).then((r) => r.arrayBuffer());
await writeFile("quarterly-report.pdf", Buffer.from(pdf));
```

### Plusieurs fichiers en un seul appel

`POST /v2/ingest/files` est le seul endpoint qui accepte plus d'un fichier. Répétez le champ `files`, jusqu'à 200 fichiers par job :

```bash
curl -X POST https://api.enconvert.com/v2/ingest/files \
  -H "X-API-Key: sk_your_private_key" \
  -F "files=@handbook.pdf" \
  -F "files=@pricing.xlsx" \
  -F "files=@faq.docx" \
  -F "max_words=700" \
  -F "webhook_url=https://your-app.example.com/hooks/ingest"
```

Chaque fichier est converti en Markdown, découpé en chunks et assemblé dans un seul livrable JSONL. Le job est toujours asynchrone et répond `202 Accepted` avec un `job_id`. Tous les détails sont sur la [page de l'endpoint ingest](/fr/docs/endpoints/ingest.md).

---

## Laisser EnConvert récupérer le fichier

Sur les endpoints URL, vous envoyez un corps JSON au lieu d'un formulaire, et c'est l'API qui effectue la récupération :

```bash
curl -X POST https://api.enconvert.com/v1/convert/url-to-markdown \
  -H "X-API-Key: sk_your_private_key" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://example.com/report"}'
```

`url` accepte une chaîne ou un tableau de chaînes. Un tableau bascule la requête en asynchrone, ce que couvre [Traitement par lot](/fr/docs/guides/batch-processing.md).

### Sources derrière une authentification

Trois champs facultatifs permettent à la récupération de porter des identifiants. Les trois nécessitent un plan avec accès à l'authentification basique (Indie et au-dessus).

| Paramètre | Type | Par défaut | Description |
|-----------|------|---------|-------------|
| `auth` | `object` | `null` | Identifiants HTTP Basic Auth : `{"username": "...", "password": "..."}`. |
| `cookies` | `array` | `null` | Tableau d'objets cookie injectés avant la navigation. 50 maximum par requête. Chacun exige `name`, `value`, et soit `domain`, soit `url`. |
| `headers` | `object` | `null` | En-têtes HTTP personnalisés envoyés avec les requêtes. 20 maximum par requête. Ne peuvent pas inclure les en-têtes bloqués : `host`, `content-length`, `transfer-encoding`, `connection`, `upgrade`, `te`, `trailer`. |

<div class="alert alert-info">
<strong>Portée des identifiants :</strong> Les identifiants de l'objet <code>auth</code>, ainsi qu'un en-tête <code>Authorization</code> (par exemple un jeton Bearer) passé via <code>headers</code>, sont envoyés <strong>uniquement à l'origine cible</strong>, jamais aux sous-ressources tierces que la page demande. Cela évite toute fuite d'identifiants vers des hôtes publicitaires, d'analytics ou de CDN.
</div>

### Adresses privées et internes

L'`url` doit être une adresse publique `http://` ou `https://`. Avant toute récupération, elle est filtrée et rejetée avec `400 Bad Request` lorsqu'elle :

- utilise un schéma autre que `http`/`https` ;
- intègre des identifiants comme `https://user:pass@host/` (utilisez plutôt le champ `auth`) ;
- vise `localhost`, un nom d'hôte de métadonnées cloud, ou une IP qui se résout dans une plage privée, loopback, link-local, réservée ou non publique d'une autre manière ;
- utilise une notation IP non standard (octale, hexadécimale ou entier compact) qui pourrait se résoudre de façon ambiguë.

Cela s'applique à l'URL de départ, à chaque URL d'un lot, et aux pages découvertes par les endpoints de crawl `website-to-*`.

Dit simplement : EnConvert s'exécute en dehors de votre réseau. Il ne peut pas atteindre `http://10.0.0.5/report.docx`, un nom d'hôte `.internal`, ni quoi que ce soit qui ne se résout qu'à l'intérieur de votre VPC. Soit vous rendez le fichier joignable depuis l'internet public, soit vous lisez les octets vous-même et vous les téléversez.

---

## Ce qu'il advient de votre nom de fichier

Le nom que vous envoyez remplit deux rôles.

**Il choisit le convertisseur.** L'extension décide quel chemin d'entrée s'exécute : nommez donc le fichier correctement. Un fichier appelé `report` sans extension est rejeté par tout endpoint doté d'une liste d'extensions autorisées.

**Il amorce le nom de sortie.** Le nom du fichier de sortie est construit ainsi :

```
{base}_{YYYYMMDD_HHMMSSmmm}.{ext}
```

L'horodatage UTC est toujours ajouté, si bien que deux conversions du même fichier n'entrent jamais en collision. `base` est résolu dans cet ordre :

1. `output_filename`, si vous en avez envoyé un. Si vous y avez inclus l'extension cible, celle-ci est d'abord retirée pour que vous n'obteniez pas `report.pdf_20260405_123456789.pdf`.
2. Le nom du fichier téléversé sans son extension. `report.docx` produit `report_20260405_123456789.pdf`.
3. Pour les conversions d'URL, le domaine. `https://example.com/page` produit `example_20260405_123456789.pdf`.
4. À défaut de tout cela, le littéral `output`.

La clé de stockage est assainie avant l'écriture du résultat : seul le nom de base survit, `..` est supprimé, les caractères `<>:"|?*` sont retirés et les espaces deviennent des tirets bas. Téléversez `My Report (final).docx` et le PDF atterrit sous `My_Report_(final)_20260405_123456789.pdf`. Ce chemin vous est renvoyé dans `object_key` et a la forme `{env}/files/{project_id}/{endpoint}/{filename}`.

Les noms de fichiers envoyés à `POST /v2/ingest/files` sont en outre plafonnés à 255 caractères.

---

## Plafond de taille et le 413

Le plafond de téléversement dépend du plan et s'applique à chaque fichier individuellement.

| Plan | Taille max de téléversement | Octets |
|------|-----------------|-------|
| Founding | 5 MB | 5242880 |
| Indie | 15 MB | 15728640 |
| Studio | 50 MB | 52428800 |
| Production | 150 MB | 157286400 |
| Enterprise | Négociée | Selon contrat |

La taille est mesurée sur la partie téléversée elle-même, au fil de l'arrivée du corps, et non d'après un en-tête que vous contrôlez : un envoi en chunked sans `Content-Length` est donc vérifié de la même manière. Un dépassement renvoie `413 Payload Too Large` avant tout travail de conversion et avant la facturation de la moindre op :

```json
{
    "detail": {
        "error": "File too large",
        "file_size": 10485760,
        "max_size": 5242880,
        "tier": "free",
        "key_type": "private"
    }
}
```

`file_size` et `max_size` sont tous deux en octets. `tier` est le slug du plan (`free`, `starter`, `pro`, `business`, `enterprise`), pas le nom affiché que vous voyez sur la page tarifaire : un projet Studio rapporte donc `"tier": "pro"`. `key_type` vaut `private`, `public` ou `dashboard`, avec repli sur `unknown`.

<div class="alert alert-warning">
<strong>5 MB, ça part vite.</strong> Sur le plan Founding, un PDF scanné de 40 pages ou une présentation avec quelques photos pleine page dépasse généralement déjà la limite. Il n'existe aucune voie de téléversement fragmenté ou reprenable : le remède, c'est un fichier plus petit ou un plan plus grand.
</div>

Franchir le contrôle de taille n'est pas le dernier obstacle. Une allocation mensuelle épuisée répond `402 Payment Required`, et trop de requêtes dans une fenêtre répondent `429`, deux cas décrits dans [Limites de débit et quotas](/fr/docs/reference/rate-limits.md).

---

## Type de contenu et octets magiques

Les téléversements passent deux contrôles, dans cet ordre.

**1. La liste d'extensions autorisées.** Chaque endpoint déclare les extensions qu'il accepte. Une non-correspondance donne `400 Bad Request` :

```json
{
    "detail": "Invalid file format '.pdf' for png-to-jpeg. Allowed: .png"
}
```

**2. Le reniflage des octets magiques.** Les premiers octets du fichier sont comparés au groupe que revendique son extension. Une non-correspondance à forte confiance donne elle aussi `400` :

```json
{
    "detail": "File content does not match the 'png-to-jpeg' input type."
}
```

C'est ce que vous obtenez quand vous renommez un PDF en `.png` et le téléversez : les octets commencent par `%PDF-`, l'extension dit PNG, et les deux se contredisent. Ce contrôle existe parce que c'est l'extension qui achemine votre requête. Sans lui, des octets PDF arrivent dans un décodeur d'images et vous récoltez un échec opaque au fond du convertisseur au lieu d'un 400 clair dès la porte, et un fichier délibérément mal étiqueté se retrouve confié à un parseur qui n'aurait jamais dû le voir.

Deux choses que ce contrôle ne fait **pas**, toutes deux bonnes à connaître :

- Le `Content-Type` de la partie n'est jamais inspecté. Il n'y a aucune liste MIME autorisée nulle part dans le chemin de téléversement : `application/octet-stream` passe donc sans problème. L'extension du nom de fichier est la seule chose qui achemine la requête, ce qui explique pourquoi l'envoi d'un fichier sans extension échoue.
- Les formats texte n'ont pas de signature fiable et sautent complètement le reniflage : `.json`, `.csv`, `.xml`, `.yaml`, `.toml`, `.md`, `.html`, `.svg`, `.txt`. Un fichier `.json` qui contient en réalité du CSV est accepté à ce stade et échoue plus tard, dans le parseur.

Les signatures binaires reconnues sont PNG, JPEG, GIF, WebP, HEIC/HEIF, PDF et le groupe bureautique (un conteneur ZIP pour `.docx`/`.xlsx`/`.pptx`/ODF/EPUB, ou l'ancien OLE2 pour `.doc`/`.xls`/`.ppt`). Le reniflage est délibérément permissif : les octets qu'il ne reconnaît pas passent plutôt que d'être bloqués.

---

## Fichiers volumineux : la checklist

1. **Vérifiez le plafond d'abord.** Un 413 est bon marché pour l'API et coûteux pour vous, car vous avez téléversé le corps entier pour l'obtenir.
2. **Attendez-vous à un téléversement synchrone.** `async_mode` n'existe que sur `url-to-pdf`, `url-to-screenshot` et `url-to-markdown`. Les endpoints de téléversement l'ignorent et convertissent toujours à l'intérieur de la requête. Voir [tâches synchrones et asynchrones](/fr/docs/concepts/sync-and-async.md).
3. **Envoyez un `job_id` que vous avez généré.** Si un proxy placé devant vous coupe la connexion avant la fin de la conversion, le travail se termine quand même. Interrogez `GET /v1/convert/status/{job_id}` et vous obtenez `{"status": "processing"}`, puis soit `{"status": "success", "presigned_url": ..., "object_key": ...}`, soit `{"status": "failed", "error": ...}`. Réutiliser votre propre ID relance cette tâche ; réutiliser l'ID d'un autre projet renvoie `409`.
4. **Prévoyez les timeouts.** Une requête est plafonnée à 300 secondes de bout en bout, après quoi vous obtenez `504` avec `{"error": "Request timeout"}`. Les conversions bureautiques adossées à LibreOffice ont leur propre plafond de 120 secondes, remonté lui aussi en `504`.
5. **Gérez le `503` avec `Retry-After: 10`.** Les conversions de fichiers s'exécutent derrière un contrôle d'admission doté d'une file d'attente bornée. Quand la file est pleine, la requête est refusée immédiatement plutôt que de patienter : réessayez donc après l'intervalle indiqué dans l'en-tête.
6. **Pour beaucoup de documents, changez d'endpoint.** `POST /v2/ingest/files` accepte jusqu'à 200 fichiers, renvoie `202` immédiatement avec un `job_id`, et accepte un `webhook_url` pour que vous n'ayez jamais à interroger. Voir [webhooks](/fr/docs/guides/webhooks.md).
7. **Téléchargez rapidement.** Les liens de sortie sont signés et expirent. Si le vôtre a expiré, relisez l'endpoint de statut : chaque interrogation émet un nouveau lien vers le même objet stocké.

---

## Questions fréquentes

### Quel nom de champ l'API EnConvert attend-elle pour un téléversement de fichier ?

`file`, envoyé en `multipart/form-data`, un fichier par requête, sur chaque endpoint de conversion V1 qui accepte un téléversement. L'exception est `POST /v2/ingest/files`, qui utilise `files` et accepte jusqu'à 200 fichiers par job.

### EnConvert peut-il télécharger le fichier depuis une URL au lieu que je le téléverse ?

Uniquement sur les endpoints URL (`url-to-pdf`, `url-to-screenshot`, `url-to-markdown`, `website-to-pdf`, `website-to-screenshot`) et les endpoints V2 `/v2/perceive` et `/v2/ingest`. Les endpoints de conversion par téléversement n'ont pas de paramètre `url`. Toute URL que vous passez doit être joignable publiquement : les adresses privées, loopback, link-local et de métadonnées cloud sont rejetées avec `400`.

### Pourquoi mon téléversement a-t-il renvoyé 413 Payload Too Large ?

Le fichier dépassait le plafond par fichier de votre plan, soit 5 MB sur Founding, 15 MB sur Indie, 50 MB sur Studio et 150 MB sur Production. Le corps de la réponse porte un objet `detail` avec `error`, `file_size`, `max_size`, `tier` et `key_type` pour que vous puissiez montrer à l'appelant les chiffres exacts.

### Pourquoi mon téléversement PNG échoue-t-il avec « File content does not match » ?

Les premiers octets du fichier appartiennent à un format différent de celui que revendique son extension, par exemple un PDF renommé en `.png`. Envoyez le fichier sous sa véritable extension. Les formats texte comme `.json` et `.csv` ne sont jamais vérifiés octet par octet, cette erreur n'apparaît donc que pour les types binaires.

### Puis-je téléverser un fichier volumineux de manière asynchrone ?

Pas sur les endpoints de téléversement V1 : ils convertissent toujours dans la requête. Envoyez un `job_id` généré par le client et interrogez `GET /v1/convert/status/{job_id}` pour survivre à une connexion coupée, ou utilisez `POST /v2/ingest/files`, asynchrone par conception et capable d'appeler un webhook à la fin du job.
