SDK Kotlin de conversion de fichiers#
com.enconvert:enconvert-kotlin est le client EnConvert officiel pour Kotlin et la JVM, compilé pour le JDK 17. Il convertit des fichiers sur 43 paires de formats implémentées (DOCX vers PDF, HEIC vers WebP, JSON vers YAML, URL vers PDF, anything to Markdown), et il lit des pages web en direct pour en tirer du Markdown, du JSON, des captures d'écran et du JSONL prêt pour le RAG, le tout prêt à être consommé par un agent, via l'espace de noms client.v2. Les options et les réponses sont des data classes Kotlin idiomatiques, avec arguments nommés et valeurs par défaut raisonnables ; le HTTP passe par le java.net.http.HttpClient du JDK lui-même, et les conversions lentes se remettent des timeouts de reverse proxy en interrogeant le statut du job.
com.enconvert:enconvert-kotlin:0.0.1 · Source : conversionapi/kotlin-sdk · Requiert : JDK 17+ · Licence : MIT
Installation#
// Gradle, DSL Kotlin
dependencies {
implementation("com.enconvert:enconvert-kotlin:0.0.1")
}
// Gradle, DSL Groovy
dependencies {
implementation 'com.enconvert:enconvert-kotlin:0.0.1'
}
<dependency>
<groupId>com.enconvert</groupId>
<artifactId>enconvert-kotlin</artifactId>
<version>0.0.1</version>
</dependency>
La seule dépendance d'exécution est org.jetbrains.kotlinx:kotlinx-serialization-json. Tout le reste vient du JDK : les requêtes sortent via java.net.http.HttpClient et les corps multipart sont assemblés par le SDK lui-même. La toolchain cible la JVM 17, donc n'importe quel runtime JDK 17 ou plus récent convient.
Démarrage rapide#
import com.enconvert.Enconvert
import com.enconvert.PerceiveOptions
import com.enconvert.PerceiveOutputName
import com.enconvert.UrlToPdfOptions
fun main() {
val client = Enconvert(apiKey = System.getenv("ENCONVERT_API_KEY"))
// Convertit une page en direct en PDF et l'écrit directement sur le disque.
val pdf = client.convertUrlToPdf("https://example.com", UrlToPdfOptions(saveTo = "page.pdf"))
println(pdf.presignedUrl)
// Lit la même page comme votre agent devrait le faire, avec un score de qualité attaché.
val op = client.v2.perceive(
"https://example.com",
PerceiveOptions(outputs = listOf(PerceiveOutputName.MARKDOWN, PerceiveOutputName.STRUCTURED)),
)
println("${op.outputs["markdown"]?.url} ${op.renderQuality}") // p. ex. 0.93
}
Chaque type EnConvert vit dans le paquet com.enconvert, et les extraits ci-dessous omettent les imports ; les types du JDK comme java.nio.file.Files et java.nio.file.Path apparaissent sans qualification pour la même raison. Chaque méthode est bloquante, puisqu'il n'y a aucune fonction suspend : depuis une coroutine, enveloppez donc l'appel dans withContext(Dispatchers.IO). Le client s'authentifie avec une clé API privée envoyée dans l'en-tête X-API-Key, ce qui le réserve au côté serveur : ne livrez jamais la clé dans une application Android ou dans quoi que ce soit d'autre que vous distribuez. Voir Authentification pour les types de clés.
Ce que le client expose#
Enconvert constitue toute la surface. La conversion de fichiers vit sur le client lui-même ; l'intelligence web vit sur l'espace de noms v2, accessible via client.v2.
| Surface | Accessible via | Couvre |
|---|---|---|
| Conversion de fichiers | client.<method>() |
URL vers PDF, capture d'écran, Markdown ; paires d'images et de documents ; anything-to-PDF et anything-to-Markdown ; lots à l'échelle d'un site ; statut des jobs et des lots |
| Intelligence web | client.v2.<method>() |
Perceive, discover, lookup, distill, ingest, watch : 23 méthodes sur 21 endpoints REST |
Douze méthodes de conversion correspondent à l'API REST décrite dans la vue d'ensemble des endpoints :
| Méthode | Endpoint | Renvoie |
|---|---|---|
convertUrlToPdf(url, opts?) |
POST /v1/convert/url-to-pdf |
ConversionResult |
convertUrlToScreenshot(url, opts?) |
POST /v1/convert/url-to-screenshot |
ConversionResult |
convertUrlToMarkdown(url, opts?) |
POST /v1/convert/url-to-markdown |
ConversionResult |
convertImage(file, opts) |
POST /v1/convert/{from}-to-{to} |
ConversionResult |
convertDocument(file, opts?) |
POST /v1/convert/{from}-to-{to} |
ConversionResult |
convertToMarkdown(file, opts?) |
POST /v1/convert/anything-to-markdown |
ConversionResult |
convertToPdf(file, opts?) |
POST /v1/convert/anything-to-pdf |
ConversionResult |
convertWebsiteToPdf(url, opts?) |
POST /v1/convert/website-to-pdf |
BatchSubmission |
convertWebsiteToScreenshot(url, opts?) |
POST /v1/convert/website-to-screenshot |
BatchSubmission |
getJobStatus(jobId) |
GET /v1/convert/status/{jobId} |
JobStatus |
getBatchStatus(batchId) |
GET /v1/convert/batch/{batchId} |
BatchStatus |
waitForBatch(batchId, opts?) |
GET /v1/convert/batch/{batchId} (interrogé en boucle) |
BatchStatus |
Les quatre méthodes d'envoi de fichier ont chacune quatre surcharges. Le premier argument peut être un chemin String, un java.nio.file.Path, un simple ByteArray (le nom de fichier vaut alors upload.bin), ou un FileInput(data, filename, contentType?) lorsque vous disposez d'octets bruts et voulez les nommer vous-même.
Conversion de fichiers#
convertUrlToPdf#
Effectue le rendu de n'importe quelle URL publique en PDF.
val result = client.convertUrlToPdf(
"https://example.com/report",
UrlToPdfOptions(
render = UrlRenderOptions(viewportWidth = 1440),
singlePage = false,
pdfOptions = PdfOptions(pageSize = "A4", orientation = PdfOrientation.LANDSCAPE, margins = PdfMargins(top = 10.0)),
saveTo = "report.pdf",
),
)
println("${result.filename} ${result.fileSize}")
| Option | Type | Défaut | Description |
|---|---|---|---|
render |
UrlRenderOptions |
UrlRenderOptions() |
Fenêtre d'affichage, médias, défilement, nom de fichier, accès navigateur. |
saveTo |
String? |
-- | Chemin local vers lequel é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? |
-- | Géométrie de page. Voir Options PDF. |
UrlRenderOptions est partagé par toutes les conversions basées sur une URL. Il porte viewportWidth et viewportHeight (1920 x 1080 par défaut), loadMedia et enableScroll (tous deux à true par défaut, ce qui attend les médias et fait défiler la page de haut en bas pour déclencher le chargement paresseux), outputFilename, et trois champs d'accès navigateur : auth (un HttpBasicAuth), cookies (une List<BrowserCookie>, 50 au maximum) et headers (20 au maximum, les en-têtes hop-by-hop sont rejetés).
auth avec un en-tête Authorization. L'API rejette le conflit plutôt que de deviner ce que vous vouliez dire.
convertUrlToScreenshot#
Capture un PNG de n'importe quelle URL. UrlToScreenshotOptions ne porte que render et saveTo.
client.convertUrlToScreenshot(
"https://example.com",
UrlToScreenshotOptions(render = UrlRenderOptions(viewportWidth = 1440), saveTo = "shot.png"),
)
convertUrlToMarkdown#
Extrait un Markdown GitHub-Flavored propre à partir d'une URL. Le convertisseur supprime la navigation, les pieds de page, les publicités et les scripts, conserve le corps principal de l'article, et ajoute en tête un frontmatter YAML avec le titre, la description, l'url, les liens et les images.
val article = client.convertUrlToMarkdown("https://example.com/post", UrlToMarkdownOptions(saveTo = "article.md"))
println(article.presignedUrl)
Lorsque vous voulez aussi un score de qualité, un ensemble d'artefacts ou une extraction structurée issus du même rendu, utilisez plutôt client.v2.perceive.
convertImage#
Convertit entre jpeg, png, svg, heic et webp dans n'importe quel sens, ou rastérise un PDF en JPEG. ConvertImageOptions prend un outputFormat obligatoire, plus saveTo et outputFilename facultatifs.
client.convertImage("photo.heic", ConvertImageOptions(outputFormat = "webp", saveTo = "photo.webp"))
client.convertImage(Path.of("logo.svg"), ConvertImageOptions(outputFormat = "png", saveTo = "logo.png"))
val bytes = Files.readAllBytes(Path.of("photo.heic"))
client.convertImage(
FileInput(data = bytes, filename = "photo.heic"),
ConvertImageOptions(outputFormat = "webp", saveTo = "photo.webp"),
)
Le format d'entrée est résolu à partir de l'extension du nom de fichier. Le format de sortie est normalisé pour vous, "jpg" se résout donc en jpeg. Les paires non prises en charge lèvent une EnconvertException avant tout appel réseau, avec la liste des sorties valides dans le message. Vous pouvez interroger la table de formats directement :
validOutputsFor("pdf") // [jpeg]
validOutputsFor("json") // [csv, toml, xml, yaml]
validOutputsFor("heic") // [jpeg, png, svg, webp]
convertDocument#
Convertit des documents et des formats de données. outputFormat vaut "pdf" par défaut.
client.convertDocument("report.docx", ConvertDocumentOptions(saveTo = "report.pdf"))
client.convertDocument("data.json", ConvertDocumentOptions(outputFormat = "yaml", saveTo = "data.yaml"))
client.convertDocument(
"README.md",
ConvertDocumentOptions(
outputFormat = "pdf",
pdfOptions = PdfOptions(pageSize = "A4", margins = PdfMargins(top = 20.0, bottom = 20.0)),
saveTo = "readme.pdf",
),
)
Entrées prises en charge : .doc, .docx, .xls, .xlsx, .ppt, .pptx, .html, .htm, .odt, .ods, .odp, .ots, .pages, .numbers, .md, .markdown, .csv, .json, .xml, .yaml, .yml, .toml.
Les 43 paires implémentées, exactement telles que le SDK les contrôle :
| Entrée | Sorties |
|---|---|
json |
csv, toml, xml, yaml |
xml |
csv, json |
csv |
json, xml |
yaml |
json |
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 |
EPUB n'a pas de paire documentaire dédiée. Faites passer les fichiers .epub par convertToPdf ou convertToMarkdown. Les options sont outputFormat, saveTo, outputFilename et pdfOptions (pris en compte uniquement lorsque la sortie est un PDF).
convertToMarkdown#
Convertit un fichier envoyé, de presque n'importe quel format documentaire, en Markdown propre. Le format est détecté automatiquement côté serveur : le SDK n'effectue donc aucune vérification d'extension et envoie le fichier tel quel.
client.convertToMarkdown("handbook.docx", ConvertToMarkdownOptions(saveTo = "handbook.md"))
Accepté : PDF, DOCX, PPTX, XLSX, CSV, HTML, EPUB, TXT et MD, plus les formats Office hérités et ODF. Les images ne sont pas prises en charge ici.
La sortie est un unique fichier .md qui respecte la hiérarchie des titres, ce qui en fait une première étape naturelle pour un pipeline RAG : un découpeur sémantique peut segmenter sur la hiérarchie de titres propre au document plutôt que sur un nombre de caractères arbitraire. Cet endpoint n'accepte aucune option PDF ; saveTo et outputFilename sont les seules options.
convertToPdf#
Convertit un fichier envoyé, de presque n'importe quel format, en PDF. Les entrées acceptées couvrent Office, ODF, Pages, Numbers, RTF, CSV, HTML, Markdown, texte brut, images matricielles, SVG, EPUB, ainsi qu'un PDF existant en passthrough.
client.convertToPdf("slides.pptx", ConvertToPdfOptions(saveTo = "slides.pdf"))
client.convertToPdf("scan.pdf", ConvertToPdfOptions(pdfOptions = PdfOptions(grayscale = true), saveTo = "gray.pdf"))
pdfOptions.grayscale est pris en compte sur cet endpoint. La taille de page, l'orientation, les marges, l'échelle, l'en-tête et le pied de page sont ignorés ici. Lorsque vous avez besoin de la géométrie de page complète, passez plutôt par convertDocument ou convertUrlToPdf.
convertWebsiteToPdf et convertWebsiteToScreenshot#
Découvrez toutes les pages d'un site, convertissez-les en arrière-plan et récupérez une archive ZIP unique. Les deux méthodes sont asynchrones : elles renvoient immédiatement un BatchSubmission, et vous interrogez avec getBatchStatus ou bloquez avec waitForBatch.
val batch = client.convertWebsiteToPdf(
"https://example.com",
WebsiteToPdfOptions(
website = WebsiteConversionOptions(crawlMode = CrawlMode.SITEMAP, excludePatterns = listOf("/tag/")),
),
)
val status = client.waitForBatch(batch.batchId, WaitForBatchOptions(saveTo = "site.zip"))
println("${status.completed} of ${status.total} converted, ${status.failed} failed")
convertWebsiteToScreenshot fonctionne à l'identique et produit un ZIP de PNG. WebsiteConversionOptions porte render, crawlMode (AUTO, SITEMAP, FULL), includePatterns, excludePatterns, notificationEmail et callbackUrl. waitForBatch interroge toutes les 5 secondes et abandonne au bout de 30 minutes, deux valeurs remplaçables via WaitForBatchOptions(intervalMs, timeoutMs, saveTo) ; en cas de dépassement, il lève une ApiException avec le statut 504.
getJobStatus#
Interroge un job de conversion asynchrone ou récupéré après timeout.
val status = client.getJobStatus("job_abc123")
when (status.status) {
JobStatusValue.SUCCESS -> println(status.presignedUrl)
JobStatusValue.FAILED -> System.err.println(status.error)
JobStatusValue.PROCESSING -> println("still running")
}
Intelligence web (V2)#
Chaque lecture V2 porte renderQuality, un score de 0.0 à 1.0 qui décrit à quel point la page a réellement été rendue proprement. Une page anti-bot, un mur de cookies, une barrière de connexion ou une coquille SPA vide revient avec un score faible, une table deductions remplie qui nomme les contrôles déclenchés, et des warnings, tandis que le contenu lui-même est quand même renvoyé. Une mauvaise lecture est signalée plutôt que d'entrer discrètement dans le contexte de votre agent. statusCode indique le statut HTTP de la réponse finale du document principal, et contentHash vous permet de constater que rien n'a changé depuis la lecture précédente. Commencez par la vue d'ensemble V2 pour les concepts derrière les six capacités.
| Capacité | Méthodes sur client.v2 |
|---|---|
| Perceive | perceive, getPerceiveOperation, perceiveBatch, getPerceiveBatch, perceiveDirect, downloadPerceiveArtifact |
| Discover | discover |
| Lookup | lookup |
| Distill | distill |
| Ingest | ingest, ingestFiles, listIngestJobs, getIngestJob, cancelIngestJob, retryIngestWebhook, getWebhookSecret, rotateWebhookSecret |
| Watch | createWatcher, listWatchers, getWatcher, getWatcherSnapshots, updateWatcher, deleteWatcher |
Perceive#
Effectue le rendu d'une URL dans les artefacts que vous demandez. Synchrone : l'appel renvoie une opération terminée dont les URL d'artefacts sont signées pour 15 minutes. Référence complète dans Perceive.
val op = client.v2.perceive(
"https://example.com/pricing",
PerceiveOptions(
outputs = listOf(PerceiveOutputName.MARKDOWN, PerceiveOutputName.SCREENSHOT_FULL_PAGE, PerceiveOutputName.STRUCTURED),
extract = listOf(PerceiveExtractName.TABLES, PerceiveExtractName.METADATA),
viewport = PerceiveViewport(width = 1440),
waitFor = "css:.pricing-table",
),
)
if ((op.renderQuality ?: 0.0) < 0.5) System.err.println("Low quality read: ${op.deductions} ${op.warnings}")
println(op.outputs["markdown"]?.url)
println(op.structured)
| Option | Type | Défaut | Description |
|---|---|---|---|
outputs |
List<PerceiveOutputName>? |
[MARKDOWN, STRUCTURED] |
MARKDOWN, HTML_CLEANED, HTML_RAW, SCREENSHOT, SCREENSHOT_FULL_PAGE, PDF, LINKS, IMAGES, STRUCTURED. |
extract |
List<PerceiveExtractName>? |
-- | TABLES, PRICES, CONTACTS, METADATA, MAIN_CONTENT, HEADINGS, STRUCTURED_DATA, TECHNOLOGIES, ALL. |
schema |
Map<String, Any?>? |
-- | Schéma JSON pour l'extraction structurée. |
waitFor / waitTimeoutMs |
String? / Int? |
-- / 30000 |
Un sélecteur CSS (éventuellement préfixé par css:) ou js:<expr> à attendre, et son budget, de 0 à 60000. |
jsCode |
String? |
-- | JavaScript exécuté après la navigation, 20000 caractères maximum. |
viewport |
PerceiveViewport? |
1920 x 1080 | width de 320 à 3840, height de 240 à 2160. |
headers / cookies / auth |
-- | -- | En-têtes supplémentaires, cookies injectés, identifiants HTTP Basic. |
cacheMode |
PerceiveCacheMode? |
ENABLED |
ENABLED (cache d'1 heure), BYPASS, REFRESH. |
pdfOptions |
PdfOptions? |
-- | N'a de sens que lorsque outputs inclut PDF. |
blockResources |
List<PerceiveResourceType>? |
-- | Types de ressources que le navigateur ne doit pas charger. |
respectRobots / mobile |
Boolean? |
-- | Respecter robots.txt ; effectuer le rendu avec un profil mobile. |
onlyMainContent |
Boolean? |
true |
Supprime la navigation, l'en-tête, le pied de page et les bandeaux de cookies de l'artefact Markdown et de l'extrait main_content. |
directDownload |
Boolean? |
-- | Renvoie les octets de l'artefact au lieu d'une enveloppe JSON. Préférez perceiveDirect. |
proxyUrl, geolocation et actionChain existent sur PerceiveOptions mais ne sont pas encore disponibles côté serveur : ils sont actuellement rejetés avec un 422.
Traitez jusqu'à 1000 URL par lot derrière un bloc d'options partagé. Les petits lots se terminent en ligne ; les plus gros reviennent en QUEUED, interrogez donc l'identifiant de job. getPerceiveOperation resigne les URL d'artefacts de n'importe quelle opération antérieure.
val batch = client.v2.perceiveBatch(
listOf("https://a.example.com", "https://b.example.com"),
PerceiveBatchOptions(
options = PerceiveOptions(outputs = listOf(PerceiveOutputName.MARKDOWN)),
outputMode = PerceiveBatchOutputMode.ZIP,
),
)
var job = client.v2.getPerceiveBatch(batch.jobId)
while (job.status == PerceiveBatchStatus.QUEUED || job.status == PerceiveBatchStatus.PROCESSING) {
Thread.sleep(5_000)
job = client.v2.getPerceiveBatch(batch.jobId)
}
println("${job.completed}/${job.total} done, zip at ${job.zip?.url}")
val again = client.v2.getPerceiveOperation(op.operationId) // URL fraîchement signées
Lorsque vous ne voulez que les octets, perceiveDirect renvoie l'artefact sur la même requête et évite l'aller-retour par URL signée. Il exige exactement une sortie produisant un artefact, c'est-à-dire n'importe quoi sauf STRUCTURED, et lève une EnconvertException en local si vous en demandez zéro ou plus d'une.
val direct = client.v2.perceiveDirect("https://example.com", PerceiveOptions(outputs = listOf(PerceiveOutputName.PDF)))
Files.write(Path.of(direct.filename ?: "page.pdf"), direct.content)
println("${direct.renderQuality} ${direct.sourceStatusCode} ${direct.warningsCount}")
// Retélécharge un artefact stocké d'une opération antérieure.
val markdown = client.v2.downloadPerceiveArtifact(op.operationId, PerceiveOutputName.MARKDOWN)
println(markdown.content.toString(Charsets.UTF_8))
downloadPerceiveArtifact accepte un output nul lorsque l'opération n'a produit qu'un seul artefact, et renvoie 410 dès que l'artefact stocké dépasse sa fenêtre de rétention.
Discover#
Énumère les URL d'un site sans aucun rendu navigateur. Référence complète dans Discover.
val found = client.v2.discover(
"https://example.com",
DiscoverOptions(mode = DiscoverMode.HYBRID, maxUrls = 200, maxDepth = 3, excludePatterns = listOf("/tag/")),
)
println("${found.total} urls, truncated=${found.truncated}, sources=${found.sources}")
| Option | Type | Défaut | Description |
|---|---|---|---|
mode |
DiscoverMode? |
HYBRID |
SITEMAP, CRAWL ou HYBRID (sitemap plus exploration HTTP). |
maxUrls / maxDepth |
Int? |
100 / 2 |
1-1000 et 1-5. |
includePatterns / excludePatterns |
List<String>? |
-- | Liste d'autorisation et liste d'exclusion en expressions régulières, 50 entrées maximum chacune. La liste d'exclusion est appliquée en second. |
sameDomainOnly |
Boolean? |
true |
Rester sur le domaine de départ. |
respectRobots |
Boolean? |
-- | Respecter robots.txt. |
DiscoverResult.sources indique les comptages bruts par source avant déduplication, par exemple {sitemap=42, crawl=30}.
Lookup#
Lance une recherche web catégorisée et, si vous le souhaitez, perçoit les premiers résultats dans le même appel. Référence complète dans Lookup.
val search = client.v2.lookup(
"best static site generators",
LookupOptions(category = LookupCategory.WEB, numResults = 10, country = "us", timeFilter = LookupTimeFilter.MONTH, perceiveTop = 3),
)
for (hit in search.results) {
println("${hit.position}. ${hit.title} ${hit.url}")
hit.perceive?.let { println(" rendered at quality ${it.renderQuality}") }
}
| Option | Type | Défaut | Description |
|---|---|---|---|
category |
LookupCategory? |
WEB |
WEB, NEWS, IMAGES, SCHOLAR, PATENTS, MAPS. |
country / locale |
String? |
-- | Code pays Google gl et langue d'interface hl. |
timeFilter |
LookupTimeFilter? |
-- | HOUR, DAY, WEEK, MONTH, YEAR. |
numResults / page |
Int? |
10 / 1 |
1-100 et 1-10. |
location |
String? |
-- | Localisation en texte libre, par exemple "Austin, Texas". |
autocorrect |
Boolean? |
true |
Laisser le fournisseur corriger les fautes de frappe. |
perceiveTop |
Int? |
0 |
Percevoir automatiquement les N premières URL de résultat, 0-10. Chacune déclenche un rendu navigateur complet. |
Le résultat porte aussi answerBox, knowledgeGraph, perceiveOperationIds et total.
Distill#
Pointez un schéma vers quelques pages et récupérez des données structurées. Une passe CSS optionnelle répond à tout ce qu'elle peut avant toute escalade vers le niveau LLM. Référence complète dans Distill.
val extraction = client.v2.distill(
DistillOptions(
urls = listOf("https://example.com/pricing"),
schema = mapOf("plans" to "list of plan names with monthly prices"),
cssSchema = CssSchema(
baseSelector = ".plan-card",
fields = listOf(
CssField(name = "name", type = CssFieldType.TEXT, selector = "h3"),
CssField(name = "price", type = CssFieldType.TEXT, selector = ".price"),
),
targetField = "plans",
),
),
)
for (item in extraction.results) {
println("${item.url} tier=${item.extractionTier} css=${item.fieldsFromCss} llm=${item.fieldsFromLlm}")
println(item.data)
}
Ou découvrez d'abord les URL et distillez-les une à une :
client.v2.distill(
DistillOptions(
discoverFrom = DistillDiscoverFrom(url = "https://example.com", mode = DiscoverMode.SITEMAP, maxPages = 10),
schema = mapOf("title" to "page title", "summary" to "one-line summary"),
),
)
Fournissez exactement l'un des deux : urls ou discoverFrom. Passer les deux, ou aucun, lève une EnconvertException avant que la requête ne quitte votre processus.
| Option | Type | Défaut | Description |
|---|---|---|---|
urls |
List<String>? |
-- | URL explicites à distiller, 50 au maximum. |
discoverFrom |
DistillDiscoverFrom? |
-- | Découvrir d'abord les URL d'un site. maxPages va de 1 à 50, avec 10 par défaut. |
schema |
Map<String, Any?> |
obligatoire | Un objet JSON Schema, ou une table plate {field to description}. |
cssSchema |
CssSchema? |
-- | Passe CSS exécutée avant toute escalade LLM. |
waitFor / waitTimeoutMs |
String? / Int? |
-- / 30000 |
Sélecteur ou expression js: à attendre, et son budget. |
headers / cookies / respectRobots |
-- | -- | Mêmes contrôles de rendu que perceive. |
CssField.type vaut l'un de TEXT, ATTRIBUTE, HTML, REGEX, NESTED, LIST, NESTED_LIST. ATTRIBUTE exige attribute, REGEX exige pattern, et les trois variantes imbriquées exigent une liste fields non vide, jusqu'à cinq niveaux de profondeur.
Ingest#
Transforme un site entier, ou une pile 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 complète dans Ingest.
val job = client.v2.ingest(
IngestOptions(
mode = IngestMode.SITEMAP,
url = "https://docs.example.com",
maxPages = 100,
chunk = IngestChunkOptions(maxWords = 512, sentenceOverlap = 1),
webhookUrl = "https://my.app/hooks/enconvert",
),
)
var state = client.v2.getIngestJob(job.jobId)
while (state.status !in setOf(IngestStatus.COMPLETED, IngestStatus.FAILED, IngestStatus.CANCELED)) {
Thread.sleep(10_000)
state = client.v2.getIngestJob(job.jobId)
}
if (state.status == IngestStatus.COMPLETED) println("${state.totalChunks} chunks at ${state.outputUrl}")
mode vaut URLS par défaut, ce qui exige une liste urls non vide et rejette url. SITEMAP et CRAWL exigent une url de départ et rejettent urls. Le SDK applique les deux règles en local et lève une EnconvertException plutôt que d'envoyer une requête qui ne peut pas aboutir.
Les fichiers envoyés passent par ingestFiles, qui partage le même cycle de vie de job sous le mode FILES et accepte PDF, DOCX, PPTX, XLSX, CSV, HTML, EPUB, TXT et MD, ainsi que les documents Office hérités et ODF.
val paths = listOf(Path.of("handbook.pdf"), Path.of("notes.docx"))
val fileJob = client.v2.ingestFiles(
paths.map { FileInput(data = Files.readAllBytes(it), filename = it.fileName.toString()) },
IngestFilesOptions(chunk = IngestChunkOptions(maxWords = 512, sentenceOverlap = 1)),
)
println(fileJob.jobId)
| Option | Type | Défaut | Description |
|---|---|---|---|
mode |
IngestMode? |
URLS |
URLS, SITEMAP, CRAWL, FILES. |
url / urls |
String? / List<String>? |
-- | URL de départ pour SITEMAP et CRAWL ; URL explicites (1000 au maximum) pour URLS. |
maxPages / maxDepth |
Int? |
50 / 2 |
Plafonds de découverte, 1-1000 et 1-5. |
sameDomainOnly |
Boolean? |
true |
Rester sur le domaine de départ. |
includePatterns / excludePatterns / respectRobots |
-- | -- | Mêmes contrôles de découverte que discover. |
waitFor / waitTimeoutMs |
String? / Int? |
-- / 30000 |
Attente de rendu par page. |
chunk |
IngestChunkOptions? |
-- | maxWords de 32 à 4000 (512 par défaut), sentenceOverlap de 0 à 10 (1 par défaut). |
webhookUrl |
String? |
-- | Webhook de fin, signé en HMAC. |
Gestion des jobs et plomberie des webhooks :
client.v2.listIngestJobs(V2ListOptions(limit = 20)) // du plus récent au plus ancien
client.v2.cancelIngestJob(job.jobId) // idempotent
val secret = client.v2.getWebhookSecret()
println("${secret.signatureHeader} ${secret.signatureScheme} ${secret.replayToleranceSeconds}s")
client.v2.rotateWebhookSecret() // les anciennes signatures cessent immédiatement d'être valides
client.v2.retryIngestWebhook(job.jobId) // renvoie le webhook d'un job terminé
Watch#
Refait le rendu d'une URL à intervalle fixe et vous notifie quand elle change. Référence complète dans Watch.
val watcher = client.v2.createWatcher(
"https://example.com/pricing",
WatchCreateOptions(
frequencyMinutes = 60,
diffMode = WatchDiffMode.AUTO,
webhookUrl = "https://my.app/hooks/changes",
notifyEmail = true,
),
)
client.v2.listWatchers(V2ListOptions(limit = 20))
for (snap in client.v2.getWatcherSnapshots(watcher.watcherId, SnapshotListOptions(limit = 10)).snapshots) {
println("${snap.checkedAt} changed=${snap.hasChanges} similarity=${snap.similarity}")
}
client.v2.updateWatcher(watcher.watcherId, WatcherUpdate(status = WatcherUpdateStatus.PAUSED))
client.v2.updateWatcher(watcher.watcherId, WatcherUpdate(webhookUrl = "")) // efface le webhook
client.v2.deleteWatcher(watcher.watcherId) // suppression logique, idempotente
| Option | Type | Défaut | Description |
|---|---|---|---|
frequencyMinutes |
Int? |
60 |
Minutes entre deux vérifications, 60-43200. Le plancher horaire est strict. |
diffMode |
WatchDiffMode? |
AUTO |
AUTO, TEXT, STRUCTURED, TABLES, METADATA. |
trackFields |
Map<String, Any?>? |
-- | Sous-ensemble de champs ou de sélecteurs que le moteur de diff doit surveiller. |
webhookUrl |
String? |
-- | Webhook de changement, signé en HMAC avec le même secret qu'ingest. |
notifyEmail |
Boolean? |
true |
Notifier le propriétaire du projet par e-mail en cas de changement. |
updateWatcher exige au moins un champ et lève une EnconvertException sur un WatcherUpdate vide. Son status n'accepte que ACTIVE ou PAUSED ; la suppression passe par deleteWatcher, qui renvoie le watcher marqué supprimé avec le statut DELETED. getWatcher sur un watcher supprimé répond 404.
WatcherSnapshot.changes est du texte brut extrait de la page surveillée. Échappez-le avant de l'afficher dans du HTML, un tableau de bord ou un message de chat.
Options PDF#
PdfOptions est partagé par convertUrlToPdf, convertDocument, convertToPdf (niveaux de gris uniquement), convertWebsiteToPdf et PerceiveOptions.pdfOptions.
client.convertUrlToPdf(
"https://example.com",
UrlToPdfOptions(
pdfOptions = PdfOptions(
pageSize = "A4",
orientation = PdfOrientation.LANDSCAPE,
margins = PdfMargins(top = 10.0, bottom = 10.0, left = 15.0, right = 15.0),
scale = 0.9,
header = PdfHeaderFooter(content = "Quarterly report", height = 12.0),
),
saveTo = "report.pdf",
),
)
| Champ | Type | Description |
|---|---|---|
pageSize |
String? |
"A4", "A3", "Letter", "Legal", et ainsi de suite. |
pageWidth / pageHeight |
Double? |
Géométrie personnalisée. Ensemble, elles remplacent pageSize. |
orientation |
PdfOrientation? |
PORTRAIT ou LANDSCAPE. Portrait par défaut. |
margins |
PdfMargins? |
top, bottom, left, right, tous des doubles facultatifs, en mm. |
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? |
content (2000 caractères maximum) et height. |
Seuls les champs que vous définissez réellement sont sérialisés sur le réseau : un PdfOptions partiellement rempli ne remplace donc jamais une valeur par défaut du serveur à laquelle vous n'avez pas touché. La matrice complète des paramètres se trouve dans Paramètres et options.
Gestion des erreurs#
Chaque échec est une EnconvertException ou une de ses sous-classes : un seul catch peut donc servir de filet de sécurité pendant que des sous-classes précises traitent les cas qui vous intéressent.
try {
client.v2.perceive("https://example.com")
} catch (e: AuthenticationException) {
System.err.println("Invalid or missing API key")
} catch (e: QuotaException) {
System.err.println("Request rejected with 402")
} catch (e: RateLimitException) {
System.err.println("Too many requests, back off and retry")
} catch (e: ApiException) {
System.err.println("API error [${e.statusCode}]: ${e.message}")
} catch (e: EnconvertException) {
System.err.println("Client-side validation failed: ${e.message}")
}
| Classe | Levée sur | Code de statut |
|---|---|---|
AuthenticationException |
Clé API invalide, absente ou révoquée | 401, 403 |
QuotaException |
Levée sur un 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, plus la validation côté client comme une paire de conversion non prise en charge ou un objet d'options mal formé | -- |
ApiException expose la propriété brute statusCode, et son message s'affiche sous la forme [<statusCode>] <server message>, avec le champ detail ou error du serveur extrait du corps JSON. L'ordre des catch compte : les trois classes précises étendent toutes ApiException, qui étend EnconvertException, listez-les donc en premier. La table des messages est documentée dans Codes d'erreur.
Récupération après timeout#
Les rendus URL vers PDF longs et les conversions de gros documents peuvent dépasser un timeout de reverse proxy de 60 à 120 secondes même lorsque la conversion réussit côté serveur. Le SDK gère cela sur les méthodes de conversion V1 :
- Avant chaque requête, il génère un identifiant de job hexadécimal de 32 caractères et l'envoie comme
job_iddans le corps JSON ou comme champ multipart. - Si la requête revient en 5xx, le SDK cesse de faire confiance à la réponse et interroge
GET /v1/convert/status/{job_id}toutes les 3 secondes. Un404pendant que la ligne du job est encore en cours d'écriture signifie « continuez d'attendre ». - Sur
success, le SDK transforme la charge utile en unConversionResultnormal. Surfailed, il lève uneApiExceptionportant le message d'erreur du serveur. - Le délai est de 5 minutes, après quoi il lève
ApiException(504, "Conversion timed out").
Les réponses réussies qui omettent job_id (c'est le cas du chemin URL synchrone) reçoivent l'identifiant généré par le client : result.jobId est donc toujours quelque chose que vous pouvez passer à getJobStatus. Deux exceptions délibérées : convertWebsiteToPdf et convertWebsiteToScreenshot sautent ce repli, car une soumission de site n'a pas de ligne de job par unité et une 5xx y signifie que la soumission elle-même a échoué. Les méthodes V2 le sautent aussi, puisque chaque endpoint V2 dispose de son propre mécanisme d'interrogation ou de webhook.
Configuration#
val client = Enconvert(
apiKey = System.getenv("ENCONVERT_API_KEY"),
timeout = 300_000, // ms, 5 minutes
baseUrl = "https://api.enconvert.com", // à remplacer pour une passerelle auto-hébergée
)
| Paramètre | Type | Défaut | Description |
|---|---|---|---|
apiKey |
String |
obligatoire | Clé API privée. Une valeur vide fait lever une IllegalArgumentException par le constructeur. |
timeout |
Long |
300_000 |
Timeout par requête, en millisecondes, appliqué au HttpRequest sous-jacent. |
baseUrl |
String |
https://api.enconvert.com |
URL de base de l'API. Les barres obliques finales sont supprimées. |
Forme du résultat#
Chaque méthode de conversion renvoie un ConversionResult :
public data class ConversionResult(
val presignedUrl: String,
val objectKey: String,
val filename: String,
val fileSize: Long? = null,
val conversionTimeSeconds: Double? = null,
val jobId: String? = null,
)
L'URL présignée est un lien signé temporaire. Passez saveTo si vous voulez les octets sur le disque immédiatement, ou téléchargez l'URL vous-même et rangez le fichier dans votre propre bucket pour un accès permanent.
Les lectures V2 renvoient plutôt un PerceiveResult, où se trouvent les signaux de fiabilité :
val op = client.v2.perceive("https://example.com")
op.operationId // "per_..."
op.status // PerceiveStatus.COMPLETED
op.url // URL demandée ; op.urlFinal après les redirections
op.renderQuality // Double?, de 0.0 à 1.0
op.statusCode // Int?, statut HTTP du document principal
op.deductions // Map<String, Double>, p. ex. {http_error=0.7}. Vide sur un rendu propre.
op.warnings // List<String>
op.cacheHit // Boolean ; op.contentHash est le SHA-256 du contenu rendu
op.outputs // Map<String, V2OutputArtifact> indexée par nom de sortie
op.structured // Map<String, Any?>?, présent quand extract ou schema a été utilisé
op.extractionTier // HEURISTIC, CSS ou LLM
op.tokens // V2Tokens(input, output) ; op.costCents et op.durationMs à côté
Chaque V2OutputArtifact porte url, objectKey, sizeBytes, contentType et expiresIn (900 secondes). Les URL d'artefacts sont resignées à chaque appel de getPerceiveOperation : stockez donc l'operationId, pas l'URL. Les charges utiles non typées (schémas d'extraction, données extraites, champs suivis, entrées de diff, extras de recherche) traversent la frontière sous forme de Map<String, Any?> et se convertissent sans perte dans les deux sens : rien de ce que vous mettez dans un schéma n'est remodelé en sortie.
Sources et signalements#
- Maven Central :
com.enconvert:enconvert-kotlin:0.0.1 - GitHub : conversionapi/kotlin-sdk
- Licence : MIT
- Autres langages : voir la liste complète des SDK
Questions fréquentes#
Comment convertir des fichiers en Kotlin ?#
Ajoutez com.enconvert:enconvert-kotlin:0.0.1 à votre build Gradle ou Maven, construisez Enconvert(apiKey = System.getenv("ENCONVERT_API_KEY")), et appelez une méthode typée comme convertDocument, convertImage ou convertUrlToPdf. Passez saveTo dans l'objet d'options pour écrire la sortie directement dans un fichier local plutôt que de télécharger vous-même l'URL présignée.
Comment convertir un DOCX en PDF en Kotlin ?#
Appelez client.convertDocument("report.docx", ConvertDocumentOptions(saveTo = "report.pdf")). Le format de sortie vaut pdf par défaut, vous pouvez donc laisser outputFormat non défini. Le format d'entrée est résolu à partir de l'extension du fichier, et .doc comme .docx pointent vers la même conversion. Pour la géométrie de page, passez un PdfOptions via ConvertDocumentOptions.pdfOptions.
Comment convertir une URL en PDF en Kotlin ?#
Appelez client.convertUrlToPdf(url, UrlToPdfOptions(saveTo = "page.pdf")). Mettez singlePage = false pour paginer avec pdfOptions.pageSize, et utilisez UrlRenderOptions pour changer la fenêtre d'affichage, désactiver le chargement des médias ou couper la passe de défilement qui déclenche le chargement paresseux.
Comment convertir du HEIC en WebP sur la JVM ?#
Appelez client.convertImage("photo.heic", ConvertImageOptions(outputFormat = "webp", saveTo = "photo.webp")). Les 20 paires entre jpeg, png, svg, heic et webp sont implémentées, plus la rastérisation pdf vers jpeg. Une paire non prise en charge lève une EnconvertException avant tout appel réseau, et validOutputsFor("heic") liste les cibles valides en amont.
Comment récupérer une page web en Markdown depuis Kotlin ?#
Deux possibilités. client.convertUrlToMarkdown(url, UrlToMarkdownOptions(saveTo = "page.md")) vous donne un fichier Markdown avec un frontmatter YAML. client.v2.perceive(url, PerceiveOptions(outputs = listOf(PerceiveOutputName.MARKDOWN))) vous donne le même contenu plus renderQuality, deductions, warnings et statusCode, ce qui est exactement ce qu'il vous faut lorsqu'un agent va lire le résultat sans supervision.
Que signifie renderQuality et quand faut-il rejeter une page ?#
renderQuality va de 0.0 à 1.0 et décrit à quel point la page a été rendue proprement, pas la qualité du contenu. Les pages anti-bot, les murs de connexion, les erreurs HTTP et les coquilles SPA vides le font baisser, et deductions nomme chaque contrôle déclenché, par exemple {http_error=0.7}. Le contenu est toujours renvoyé pour que vous puissiez l'inspecter. Un schéma courant consiste à considérer tout ce qui est en dessous de 0.5 comme suspect, puis à redemander la page avec cacheMode = PerceiveCacheMode.REFRESH ou à la router vers un humain.
Comment transformer un site de documentation en chunks RAG depuis Kotlin ?#
Appelez client.v2.ingest(IngestOptions(mode = IngestMode.SITEMAP, url = "https://docs.example.com", chunk = IngestChunkOptions(maxWords = 512, sentenceOverlap = 1))). Ingest est toujours asynchrone : interrogez getIngestJob(jobId) jusqu'à ce que le statut soit COMPLETED et lisez outputUrl pour récupérer le JSONL, ou définissez webhookUrl et laissez le webhook de fin vous trouver. Les documents locaux passent par ingestFiles avec les mêmes réglages de découpage.
Le SDK bloque-t-il le thread appelant ?#
Oui. Chaque méthode appelle HttpClient.send de manière synchrone, et il n'y a ni fonction suspend ni builder de coroutine dans le SDK. waitForBatch et l'interrogateur interne de récupération après timeout endorment le thread courant entre deux tentatives. Depuis une coroutine, enveloppez les appels dans withContext(Dispatchers.IO) ; depuis un framework serveur, gardez-les hors du pool de threads qui traite les requêtes.
Puis-je appeler ce SDK depuis Java ?#
Oui, puisque ce sont des classes JVM ordinaires, mais les arguments par défaut de Kotlin ne sont pas exposés à Java sous forme de surcharges : un appelant Java doit donc passer tous les arguments de constructeur d'une data class d'options. Si votre base de code est en Java, utilisez plutôt le SDK Java distinct listé sur la page des SDK.
Où obtenir une clé API ?#
Créez une clé privée dans votre tableau de bord. Elle est envoyée dans l'en-tête X-API-Key à chaque requête, gardez-la donc côté serveur. Les types de clés et leurs portées sont traités dans Authentification, et la tarification couvre le volet commercial.