---
seo_title: SDK Java de conversion de fichiers (Maven Central) | EnConvert
meta_desc: SDK Java officiel EnConvert pour Java 17+. Une seule dépendance Maven ou Gradle pour convertir des fichiers et pour perceive, discover, distill, ingest et watch.
keywords: sdk java conversion de fichiers, convertir des fichiers en java, client api conversion maven, bibliothèque gradle conversion de fichiers, url vers pdf java, docx vers pdf java, html vers pdf java, heic vers webp java, api scraping web java, page web vers markdown java, pipeline ingestion rag java, enconvert java sdk
---

# SDK Java de conversion de fichiers

`com.enconvert:enconvert-sdk` est le client Java officiel de l'API EnConvert. Une seule dépendance Maven ou Gradle vous donne la conversion de fichiers (URL vers PDF, DOCX vers PDF, HEIC vers WebP, n'importe quoi vers Markdown) ainsi que la surface de web intelligence V2 : perceive, discover, lookup, distill, ingest et watch. Il cible Java 17 et versions ultérieures, s'appuie sur le `java.net.http.HttpClient` intégré au JDK, et n'embarque que Gson comme dépendance tierce. Chaque appel est une méthode bloquante ordinaire qui renvoie un record typé, et les conversions longues récupèrent de façon transparente les timeouts du reverse proxy en interrogeant le statut du job.

<div class="alert alert-info">
<strong>Maven Central :</strong> <code>com.enconvert:enconvert-sdk:0.0.1</code> · <strong>Source :</strong> <a href="https://github.com/conversionapi/java-sdk">conversionapi/java-sdk</a> · <strong>Java :</strong> 17+ · <strong>Dépendances :</strong> Gson uniquement
</div>

---

## Installation

```groovy
// build.gradle
dependencies {
    implementation 'com.enconvert:enconvert-sdk:0.0.1'
}
```

```kotlin
// build.gradle.kts
dependencies {
    implementation("com.enconvert:enconvert-sdk:0.0.1")
}
```

```xml
<!-- pom.xml -->
<dependency>
    <groupId>com.enconvert</groupId>
    <artifactId>enconvert-sdk</artifactId>
    <version>0.0.1</version>
</dependency>
```

Le HTTP est pris en charge par `java.net.http.HttpClient`, fourni par le JDK. Le seul artefact tiers embarqué est [Gson](https://github.com/google/gson) pour le JSON, déclaré en dépendance `api` afin d'être visible sur votre classpath de compilation.

---

## Démarrage rapide

```java
import com.enconvert.Enconvert;
import com.enconvert.model.ConversionResult;
import com.enconvert.model.UrlToPdfOptions;
import com.enconvert.model.v2.PerceiveOptions;
import com.enconvert.model.v2.PerceiveResult;

import java.util.List;

Enconvert client = new Enconvert(System.getenv("ENCONVERT_API_KEY"));

ConversionResult pdf = client.convertUrlToPdf("https://example.com",
        UrlToPdfOptions.builder().saveTo("page.pdf").build());
System.out.println(pdf.presignedUrl());

// Lire une page comme votre agent devrait le faire, avec un score de qualité attaché.
PerceiveResult page = client.v2.perceive("https://example.com",
        PerceiveOptions.builder().outputs(List.of("markdown", "structured")).build());
System.out.println(page.outputs().get("markdown").url());
System.out.println(page.renderQuality());   // par ex. 0.93
```

Chaque classe d'options est un builder immuable et chaque réponse est un `record` Java : les accesseurs se lisent donc comme `pdf.presignedUrl()` et `page.renderQuality()`. Le client détient un seul `HttpClient` partagé et aucun état mutable par requête, si bien qu'une instance unique peut servir de singleton ou de bean Spring partagé entre plusieurs threads. Les extraits ci-dessous omettent les imports : les types d'options et de réponses vivent dans `com.enconvert.model` (conversion) et `com.enconvert.model.v2` (web intelligence), les exceptions dans `com.enconvert.exceptions`.

---

## Ce que le client expose

`Enconvert` porte directement la surface de conversion. La surface de web intelligence vit sur le champ public final `client.v2`, une instance de `EnconvertV2`.

| Groupe | Méthodes | Renvoie |
|-------|---------|---------|
| URL unique | `convertUrlToPdf`, `convertUrlToScreenshot`, `convertUrlToMarkdown` | `ConversionResult` |
| Envoi de fichier | `convertImage`, `convertDocument`, `convertToMarkdown`, `convertToPdf` | `ConversionResult` |
| Site entier | `convertWebsiteToPdf`, `convertWebsiteToScreenshot` | `BatchSubmission` |
| Statut | `getJobStatus`, `getBatchStatus`, `waitForBatch` | `JobStatus`, `BatchStatus` |
| `v2` perceive | `perceive`, `perceiveDirect`, `getPerceiveOperation`, `perceiveBatch`, `getPerceiveBatch`, `downloadPerceiveArtifact` | `PerceiveResult`, `PerceiveDirectResult`, `PerceiveBatchResult` |
| `v2` discover | `discover` | `DiscoverResult` |
| `v2` lookup | `lookup` | `LookupResult` |
| `v2` distill | `distill` | `DistillResult` |
| `v2` ingest | `ingest`, `ingestFiles`, `getIngestJob`, `listIngestJobs`, `cancelIngestJob`, `retryIngestWebhook`, `getWebhookSecret`, `rotateWebhookSecret` | `IngestJob`, `IngestJobList`, `WebhookRetryResult`, `WebhookSecret` |
| `v2` watch | `createWatcher`, `getWatcher`, `listWatchers`, `getWatcherSnapshots`, `updateWatcher`, `deleteWatcher` | `Watcher`, `WatcherList`, `WatcherSnapshotList` |

La plupart des méthodes possèdent une surcharge courte sans argument d'options : `client.v2.perceive(url)` et `client.convertUrlToPdf(url)` compilent donc tous les deux. `convertImage`, `distill` et `ingest` font exception : chacune exige toujours son objet d'options, parce que le format cible, le schéma et la source sont respectivement obligatoires.

---

## Conversion de fichiers

Les endpoints de conversion couvrent 43 paires `{input}-to-{output}` implémentées, deux endpoints à détection automatique (`anything-to-markdown` et `anything-to-pdf`), et les endpoints de rendu navigateur. La référence complète des paramètres se trouve dans [Paramètres et options](/fr/docs/parameters-options).

### convertUrlToPdf

Rend n'importe quelle URL publique en PDF.

```java
ConversionResult result = client.convertUrlToPdf("https://example.com",
        UrlToPdfOptions.builder()
                .pdfOptions(PdfOptions.builder().pageSize("A4").orientation("landscape").build())
                .singlePage(false)
                .viewportWidth(1440)
                .saveTo("report.pdf")
                .build());
```

| Option | Type | Par défaut | Description |
|--------|------|---------|-------------|
| `saveTo` | `String` | aucun | Chemin local où écrire le PDF. Les répertoires parents sont créés pour vous. |
| `singlePage` | `boolean` | `true` | `true` produit une seule page continue. `false` pagine en utilisant `pdfOptions.pageSize`. |
| `pdfOptions` | `PdfOptions` | aucun | Taille de page, orientation, marges, échelle, niveaux de gris, en-tête, pied de page. Voir [Options PDF](#options-pdf). |
| `viewportWidth` | `int` | `1920` | Largeur de la fenêtre d'affichage du navigateur, en pixels. |
| `viewportHeight` | `int` | `1080` | Hauteur de la fenêtre d'affichage du navigateur, en pixels. |
| `loadMedia`, `enableScroll` | `boolean` | `true` | Attend les images et les vidéos avant la capture, et fait défiler la page de haut en bas pour déclencher les chargements différés. |
| `outputFilename` | `String` | auto | Remplace le nom de fichier généré. |
| `auth`, `cookies`, `headers` | `HttpBasicAuth`, `List<BrowserCookie>`, `Map<String, String>` | aucun | Identifiants, cookies injectés et en-têtes de requête supplémentaires pour les pages derrière une authentification. |

### convertUrlToScreenshot

Capture un PNG de n'importe quelle URL. Mêmes options de fenêtre d'affichage, de médias, de défilement, de nom de fichier, d'authentification, de cookies et d'en-têtes que `convertUrlToPdf`, à l'exception de `singlePage` et `pdfOptions`.

```java
client.convertUrlToScreenshot("https://example.com",
        UrlToScreenshotOptions.builder().viewportWidth(1440).saveTo("screenshot.png").build());
```

### convertUrlToMarkdown

Extrait du Markdown GitHub-Flavored propre à partir d'une URL. La navigation, les pieds de page, les publicités et les scripts sont supprimés, le corps principal de l'article est conservé, et un frontmatter YAML (title, description, url, links, images) est ajouté en tête. Même jeu d'options que `convertUrlToScreenshot`.

```java
client.convertUrlToMarkdown("https://example.com/article",
        UrlToMarkdownOptions.builder().saveTo("article.md").build());
```

### convertImage

Convertit entre `jpeg`, `png`, `svg`, `heic` et `webp`, ou rastérise un PDF en JPEG.

```java
// Depuis un chemin sur le disque
client.convertImage(Path.of("photo.heic"),
        ConvertImageOptions.builder("webp").saveTo("photo.webp").build());

// Rastériser un PDF
client.convertImage(Path.of("scan.pdf"),
        ConvertImageOptions.builder("jpeg").saveTo("scan.jpeg").build());

// Depuis des octets en mémoire, avec un nom de fichier explicite
byte[] bytes = Files.readAllBytes(Path.of("photo.heic"));
client.convertImage(new FileInput(bytes, "photo.heic"),
        ConvertImageOptions.builder("webp").build());
```

Trois surcharges d'entrée existent sur chaque méthode de fichier : `java.nio.file.Path` (lecture depuis le disque), `byte[]` brut (le nom de fichier vaut alors `upload.bin`), et `com.enconvert.FileInput` lorsque vous devez associer des octets en mémoire à un vrai nom de fichier. Le format d'entrée est déduit de l'extension ; le format de sortie est obligatoire.

| Option | Type | Requis | Description |
|--------|------|----------|-------------|
| `outputFormat` | `String` | Oui | Passé à `ConvertImageOptions.builder(outputFormat)`. Une valeur parmi `jpeg`, `png`, `svg`, `heic`, `webp`. Les alias comme `jpg` sont normalisés. |
| `saveTo` | `String` | non | Chemin local où écrire le résultat. |
| `outputFilename` | `String` | non | Remplace le nom de fichier généré. |

### convertDocument

Convertit des documents et des formats de données structurées. Le format de sortie vaut `pdf` par défaut.

```java
// docx vers pdf
client.convertDocument(Path.of("report.docx"),
        ConvertDocumentOptions.builder().saveTo("report.pdf").build());

// json vers yaml
client.convertDocument(Path.of("data.json"),
        ConvertDocumentOptions.builder().outputFormat("yaml").saveTo("data.yaml").build());

// markdown vers pdf avec mise en page
client.convertDocument(Path.of("README.md"),
        ConvertDocumentOptions.builder()
                .outputFormat("pdf")
                .pdfOptions(PdfOptions.builder()
                        .pageSize("A4")
                        .margins(new PdfMargins(20.0, 20.0, 25.0, 25.0))
                        .build())
                .saveTo("readme.pdf")
                .build());
```

**Extensions d'entrée reconnues :** `.doc`, `.docx`, `.xls`, `.xlsx`, `.ppt`, `.pptx`, `.html`, `.htm`, `.odt`, `.ods`, `.odp`, `.ots`, `.pages`, `.numbers`, `.md`, `.markdown`, `.csv`, `.json`, `.xml`, `.yaml`, `.yml`, `.toml`.

L'EPUB n'a pas de paire de conversion documentaire dédiée. Faites plutôt passer les fichiers `.epub` par [`convertToPdf`](#converttopdf) ou [`convertToMarkdown`](#converttomarkdown).

| Option | Type | Par défaut | Description |
|--------|------|---------|-------------|
| `outputFormat` | `String` | `"pdf"` | Format cible. |
| `saveTo` | `String` | aucun | Chemin local où écrire le résultat. |
| `outputFilename` | `String` | aucun | Remplace le nom de fichier généré. |
| `pdfOptions` | `PdfOptions` | aucun | Mise en page, prise en compte lorsque la sortie est un PDF. |

### Conversions prises en charge

`convertImage` et `convertDocument` valident la paire `{input}-to-{output}` contre les endpoints que l'API implémente réellement. Une paire non prise en charge lève immédiatement une `IllegalArgumentException` en listant les sorties valides pour cette entrée, plutôt que de payer un aller-retour réseau pour une requête vouée à l'échec.

| Entrée | Sorties |
|-------|---------|
| `json` | `csv`, `toml`, `xml`, `yaml` |
| `xml` | `csv`, `json` |
| `yaml` | `json` |
| `csv` | `json`, `xml` |
| `toml` | `json` |
| `markdown` | `html`, `pdf` |
| `html` | `pdf` |
| `doc`, `excel`, `ppt`, `odt`, `ods`, `odp`, `ots`, `pages`, `numbers` | `pdf` |
| `jpeg`, `png`, `svg`, `heic`, `webp` | les uns vers les autres, les 20 paires |
| `pdf` | `jpeg` |

Le même tableau est interrogeable à l'exécution via `com.enconvert.Formats` : `Formats.validOutputsFor("json")` renvoie `[csv, toml, xml, yaml]`, `Formats.validOutputsFor("pdf")` renvoie `[jpeg]`, et `Formats.IMPLEMENTED_CONVERSIONS` contient les 43 noms d'endpoints.

### convertToMarkdown

Envoyez n'importe quel document pris en charge vers un endpoint à détection automatique et récupérez du Markdown propre. La hiérarchie des titres survit à la conversion, ce qui en fait une première étape naturelle pour un pipeline RAG.

```java
client.convertToMarkdown(Path.of("handbook.docx"),
        ConvertToMarkdownOptions.builder().saveTo("handbook.md").build());
```

Accepte les formats PDF, DOCX, PPTX, XLSX, CSV, HTML, EPUB, TXT et MD, ainsi que les formats bureautiques hérités et ODF. Le format est détecté côté serveur, il n'y a donc aucune vérification d'extension côté client : tout fichier est envoyé tel quel. Les images ne sont pas prises en charge et sont rejetées avec un `400`. Les seules options sont `saveTo` et `outputFilename`.

### convertToPdf

L'autre endpoint à détection automatique : presque n'importe quoi vers PDF.

```java
// pptx vers pdf
client.convertToPdf(Path.of("slides.pptx"),
        ConvertToPdfOptions.builder().saveTo("slides.pdf").build());

// pdf transmis tel quel, converti en niveaux de gris
client.convertToPdf(Path.of("scan.pdf"),
        ConvertToPdfOptions.builder()
                .pdfOptions(PdfOptions.builder().grayscale(true).build())
                .saveTo("scan-gray.pdf")
                .build());
```

Accepte les formats bureautiques, ODF, Pages, Numbers, RTF, CSV, HTML, Markdown, texte brut, images matricielles, SVG, EPUB, ainsi qu'un PDF existant transmis tel quel. L'EPUB est traité ici parce qu'il n'a pas de paire de conversion documentaire dédiée. Les options sont `saveTo`, `outputFilename` et `pdfOptions`.

<div class="alert alert-warning">
<strong>Seul <code>grayscale</code> est pris en compte sur cet endpoint.</strong> La géométrie de page (taille de page, largeur et hauteur, orientation, marges, échelle, en-tête, pied de page) est ignorée par <code>anything-to-pdf</code>. Lorsque vous avez besoin d'une mise en page complète, passez plutôt par <code>convertDocument</code> ou <code>convertUrlToPdf</code>.
</div>

### Conversion d'un site entier

`convertWebsiteToPdf` et `convertWebsiteToScreenshot` découvrent chaque page d'un site, convertissent chacune d'elles en arrière-plan, et regroupent les résultats dans une seule archive ZIP. Les deux sont asynchrones et renvoient un `BatchSubmission`. Les deux exigent une clé API privée.

```java
BatchSubmission batch = client.convertWebsiteToPdf("https://example.com",
        WebsiteToPdfOptions.builder()
                .crawlMode("sitemap")                      // "auto" (par défaut), "sitemap", "full"
                .excludePatterns(List.of("/blog/tag/"))    // mode full crawl uniquement
                .notificationEmail("ops@example.com")
                .build());

System.out.println(batch.batchId() + " " + batch.urlCount() + " " + batch.discoveryMethod());

// Bloque jusqu'à ce que le lot quitte l'état "processing", puis enregistre le ZIP
BatchStatus status = client.waitForBatch(batch.batchId(),
        WaitForBatchOptions.builder().saveTo("site.zip").build());
System.out.println(status.completed() + " of " + status.total() + " pages converted");
```

`convertWebsiteToScreenshot` fonctionne à l'identique et produit un ZIP de PNG. `waitForBatch` interroge toutes les 5 secondes par défaut, abandonne au bout de 30 minutes, et accepte `intervalMs`, `timeoutMs` et `saveTo`. En cas de timeout, elle lève une `ApiException` avec le statut `504`.

### Interroger le statut vous-même

```java
JobStatus job = client.getJobStatus("job_abc123");
if ("success".equals(job.status())) System.out.println(job.presignedUrl());
if ("failed".equals(job.status())) System.err.println(job.error());
BatchStatus batch = client.getBatchStatus("bat_abc123");
if (!"processing".equals(batch.status())) System.out.println(batch.zipDownloadUrl());
```

---

## Web intelligence (V2)

Tout ce qui se trouve sous `client.v2` transforme des pages web en données prêtes pour un agent. Chaque lecture porte `renderQuality`, un score de 0.0 à 1.0 qui indique à quel point la page s'est réellement rendue proprement. Une page de challenge, un mur de cookies ou une coquille SPA vide reviennent avec un score bas et des `warnings()` et `deductions()` renseignés, au lieu d'être présentés comme du vrai contenu : une mauvaise lecture n'entre donc jamais silencieusement dans le contexte de votre agent. Le contenu est toujours renvoyé, il est simplement signalé. Le modèle est présenté en détail dans la [vue d'ensemble V2](/fr/docs/v2-overview).

### Perceive

Rend une URL vers les artefacts que vous demandez. Référence de l'endpoint : [Perceive](/fr/docs/v2-perceive).

```java
PerceiveResult page = client.v2.perceive("https://example.com",
        PerceiveOptions.builder()
                .outputs(List.of("markdown", "screenshot", "structured"))
                .extract(List.of("tables", "metadata"))
                .waitFor("css:main")
                .build());

System.out.println(page.renderQuality());                 // de 0.0 à 1.0
System.out.println(page.statusCode() + " " + page.deductions());  // par ex. 200 {http_error=0.7}
System.out.println(page.outputs().get("markdown").url()); // URL signée, 15 minutes
System.out.println(page.structured());                    // forme définie par l'appelant
```

| Option | Type | Par défaut | Description |
|--------|------|---------|-------------|
| `outputs` | `List<String>` | `["markdown", "structured"]` | Une valeur parmi `markdown`, `html_cleaned`, `html_raw`, `screenshot`, `screenshot_full_page`, `pdf`, `links`, `images`, `structured`. |
| `extract` | `List<String>` | aucun | Cibles heuristiques : `tables`, `prices`, `contacts`, `metadata`, `main_content`, `headings`, `structured_data`, `technologies`, `all`. |
| `schema` | `Map<String, Object>` | aucun | Schéma JSON pour l'extraction structurée. |
| `waitFor`, `waitTimeoutMs` | `String`, `int` | aucun, `30000` | Un sélecteur CSS, éventuellement préfixé par `css:`, ou `js:<expr>` à attendre, avec un budget de 0 à 60000 ms. |
| `jsCode` | `String` | aucun | JavaScript exécuté après la navigation, 20000 caractères maximum. |
| `viewport`, `mobile` | `PerceiveViewport`, `boolean` | 1920 x 1080, `false` | Largeur de 320 à 3840, hauteur de 240 à 2160, ou émulation mobile. |
| `onlyMainContent` | `boolean` | `true` | Supprime la navigation, l'en-tête, le pied de page et les bandeaux cookies de l'artefact Markdown et de l'extrait `main_content`. |
| `cacheMode` | `String` | `"enabled"` | `enabled` réutilise un cache d'une heure, `bypass` le contourne, `refresh` force un nouveau rendu. |
| `blockResources` | `List<String>` | aucun | Types de ressources que le navigateur ne doit pas charger, par exemple `image`, `font`, `script`. |
| `pdfOptions` | `PdfOptions` | aucun | Utile uniquement lorsque `outputs` contient `pdf`. |
| `headers`, `cookies`, `auth` | `Map`, `List<BrowserCookie>`, `HttpBasicAuth` | aucun | En-têtes de requête, cookies injectés, identifiants HTTP Basic. |
| `respectRobots` | `boolean` | aucun | Respecte les règles robots du site. |

<div class="alert alert-warning">
<strong>Pas encore branché.</strong> <code>proxyUrl</code>, <code>geolocation</code> et <code>actionChain</code> existent sur le builder mais ne sont pas disponibles côté serveur et sont pour l'instant rejetés avec un <code>422</code>.
</div>

Les URL d'artefacts sont signées pour 15 minutes et resignées à chaque lecture de l'opération : `client.v2.getPerceiveOperation(page.operationId())` vous rend donc des liens frais. Traitez jusqu'à 1000 URL par lot avec un seul bloc d'options partagé : les petits lots se terminent en ligne, les plus gros reviennent avec le statut `queued`, il faut donc les interroger.

```java
PerceiveBatchResult batch = client.v2.perceiveBatch(
        List.of("https://a.example.com", "https://b.example.com"),
        PerceiveBatchOptions.builder()
                .outputs(List.of("markdown"))
                .outputMode("zip")          // "manifest" (par défaut) ou "zip"
                .build());

PerceiveBatchResult done = client.v2.getPerceiveBatch(batch.jobId());
System.out.println(done.completed() + "/" + done.total());
```

Évitez complètement l'aller-retour par URL signée avec `perceiveDirect`, qui renvoie directement les octets de l'artefact. Cette méthode exige exactement une sortie produisant un artefact (n'importe laquelle sauf `structured`) et lève une `IllegalArgumentException` avant l'envoi si vous en demandez plus ou moins :

```java
PerceiveDirectResult direct = client.v2.perceiveDirect("https://example.com",
        PerceiveOptions.builder().outputs(List.of("pdf")).build());

Files.write(Path.of(direct.filename()), direct.content());
System.out.println(direct.renderQuality() + " " + direct.contentType());

// Retélécharger un artefact stocké d'une opération antérieure
PerceiveDirectResult stored = client.v2.downloadPerceiveArtifact(page.operationId(), "markdown");
```

`downloadPerceiveArtifact` accepte un nom de sortie null ou omis lorsque l'opération n'a produit qu'un seul artefact ; sinon elle renvoie un `400` listant les sorties disponibles. Une fois l'artefact stocké arrivé au bout de sa rétention, elle renvoie un `410`.

### Discover

Énumère les URL d'un site sans rien rendre. Aucun navigateur n'intervient, c'est donc rapide. Référence de l'endpoint : [Discover](/fr/docs/v2-discover).

```java
DiscoverResult found = client.v2.discover("https://example.com",
        DiscoverOptions.builder()
                .mode("hybrid")                        // "sitemap", "crawl", "hybrid"
                .maxUrls(200)
                .maxDepth(3)
                .excludePatterns(List.of("/tag/"))
                .build());

System.out.println(found.total() + " urls, truncated=" + found.truncated());
found.urls().forEach(System.out::println);
```

| Option | Type | Par défaut | Plage |
|--------|------|---------|-------|
| `mode` | `String` | `"hybrid"` | `sitemap`, `crawl`, `hybrid` |
| `maxUrls` | `int` | `100` | de 1 à 1000 |
| `maxDepth` | `int` | `2` | de 1 à 5 |
| `includePatterns`, `excludePatterns` | `List<String>` | aucun | Liste blanche et liste noire d'expressions régulières, 50 entrées maximum chacune. La liste noire est appliquée en second. |
| `sameDomainOnly`, `respectRobots` | `boolean` | `true`, aucun | Rester sur le domaine de départ, et respecter les règles robots du site. |

### Lookup

Recherche web catégorisée, avec rendu automatique facultatif des meilleurs résultats. Référence de l'endpoint : [Lookup](/fr/docs/v2-lookup).

```java
LookupResult search = client.v2.lookup("best static site generators",
        LookupOptions.builder()
                .category("web")        // web, news, images, scholar, patents, maps
                .numResults(10)
                .country("us")
                .timeFilter("month")    // hour, day, week, month, year
                .perceiveTop(3)         // rend automatiquement les 3 premiers résultats
                .build());

search.results().forEach(hit -> {
    System.out.println(hit.position() + " " + hit.title() + " " + hit.url());
    if (hit.perceive() != null) System.out.println("  quality " + hit.perceive().renderQuality());
});
```

Avec `perceiveTop` supérieur à 0 (de 0 à 10, 0 par défaut), les N premières URL de résultats sont rendues via perceive et chaque résultat porte son `PerceiveResult` complet en ligne sur `hit.perceive()`. `numResults` va de 1 à 100 et vaut 10 par défaut ; `page` va de 1 à 10.

### Distill

Extraction structurée pilotée par schéma, sur une ou plusieurs pages. Référence de l'endpoint : [Distill](/fr/docs/v2-distill).

```java
DistillResult extraction = client.v2.distill(
        DistillOptions.builder(Map.of("products", "list of product names with their listed price"))
                .urls(List.of("https://example.com/catalog"))
                .cssSchema(CssSchema.builder(".product-card", List.of(
                                CssField.builder("name", "text").selector("h3").build(),
                                CssField.builder("price", "text").selector(".price").build()))
                        .targetField("products")
                        .build())
                .build());

extraction.results().forEach(item ->
        System.out.println(item.data() + " via " + item.extractionTier()));
```

Le `cssSchema` facultatif exécute d'abord une passe CSS gratuite ; seuls les champs auxquels elle ne peut pas répondre escaladent vers le niveau LLM, et `item.extractionTier()` indique quel chemin a produit l'enregistrement (`css`, `llm`, `mixed` ou `none`). Vous pouvez aussi découvrir les URL au préalable au lieu de les lister :

```java
client.v2.distill(
        DistillOptions.builder(Map.of("title", "page title", "summary", "one-line summary"))
                .discoverFrom(new DistillDiscoverFrom("https://example.com", "sitemap", 10))
                .build());
```

Exactement l'un de `urls` (50 maximum) et `discoverFrom` doit être défini, et `schema` est exigé par la fabrique du builder. Les deux règles sont vérifiées côté client et lèvent une `IllegalArgumentException` avant qu'aucune requête ne parte.

### Ingest

Transforme un site entier, ou un ensemble de documents envoyés, en JSONL découpé et prêt pour le RAG, à travers un seul pipeline. Ingest est toujours asynchrone. Référence de l'endpoint : [Ingest](/fr/docs/v2-ingest).

```java
// Depuis un site
IngestJob job = client.v2.ingest(IngestOptions.builder()
        .mode("sitemap")                                  // "urls" (par défaut), "sitemap", "crawl"
        .url("https://docs.example.com")
        .maxPages(100)
        .chunk(new IngestChunkOptions(512, 1))            // maxWords, sentenceOverlap
        .webhookUrl("https://my.app/hooks/enconvert")
        .build());

// Ou depuis des fichiers envoyés
IngestJob fileJob = client.v2.ingestFiles(
        List.of(new FileInput(Files.readAllBytes(Path.of("handbook.pdf")), "handbook.pdf"),
                new FileInput(Files.readAllBytes(Path.of("notes.docx")), "notes.docx")),
        IngestFilesOptions.builder().chunk(new IngestChunkOptions(512, 1)).build());

// Interroger jusqu'à obtenir le JSONL
IngestJob status = client.v2.getIngestJob(job.jobId());
if ("completed".equals(status.status())) {
    System.out.println(status.outputUrl() + " (" + status.totalChunks() + " chunks)");
}

client.v2.listIngestJobs(V2ListOptions.builder().limit(20).build());
client.v2.cancelIngestJob(job.jobId());   // idempotent
```

`ingestFiles` accepte les formats PDF, DOCX, PPTX, XLSX, CSV, HTML, EPUB, TXT et MD, ainsi que les formats bureautiques hérités et ODF. Le découpage vaut par défaut 512 mots (de 32 à 4000) avec 1 phrase de chevauchement (de 0 à 10). `mode` vaut `urls` par défaut, ce qui exige une liste `urls` non vide et interdit `url` ; tous les autres modes exigent une `url` de départ et interdisent `urls`. Le SDK vérifie cet appariement avant l'envoi.

Les webhooks de fin sont signés en HMAC. Récupérez le secret et les noms d'en-têtes dont vous avez besoin pour vérifier une livraison, faites-le tourner en cas de fuite, et relancez une livraison que votre endpoint aurait manquée :

```java
WebhookSecret secret = client.v2.getWebhookSecret();
System.out.println(secret.signatureHeader() + " " + secret.signatureScheme());

client.v2.rotateWebhookSecret();          // les anciennes signatures cessent aussitôt d'être valides

WebhookRetryResult retry = client.v2.retryIngestWebhook(job.jobId());
System.out.println(retry.delivered() + " after " + retry.attempts() + " attempts");
```

### Watch

Surveillance récurrente des changements sur une URL, avec notification par e-mail et par webhook. Référence de l'endpoint : [Watch](/fr/docs/v2-watch).

```java
Watcher watcher = client.v2.createWatcher("https://example.com/pricing",
        WatchCreateOptions.builder()
                .frequencyMinutes(60)      // de 60 à 43200, plancher horaire
                .diffMode("auto")          // auto, text, structured, tables, metadata
                .webhookUrl("https://my.app/hooks/changes")
                .notifyEmail(true)
                .build());

WatcherSnapshotList history = client.v2.getWatcherSnapshots(watcher.watcherId(),
        SnapshotListOptions.builder().limit(10).build());
history.snapshots().forEach(s ->
        System.out.println(s.checkedAt() + " changed=" + s.hasChanges()
                + " similarity=" + s.similarity()));

client.v2.updateWatcher(watcher.watcherId(), WatcherUpdate.builder().status("paused").build());
client.v2.updateWatcher(watcher.watcherId(), WatcherUpdate.builder().webhookUrl("").build());
client.v2.deleteWatcher(watcher.watcherId());   // suppression logique, idempotente
```

`listWatchers()` et `getWatcher(watcherId)` permettent de les relire. `updateWatcher` exige au moins un champ et lève sinon une `IllegalArgumentException`. Une chaîne vide explicite pour `webhookUrl` efface le webhook, tandis que la laisser à null signifie « aucun changement ». `deleteWatcher` est une suppression logique : elle renvoie le watcher marqué avec le statut `deleted`, et un watcher supprimé se relit ensuite en `404`.

<div class="alert alert-warning">
<strong>Les diffs de snapshots contiennent du contenu de page non fiable.</strong> <code>WatcherSnapshot.changes()</code> est du texte brut repris de la page surveillée. Échappez-le avant de l'afficher dans un tableau de bord, un e-mail ou un message de chat.
</div>

---

## Options PDF

`PdfOptions` est partagé par `convertUrlToPdf`, `convertWebsiteToPdf`, `convertDocument`, `convertToPdf` et `PerceiveOptions.pdfOptions`.

```java
client.convertUrlToPdf("https://internal.example.com/report",
        UrlToPdfOptions.builder()
                .pdfOptions(PdfOptions.builder()
                        .pageSize("A4")
                        .orientation("landscape")
                        .margins(new PdfMargins(10.0, 10.0, 15.0, 15.0))
                        .scale(0.9)
                        .header(new PdfHeaderFooter("Quarterly Report", 15.0))
                        .footer(new PdfHeaderFooter("Confidential", 12.0))
                        .build())
                .auth(new HttpBasicAuth("user", "pass"))
                .cookies(List.of(BrowserCookie.builder("session", "abc123").domain("internal.example.com").build()))
                .headers(Map.of("X-Tenant", "acme"))
                .saveTo("report.pdf")
                .build());
```

| Champ | Type | Description |
|-------|------|-------------|
| `pageSize` | `String` | `"A4"`, `"A3"`, `"Letter"`, `"Legal"`, et similaires. |
| `pageWidth`, `pageHeight` | `double` | Dimensions personnalisées. Définies ensemble, elles remplacent `pageSize`. |
| `orientation` | `String` | `"portrait"` ou `"landscape"`. |
| `margins` | `PdfMargins` | Record composé de `top`, `bottom`, `left`, `right`. Tout champ null est omis. |
| `scale` | `double` | Échelle de rendu, par exemple `0.9` pour 90 pour cent. |
| `grayscale` | `boolean` | Post-traite le PDF en niveaux de gris. |
| `header`, `footer` | `PdfHeaderFooter` | Record composé de `content` (2000 caractères maximum) et `height`. |

`BrowserCookie` a besoin d'un nom et d'une valeur, plus soit `domain`, soit `url` ; lorsque `domain` est défini sans `path`, l'API fixe `path` à `/` par défaut. Ne combinez pas `auth` avec un en-tête `Authorization` explicite, car l'API rejette ce conflit.

---

## Gestion des erreurs

Chaque exception du SDK hérite d'`EnconvertException`, qui hérite elle-même de `RuntimeException` : rien ne vous impose donc une clause `throws` sur vos sites d'appel. Attrapez d'abord les sous-classes spécifiques.

```java
try {
    client.convertUrlToPdf("https://example.com");
} catch (AuthenticationException e) {
    System.err.println("Invalid or missing API key");
} catch (QuotaException e) {
    System.err.println("Request refused with 402: " + e.getMessage());
} catch (RateLimitException e) {
    System.err.println("Too many requests, back off and retry");
} catch (ApiException e) {
    System.err.println("API error [" + e.getStatusCode() + "]: " + e.getMessage());
}
```

| Classe | Déclenchée sur | Code de statut |
|-------|-----------|-------------|
| `AuthenticationException` | Clé API manquante, invalide ou non autorisée | `401`, `403` |
| `QuotaException` | HTTP 402 | `402` |
| `RateLimitException` | Trop de requêtes | `429` |
| `ApiException` | Toute autre réponse 4xx ou 5xx | le code réel |
| `EnconvertException` | Classe de base, également levée en cas d'échec de transport, de requête interrompue ou de fichier d'entrée illisible | aucun |

La validation côté client (paire de conversion non prise en charge, schéma distill manquant, mise à jour de watcher vide, mauvais nombre de sorties pour `perceiveDirect`) lève une `IllegalArgumentException` avant qu'aucune requête ne soit émise. La table des messages des réponses serveur se trouve dans la référence [Codes d'erreur](/fr/docs/error-codes).

---

## Récupération des timeouts

Les rendus d'URL longs et les conversions de documents volumineux peuvent survivre au timeout du reverse proxy, même quand la conversion finit par réussir. Le SDK gère cela de manière transparente :

1. Avant chaque requête d'URL unique ou d'envoi de fichier, le SDK génère un UUID et l'envoie en tant que `job_id`.
2. Si la requête revient en 5xx, le SDK bascule vers le polling de `GET /v1/convert/status/{jobId}` toutes les 3 secondes.
3. Sur `success`, il renvoie le résultat. Sur `failed`, il lève une `ApiException` portant le message d'erreur du serveur.
4. Le délai maximal de polling est de 5 minutes. Au-delà, il lève `ApiException(504, "Conversion timed out")`.

Vous n'avez aucun code à écrire pour cela. Si une réponse réussie omet `job_id`, le SDK y réinjecte l'identifiant qu'il a généré, si bien que `result.jobId()` reste toujours utilisable avec `getJobStatus`.

<div class="alert alert-info">
<strong>Les soumissions de lots de site entier sont volontairement exclues.</strong> <code>convertWebsiteToPdf</code> et <code>convertWebsiteToScreenshot</code> n'ont pas de ligne de job à interroger : un 5xx signifie donc que la soumission elle-même a échoué, et l'erreur remonte directement. Les endpoints V2 n'utilisent pas non plus le polling de job ; leurs flux asynchrones passent par <code>getPerceiveBatch</code> et <code>getIngestJob</code>.
</div>

---

## Configuration

```java
Enconvert client = Enconvert.builder(System.getenv("ENCONVERT_API_KEY"))
        .baseUrl("https://api.enconvert.com")
        .timeout(Duration.ofSeconds(300))
        .build();
```

Trois constructeurs sont disponibles comme raccourcis : `new Enconvert(apiKey)`, `new Enconvert(apiKey, baseUrl)` et `new Enconvert(apiKey, baseUrl, timeout)`.

| Option | Type | Par défaut | Description |
|--------|------|---------|-------------|
| `apiKey` | `String` | obligatoire | Clé API privée. Une valeur null ou vide lève une `IllegalArgumentException`. |
| `baseUrl` | `String` | `https://api.enconvert.com` | URL de base de l'API. Les barres obliques finales sont supprimées. |
| `timeout` | `Duration` | 300 secondes | Appliqué à la fois comme timeout de connexion et comme timeout par requête. |

La clé voyage dans l'en-tête `X-API-Key`. Les URL de téléchargement présignées sont récupérées sans elle, puisqu'elles sont déjà signées. Les types de clés sont traités dans [Authentification](/fr/docs/authentication) ; créez et gérez vos clés depuis le [tableau de bord](/fr/dashboard).

<div class="alert alert-warning">
<strong>Ne codez jamais la clé API en dur.</strong> Lisez-la depuis une variable d'environnement ou votre gestionnaire de secrets. Le SDK est côté serveur uniquement : une clé privée ne doit pas être livrée dans un artefact desktop ou mobile qu'un utilisateur peut décompresser.
</div>

---

## Structure du résultat

Chaque conversion de fichier unique et d'URL unique renvoie le même record :

```java
public record ConversionResult(
        String presignedUrl,
        String objectKey,
        String filename,
        Long fileSize,
        Double conversionTimeSeconds,
        String jobId) {}
```

L'URL présignée est valable un temps limité. Passez `saveTo` (ou récupérez l'URL vous-même) et stockez les octets dans votre propre bucket si vous voulez qu'ils lui survivent.

Les autres records de réponse que vous manipulerez le plus souvent :

| Record | Accesseurs principaux |
|--------|---------------|
| `JobStatus` | `status()` (`processing`, `success`, `failed`), `presignedUrl()`, `objectKey()`, `error()` |
| `BatchStatus` | `status()`, `total()`, `completed()`, `failed()`, `inProgress()`, `zipDownloadUrl()`, `items()` |
| `PerceiveResult` | `operationId()`, `renderQuality()`, `statusCode()`, `deductions()`, `outputs()`, `structured()`, `cacheHit()`, `warnings()` |
| `V2OutputArtifact` | `url()`, `objectKey()`, `sizeBytes()`, `contentType()`, `expiresIn()` |
| `PerceiveDirectResult` | `content()`, `contentType()`, `filename()`, `renderQuality()`, `contentHash()` |
| `IngestJob` | `jobId()`, `status()`, `pagesProcessed()`, `totalChunks()`, `outputUrl()`, `webhookDelivered()` |
| `Watcher` | `watcherId()`, `status()`, `frequencyMinutes()`, `checksCount()`, `nextCheckAt()`, `lastChangeAt()` |

Les champs dont la forme est définie par votre propre requête (`structured`, `data`, `trackFields`, les `changes` d'un snapshot) sont exposés en `JsonObject` Gson et passent sans modification. Les énumérations à valeurs textuelles restent des `String` plutôt que de devenir des constantes `enum` Java : une nouvelle valeur de l'API ne casse donc jamais la désérialisation sur une version plus ancienne du SDK. `com.enconvert.model.v2.V2Enums` contient chaque valeur acceptée sous forme de constante à l'abri des fautes de frappe.

---

## Source et problèmes

- **Maven Central :** `com.enconvert:enconvert-sdk:0.0.1`
- **GitHub :** [conversionapi/java-sdk](https://github.com/conversionapi/java-sdk)
- **Licence :** MIT
- **Autres clients :** [tous les SDK](/fr/docs/sdks) · [référence des endpoints](/fr/docs/endpoints-overview) · [tarifs](/fr/pricing)

---

## Questions fréquentes

### Comment convertir des fichiers en Java avec une dépendance Maven ?

Ajoutez `com.enconvert:enconvert-sdk:0.0.1` à votre `pom.xml` ou votre `build.gradle`, construisez un client avec `new Enconvert(System.getenv("ENCONVERT_API_KEY"))`, et appelez une méthode typée comme `convertUrlToPdf`, `convertImage`, `convertDocument` ou `convertToPdf`. Passez `saveTo` sur le builder d'options pour écrire la sortie directement sur le disque au lieu de gérer vous-même l'URL présignée.

### Comment convertir une URL en PDF en Java ?

Appelez `client.convertUrlToPdf(url, UrlToPdfOptions.builder().saveTo("page.pdf").build())`. Définissez `singlePage(false)` pour paginer selon `pdfOptions.pageSize` au lieu de produire une seule page continue, et passez `auth`, `cookies` ou `headers` pour une page derrière une authentification.

### Comment convertir un DOCX en PDF en Java ?

Appelez `client.convertDocument(Path.of("report.docx"), ConvertDocumentOptions.builder().saveTo("report.pdf").build())`. Le format de sortie vaut `pdf` par défaut : vous ne définissez donc `outputFormat` que lorsque vous voulez autre chose, par exemple `yaml` à partir d'une entrée `.json`. Pour les formats sans paire dédiée, comme l'EPUB ou le RTF, utilisez `convertToPdf`.

### Comment convertir du HEIC en WebP en Java ?

Appelez `client.convertImage(Path.of("photo.heic"), ConvertImageOptions.builder("webp").saveTo("photo.webp").build())`. Le format d'entrée vient de l'extension du fichier et le format de sortie est l'argument obligatoire du builder. `jpeg`, `png`, `svg`, `heic` et `webp` se convertissent tous les uns vers les autres, et `pdf` se rastérise en `jpeg`. Ce client ne propose pas de méthode de compression sur place.

### Comment récupérer une page web en Markdown propre depuis Java ?

Deux chemins. `client.convertUrlToMarkdown(url, ...)` renvoie du Markdown GitHub-Flavored avec un frontmatter YAML, sous forme de fichier téléchargeable. `client.v2.perceive(url, PerceiveOptions.builder().outputs(List.of("markdown")).build())` renvoie le même contenu sous forme d'artefact prêt pour un agent, avec un score `renderQuality`, des options d'extraction et un contrôle du cache. Utilisez perceive quand une mauvaise lecture doit être détectable plutôt que silencieuse.

### Qu'est-ce que renderQuality et pourquoi chaque lecture en porte-t-il un ?

`renderQuality` est un score de 0.0 à 1.0 attaché à chaque rendu V2. Un score élevé signifie que la page s'est rendue proprement ; un score bas signifie que quelque chose s'est interposé, par exemple un challenge anti-bot, un mur de cookies, un écran de connexion, une page d'erreur HTTP ou une coquille SPA vide. Le contenu est tout de même renvoyé, avec `warnings()` et `deductions()` renseignés, pour que votre pipeline puisse écarter ou relancer la lecture au lieu d'injecter une page de challenge dans un modèle comme s'il s'agissait de l'article.

### Comment transformer un site de documentation en fragments prêts pour le RAG en Java ?

Appelez `client.v2.ingest(IngestOptions.builder().mode("sitemap").url("https://docs.example.com").maxPages(100).chunk(new IngestChunkOptions(512, 1)).build())`. Le job est asynchrone : soit vous interrogez `getIngestJob(jobId)` jusqu'à ce que le statut soit `completed` puis lisez `outputUrl()` pour récupérer le JSONL, soit vous définissez `webhookUrl` et vérifiez la signature HMAC avec le secret renvoyé par `getWebhookSecret()`. Pour des documents locaux plutôt qu'un site, utilisez `ingestFiles`.

### Comment le SDK gère-t-il les conversions qui dépassent le timeout du proxy ?

Avant chaque requête d'URL unique ou d'envoi de fichier, il génère un UUID et l'envoie comme `job_id`. Si la requête renvoie un 5xx, il interroge `GET /v1/convert/status/{jobId}` toutes les 3 secondes jusqu'à ce que le job signale `success` ou `failed`, avec un délai maximal de 5 minutes au-delà duquel il lève `ApiException(504, "Conversion timed out")`. Les soumissions de lots de site entier sont exclues, parce qu'elles n'ont pas de ligne de job à interroger.

### Quelle version de Java le SDK exige-t-il, et qu'embarque-t-il ?

Java 17 ou plus récent. Le HTTP passe par le `java.net.http.HttpClient` du JDK, et Gson est le seul artefact tiers sur le classpath. Les réponses sont des records Java : un `switch` moderne ou un pattern matching sur ces types fonctionne donc comme attendu. Le client est thread-safe : gardez une seule instance et partagez-la.
