SDK de Conversión de Archivos para Java#

com.enconvert:enconvert-sdk es el cliente oficial de Java para la API de EnConvert. Una única dependencia de Maven o Gradle te da conversión de archivos (URL a PDF, DOCX a PDF, HEIC a WebP, cualquier cosa a Markdown) más toda la superficie de inteligencia web V2: perceive, discover, lookup, distill, ingest y watch. Está dirigido a Java 17 y superior, se ejecuta sobre el java.net.http.HttpClient incorporado en el JDK y arrastra Gson como su única dependencia de terceros. Cada llamada es un método bloqueante corriente que devuelve un record tipado, y las conversiones largas se recuperan de forma transparente de los timeouts del proxy inverso mediante sondeo del estado del job.

Maven Central: com.enconvert:enconvert-sdk:0.0.1 · Fuente: conversionapi/java-sdk · Java: 17+ · Dependencias: solo Gson

Instalación#

// build.gradle
dependencies {
    implementation 'com.enconvert:enconvert-sdk:0.0.1'
}
// build.gradle.kts
dependencies {
    implementation("com.enconvert:enconvert-sdk:0.0.1")
}
<!-- pom.xml -->
<dependency>
    <groupId>com.enconvert</groupId>
    <artifactId>enconvert-sdk</artifactId>
    <version>0.0.1</version>
</dependency>

De HTTP se encarga java.net.http.HttpClient del JDK. El único artefacto de terceros que viene con él es Gson para JSON, declarado como dependencia api para que sea visible en tu classpath de compilación.


Inicio rápido#

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());

// Lee una página como debería hacerlo tu agente, con una puntuación de calidad adjunta.
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());   // p. ej. 0.93

Cada clase de opciones es un builder inmutable y cada respuesta es un record de Java, así que los accesores se leen como pdf.presignedUrl() y page.renderQuality(). El cliente mantiene un único HttpClient compartido y ningún estado mutable por solicitud, de modo que una sola instancia puede ser un singleton o un bean de Spring compartido entre hilos. Los fragmentos siguientes omiten los imports: las opciones y los tipos de respuesta viven en com.enconvert.model (conversión) y com.enconvert.model.v2 (inteligencia web), y las excepciones en com.enconvert.exceptions.


Qué expone el cliente#

Enconvert lleva la superficie de conversión directamente. La superficie de inteligencia web vive en el campo público final client.v2, una instancia de EnconvertV2.

Grupo Métodos Devuelve
URL individual convertUrlToPdf, convertUrlToScreenshot, convertUrlToMarkdown ConversionResult
Subida de archivo convertImage, convertDocument, convertToMarkdown, convertToPdf ConversionResult
Sitio completo convertWebsiteToPdf, convertWebsiteToScreenshot BatchSubmission
Estado 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 mayoría de los métodos tienen una sobrecarga corta sin argumento de opciones, así que tanto client.v2.perceive(url) como client.convertUrlToPdf(url) compilan. convertImage, distill e ingest son las excepciones: cada uno recibe siempre su objeto de opciones, porque el formato de destino, el schema y el origen son obligatorios respectivamente.


Conversión de archivos#

Los endpoints de conversión cubren 43 pares {input}-to-{output} implementados, dos endpoints con detección automática (anything-to-markdown y anything-to-pdf) y los endpoints de renderizado en navegador. La referencia completa de parámetros está en Parámetros y opciones.

convertUrlToPdf#

Renderiza cualquier URL pública a PDF.

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());
Opción Tipo Por defecto Descripción
saveTo String ninguno Ruta local donde escribir el PDF. Los directorios padre se crean automáticamente.
singlePage boolean true true genera una única página continua. false pagina usando pdfOptions.pageSize.
pdfOptions PdfOptions ninguno Tamaño de página, orientación, márgenes, escala, escala de grises, encabezado, pie. Ver Opciones de PDF.
viewportWidth int 1920 Ancho del viewport del navegador en píxeles.
viewportHeight int 1080 Alto del viewport del navegador en píxeles.
loadMedia, enableScroll boolean true Espera a las imágenes y el vídeo antes de capturar, y desplaza de arriba abajo para disparar los cargadores diferidos.
outputFilename String auto Sustituye el nombre de archivo generado.
auth, cookies, headers HttpBasicAuth, List<BrowserCookie>, Map<String, String> ninguno Credenciales, cookies inyectadas y encabezados de solicitud extra para páginas tras un inicio de sesión.

convertUrlToScreenshot#

Captura un PNG de cualquier URL. Las mismas opciones de viewport, medios, desplazamiento, nombre de archivo, autenticación, cookies y encabezados que convertUrlToPdf, menos singlePage y pdfOptions.

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

convertUrlToMarkdown#

Extrae Markdown limpio con sabor GitHub desde una URL. Se eliminan la navegación, los pies de página, los anuncios y los scripts, se conserva el cuerpo principal del artículo y se antepone un frontmatter YAML (title, description, url, links, images). El mismo conjunto de opciones que convertUrlToScreenshot.

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

convertImage#

Convierte entre jpeg, png, svg, heic y webp, o rasteriza un PDF a JPEG.

// Desde una ruta en disco
client.convertImage(Path.of("photo.heic"),
        ConvertImageOptions.builder("webp").saveTo("photo.webp").build());

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

// Desde bytes en memoria con un nombre de archivo explícito
byte[] bytes = Files.readAllBytes(Path.of("photo.heic"));
client.convertImage(new FileInput(bytes, "photo.heic"),
        ConvertImageOptions.builder("webp").build());

Existen tres sobrecargas de entrada en cada método de archivo: java.nio.file.Path (lectura desde disco), byte[] en crudo (el nombre de archivo pasa a ser upload.bin por defecto) y com.enconvert.FileInput cuando necesitas emparejar bytes en memoria con un nombre de archivo real. El formato de entrada se resuelve a partir de la extensión; el formato de salida es obligatorio.

Opción Tipo Obligatorio Descripción
outputFormat String Se pasa a ConvertImageOptions.builder(outputFormat). Uno de jpeg, png, svg, heic, webp. Los alias como jpg se normalizan.
saveTo String no Ruta local donde escribir el resultado.
outputFilename String no Sustituye el nombre de archivo generado.

convertDocument#

Convierte documentos y formatos de datos estructurados. El formato de salida es pdf por defecto.

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

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

// markdown a pdf con configuración de página
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());

Extensiones de entrada reconocidas: .doc, .docx, .xls, .xlsx, .ppt, .pptx, .html, .htm, .odt, .ods, .odp, .ots, .pages, .numbers, .md, .markdown, .csv, .json, .xml, .yaml, .yml, .toml.

EPUB no tiene un par de documento propio. Envía los archivos .epub a través de convertToPdf o convertToMarkdown en su lugar.

Opción Tipo Por defecto Descripción
outputFormat String "pdf" Formato de destino.
saveTo String ninguno Ruta local donde escribir el resultado.
outputFilename String ninguno Sustituye el nombre de archivo generado.
pdfOptions PdfOptions ninguno Configuración de página, respetada cuando la salida es PDF.

Conversiones admitidas#

convertImage y convertDocument validan el par {input}-to-{output} contra los endpoints que la API implementa realmente. Un par no admitido lanza IllegalArgumentException de inmediato, enumerando las salidas válidas para esa entrada, en lugar de pagar una ida y vuelta por una solicitud que no puede tener éxito.

Entrada Salidas
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 entre sí, los 20 pares
pdf jpeg

La misma tabla se puede consultar en tiempo de ejecución a través de com.enconvert.Formats: Formats.validOutputsFor("json") devuelve [csv, toml, xml, yaml], Formats.validOutputsFor("pdf") devuelve [jpeg] y Formats.IMPLEMENTED_CONVERSIONS contiene los 43 nombres de endpoint.

convertToMarkdown#

Envía cualquier documento admitido a través de un único endpoint con detección automática y recibe Markdown limpio. La jerarquía de encabezados sobrevive, lo que convierte a esto en una primera etapa natural para un pipeline de RAG.

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

Acepta PDF, DOCX, PPTX, XLSX, CSV, HTML, EPUB, TXT y MD, además de formatos ofimáticos heredados y ODF. El formato se detecta en el servidor, así que no hay comprobación de extensión en el cliente: cualquier archivo se sube tal cual. Las imágenes no están admitidas y se rechazan con 400. Las únicas opciones son saveTo y outputFilename.

convertToPdf#

El otro endpoint con detección automática: casi cualquier cosa a PDF.

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

// pdf de paso directo, convertido a escala de grises
client.convertToPdf(Path.of("scan.pdf"),
        ConvertToPdfOptions.builder()
                .pdfOptions(PdfOptions.builder().grayscale(true).build())
                .saveTo("scan-gray.pdf")
                .build());

Acepta formatos ofimáticos, ODF, Pages, Numbers, RTF, CSV, HTML, Markdown, texto plano, imágenes rasterizadas, SVG, EPUB y un PDF existente como paso directo. EPUB se gestiona aquí porque no tiene un par de documento propio. Las opciones son saveTo, outputFilename y pdfOptions.

En este endpoint solo se respeta grayscale. La geometría de página (tamaño de página, ancho y alto, orientación, márgenes, escala, encabezado, pie) es ignorada por anything-to-pdf. Cuando necesites la configuración de página completa, pasa por convertDocument o convertUrlToPdf en su lugar.

Conversión de sitios completos#

convertWebsiteToPdf y convertWebsiteToScreenshot descubren todas las páginas de un sitio, convierten cada una en segundo plano y empaquetan los resultados en un único ZIP. Ambos son asíncronos y devuelven un BatchSubmission. Ambos requieren una clave de API privada.

BatchSubmission batch = client.convertWebsiteToPdf("https://example.com",
        WebsiteToPdfOptions.builder()
                .crawlMode("sitemap")                      // "auto" (por defecto), "sitemap", "full"
                .excludePatterns(List.of("/blog/tag/"))    // solo en modo de rastreo completo
                .notificationEmail("[email protected]")
                .build());

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

// Bloquea hasta que el lote salga de "processing", luego guarda el ZIP
BatchStatus status = client.waitForBatch(batch.batchId(),
        WaitForBatchOptions.builder().saveTo("site.zip").build());
System.out.println(status.completed() + " of " + status.total() + " pages converted");

convertWebsiteToScreenshot funciona igual y produce un ZIP de PNG. waitForBatch sondea cada 5 segundos por defecto, se rinde a los 30 minutos y acepta intervalMs, timeoutMs y saveTo. Al agotarse el tiempo lanza ApiException con estado 504.

Sondear el estado por tu cuenta#

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());

Inteligencia web (V2)#

Todo lo que hay bajo client.v2 convierte páginas web en datos listos para agentes. Cada lectura lleva renderQuality, una puntuación de 0.0 a 1.0 que indica con qué limpieza se renderizó realmente la página. Una página de desafío, un muro de cookies o el armazón vacío de una SPA vuelven con una puntuación baja y con warnings() y deductions() rellenos, en lugar de pasar por contenido real, de modo que una mala lectura nunca entra en silencio en el contexto de tu agente. El contenido se sigue devolviendo; simplemente queda marcado. Los fundamentos del modelo están en la visión general de V2.

Perceive#

Renderiza una URL en los artefactos que pidas. Referencia del endpoint: Perceive.

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 a 1.0
System.out.println(page.statusCode() + " " + page.deductions());  // p. ej. 200 {http_error=0.7}
System.out.println(page.outputs().get("markdown").url()); // URL firmada, 15 minutos
System.out.println(page.structured());                    // forma definida por quien llama
Opción Tipo Por defecto Descripción
outputs List<String> ["markdown", "structured"] Cualquiera de markdown, html_cleaned, html_raw, screenshot, screenshot_full_page, pdf, links, images, structured.
extract List<String> ninguno Objetivos heurísticos: tables, prices, contacts, metadata, main_content, headings, structured_data, technologies, all.
schema Map<String, Object> ninguno Schema JSON para la extracción estructurada.
waitFor, waitTimeoutMs String, int ninguno, 30000 Un selector CSS, opcionalmente con el prefijo css:, o js:<expr> que esperar, con un presupuesto de 0 a 60000 ms.
jsCode String ninguno JavaScript ejecutado tras la navegación, máximo 20000 caracteres.
viewport, mobile PerceiveViewport, boolean 1920 x 1080, false Ancho de 320 a 3840, alto de 240 a 2160, o emulación móvil.
onlyMainContent boolean true Elimina navegación, encabezado, pie y banners de cookies del artefacto Markdown y del extracto main_content.
cacheMode String "enabled" enabled reutiliza una caché de 1 hora, bypass la omite, refresh fuerza un nuevo renderizado.
blockResources List<String> ninguno Tipos de recurso que el navegador no debe cargar, por ejemplo image, font, script.
pdfOptions PdfOptions ninguno Solo tiene sentido cuando outputs contiene pdf.
headers, cookies, auth Map, List<BrowserCookie>, HttpBasicAuth ninguno Encabezados de solicitud, cookies inyectadas, credenciales HTTP Basic.
respectRobots boolean ninguno Respeta las reglas robots del sitio.
Todavía sin conectar. proxyUrl, geolocation y actionChain existen en el builder pero no están disponibles en el servidor y actualmente se rechazan con 422.

Las URL de artefacto se firman durante 15 minutos y se vuelven a firmar en cada lectura de la operación, así que client.v2.getPerceiveOperation(page.operationId()) te entrega enlaces frescos. Agrupa hasta 1000 URL con un único bloque de opciones compartido: los lotes pequeños se completan en línea, los más grandes vuelven con estado queued, así que sondéalos.

PerceiveBatchResult batch = client.v2.perceiveBatch(
        List.of("https://a.example.com", "https://b.example.com"),
        PerceiveBatchOptions.builder()
                .outputs(List.of("markdown"))
                .outputMode("zip")          // "manifest" (por defecto) o "zip"
                .build());

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

Sáltate por completo la ida y vuelta de la URL firmada con perceiveDirect, que devuelve los bytes del artefacto en streaming. Requiere exactamente una salida que produzca artefacto (cualquiera salvo structured) y lanza IllegalArgumentException antes de enviar si pides más o menos:

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());

// Vuelve a descargar un artefacto almacenado de una operación anterior
PerceiveDirectResult stored = client.v2.downloadPerceiveArtifact(page.operationId(), "markdown");

downloadPerceiveArtifact acepta un nombre de salida nulo u omitido cuando la operación produjo exactamente un artefacto; en caso contrario devuelve 400 enumerando las salidas disponibles. Una vez que el artefacto almacenado caduca, devuelve 410.

Discover#

Enumera las URL de un sitio sin renderizar nada. No interviene ningún navegador, así que es rápido. Referencia del endpoint: Discover.

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);
Opción Tipo Por defecto Rango
mode String "hybrid" sitemap, crawl, hybrid
maxUrls int 100 de 1 a 1000
maxDepth int 2 de 1 a 5
includePatterns, excludePatterns List<String> ninguno Lista de permitidos y lista de bloqueados con expresiones regulares, máximo 50 cada una. La lista de bloqueados se aplica en segundo lugar.
sameDomainOnly, respectRobots boolean true, ninguno Permanece en el dominio semilla y respeta las reglas robots del sitio.

Lookup#

Búsqueda web por categorías, con renderizado automático opcional de los primeros resultados. Referencia del endpoint: Lookup.

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)         // renderiza automáticamente los 3 primeros resultados
                .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());
});

Con perceiveTop por encima de 0 (de 0 a 10, por defecto 0), las URL de los N primeros resultados se renderizan mediante perceive y cada resultado lleva su PerceiveResult completo en línea en hit.perceive(). numResults va de 1 a 100 y su valor por defecto es 10; page va de 1 a 10.

Distill#

Extracción estructurada guiada por schema a través de una o varias páginas. Referencia del endpoint: Distill.

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()));

El cssSchema opcional ejecuta primero una pasada CSS gratuita; solo los campos que no puede resolver escalan al nivel del LLM, e item.extractionTier() informa de qué ruta produjo el registro (css, llm, mixed o none). También puedes descubrir las URL primero en lugar de enumerarlas:

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

Debe estar establecido exactamente uno de urls (máximo 50) y discoverFrom, y la factoría del builder exige schema. Ambas reglas se comprueban en el cliente y lanzan IllegalArgumentException antes de que salga ninguna solicitud.

Ingest#

Convierte un sitio entero, o un conjunto de documentos subidos, en JSONL troceado y listo para RAG mediante un único pipeline. Ingest siempre es asíncrono. Referencia del endpoint: Ingest.

// Desde un sitio
IngestJob job = client.v2.ingest(IngestOptions.builder()
        .mode("sitemap")                                  // "urls" (por defecto), "sitemap", "crawl"
        .url("https://docs.example.com")
        .maxPages(100)
        .chunk(new IngestChunkOptions(512, 1))            // maxWords, sentenceOverlap
        .webhookUrl("https://my.app/hooks/enconvert")
        .build());

// O desde archivos subidos
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());

// Sondea hasta obtener el 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());   // idempotente

ingestFiles acepta PDF, DOCX, PPTX, XLSX, CSV, HTML, EPUB, TXT y MD, además de formatos ofimáticos heredados y ODF. El troceado usa por defecto 512 palabras (de 32 a 4000) con 1 frase de solapamiento (de 0 a 10). mode es urls por defecto, lo que exige una lista urls no vacía y prohíbe url; cualquier otro modo exige una url semilla y prohíbe urls. El SDK impone ese emparejamiento antes de enviar.

Los webhooks de finalización se firman con HMAC. Obtén el secreto y los nombres de encabezado que necesitas para verificar una entrega, rótalo cuando se filtre y vuelve a disparar una entrega que tu endpoint se perdió:

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

client.v2.rotateWebhookSecret();          // las firmas antiguas dejan de verificarse de inmediato

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

Watch#

Monitorización recurrente de cambios en una URL, con notificación por correo electrónico y webhook. Referencia del endpoint: Watch.

Watcher watcher = client.v2.createWatcher("https://example.com/pricing",
        WatchCreateOptions.builder()
                .frequencyMinutes(60)      // de 60 a 43200, mínimo de una hora
                .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());   // borrado lógico, idempotente

listWatchers() y getWatcher(watcherId) los leen de vuelta. updateWatcher requiere al menos un campo y lanza IllegalArgumentException en caso contrario. Una cadena vacía explícita en webhookUrl borra el webhook, mientras que dejarlo nulo significa "sin cambios". deleteWatcher es un borrado lógico: devuelve el watcher marcado como eliminado con estado deleted, y a partir de ahí un watcher eliminado se lee como 404.

Los diffs de snapshot contienen contenido de página no confiable. WatcherSnapshot.changes() es texto en crudo tomado de la página monitorizada. Escápalo antes de renderizarlo en un panel, un correo electrónico o un mensaje de chat.

Opciones de PDF#

PdfOptions es compartido por convertUrlToPdf, convertWebsiteToPdf, convertDocument, convertToPdf y PerceiveOptions.pdfOptions.

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());
Campo Tipo Descripción
pageSize String "A4", "A3", "Letter", "Legal" y similares.
pageWidth, pageHeight double Dimensiones personalizadas. Establecidas juntas, prevalecen sobre pageSize.
orientation String "portrait" o "landscape".
margins PdfMargins Record de top, bottom, left, right. Cualquier campo nulo se omite.
scale double Escala de renderizado, por ejemplo 0.9 para el 90 por ciento.
grayscale boolean Posprocesa el PDF a escala de grises.
header, footer PdfHeaderFooter Record de content (máximo 2000 caracteres) y height.

BrowserCookie necesita un nombre y un valor, más domain o url; cuando se establece domain sin path, la API asigna / a path por defecto. No combines auth con un encabezado Authorization explícito, porque la API rechaza el conflicto.


Manejo de errores#

Toda excepción del SDK extiende EnconvertException, que a su vez extiende RuntimeException, así que nada obliga a una cláusula throws en tus puntos de llamada. Captura primero las subclases específicas.

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());
}
Clase Se lanza en Código de estado
AuthenticationException Clave de API ausente, inválida o sin permiso 401, 403
QuotaException HTTP 402 402
RateLimitException Demasiadas solicitudes 429
ApiException Cualquier otra respuesta 4xx o 5xx el código real
EnconvertException Clase base, también lanzada ante un fallo de transporte, una solicitud interrumpida o un archivo de entrada ilegible ninguno

La validación del lado del cliente (un par de conversión no admitido, un schema de distill ausente, una actualización de watcher vacía, un número incorrecto de salidas para perceiveDirect) lanza IllegalArgumentException antes de realizar ninguna solicitud. El mapa de mensajes de las respuestas del servidor está en la referencia de Códigos de error.


Recuperación de timeouts#

Los renderizados de URL largos y las conversiones de documentos grandes pueden sobrevivir al timeout del proxy inverso incluso cuando la conversión en sí acaba teniendo éxito. El SDK lo gestiona de forma transparente:

  1. Antes de cada solicitud de URL individual o de subida de archivo, el SDK genera un UUID y lo envía como job_id.
  2. Si la solicitud vuelve con 5xx, el SDK pasa a sondear GET /v1/convert/status/{jobId} cada 3 segundos.
  3. Ante success devuelve el resultado. Ante failed lanza ApiException con el mensaje de error del servidor.
  4. El plazo de sondeo es de 5 minutos. Pasado ese punto lanza ApiException(504, "Conversion timed out").

No escribes ningún código para esto. Si una respuesta correcta omite job_id, el SDK rellena el id que generó, de modo que result.jobId() siempre se puede usar con getJobStatus.

Los envíos de lotes de sitios web quedan excluidos a propósito. convertWebsiteToPdf y convertWebsiteToScreenshot no tienen una fila por job que sondear, así que un 5xx ahí significa que falló el propio envío y se expone directamente. Los endpoints V2 tampoco usan sondeo de jobs; sus flujos asíncronos pasan por getPerceiveBatch y getIngestJob.

Configuración#

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

Hay tres constructores disponibles como atajo: new Enconvert(apiKey), new Enconvert(apiKey, baseUrl) y new Enconvert(apiKey, baseUrl, timeout).

Opción Tipo Por defecto Descripción
apiKey String obligatorio Clave de API privada. Un valor nulo o vacío lanza IllegalArgumentException.
baseUrl String https://api.enconvert.com URL base de la API. Las barras finales se eliminan.
timeout Duration 300 segundos Se aplica tanto como timeout de conexión como timeout por solicitud.

La clave viaja en el encabezado X-API-Key. Las URL de descarga prefirmadas se obtienen sin ella, ya que vienen firmadas. Los tipos de clave se tratan en Autenticación; crea y gestiona claves en el panel de control.

Nunca incrustes la clave de API en el código. Léela desde una variable de entorno o desde tu gestor de secretos. El SDK es solo del lado del servidor: una clave privada no debe viajar dentro de un artefacto de escritorio o móvil que un usuario pueda desempaquetar.

Forma del resultado#

Toda conversión de un solo archivo o de una sola URL devuelve el mismo record:

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

La URL prefirmada tiene un tiempo limitado. Pasa saveTo (o descarga la URL tú mismo) y guarda los bytes en tu propio bucket si necesitas que le sobrevivan.

Los demás records de respuesta que tocarás con más frecuencia:

Record Accesores principales
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()

Los campos cuya forma define tu propia solicitud (structured, data, trackFields, los changes del snapshot) se exponen como JsonObject de Gson y pasan intactos. Las enumeraciones con valores de cadena siguen siendo String en lugar de convertirse en constantes enum de Java, así que un valor más reciente de la API nunca rompe la deserialización en una build más antigua del SDK. com.enconvert.model.v2.V2Enums contiene cada valor aceptado como una constante a prueba de erratas.


Código fuente e incidencias#


Preguntas frecuentes#

¿Cómo convierto archivos en Java con una dependencia de Maven?#

Añade com.enconvert:enconvert-sdk:0.0.1 a tu pom.xml o build.gradle, construye un cliente con new Enconvert(System.getenv("ENCONVERT_API_KEY")) y llama a un método tipado como convertUrlToPdf, convertImage, convertDocument o convertToPdf. Pasa saveTo en el builder de opciones para escribir la salida directamente en disco en lugar de gestionar tú la URL prefirmada.

¿Cómo convierto una URL a PDF en Java?#

Llama a client.convertUrlToPdf(url, UrlToPdfOptions.builder().saveTo("page.pdf").build()). Establece singlePage(false) para paginar usando pdfOptions.pageSize en lugar de producir una única página continua, y pasa auth, cookies o headers para una página tras un inicio de sesión.

¿Cómo convierto DOCX a PDF en Java?#

Llama a client.convertDocument(Path.of("report.docx"), ConvertDocumentOptions.builder().saveTo("report.pdf").build()). El formato de salida es pdf por defecto, así que solo estableces outputFormat cuando quieres otra cosa, por ejemplo yaml a partir de una entrada .json. Para formatos sin un par propio, como EPUB o RTF, usa convertToPdf.

¿Cómo convierto HEIC a WebP en Java?#

Llama a client.convertImage(Path.of("photo.heic"), ConvertImageOptions.builder("webp").saveTo("photo.webp").build()). El formato de entrada procede de la extensión del archivo y el formato de salida es el argumento obligatorio del builder. jpeg, png, svg, heic y webp se convierten todos entre sí, y pdf se rasteriza a jpeg. En este cliente no hay un método de compresión in situ.

¿Cómo extraigo una página web a Markdown limpio desde Java?#

Dos caminos. client.convertUrlToMarkdown(url, ...) devuelve Markdown con sabor GitHub y frontmatter YAML como archivo descargable. client.v2.perceive(url, PerceiveOptions.builder().outputs(List.of("markdown")).build()) devuelve el mismo contenido como un artefacto listo para agentes, con una puntuación renderQuality, opciones de extracción y control de caché. Usa perceive cuando una mala lectura tenga que ser detectable en lugar de silenciosa.

¿Qué es renderQuality y por qué toda lectura tiene una?#

renderQuality es una puntuación de 0.0 a 1.0 adjunta a cada renderizado V2. Una puntuación alta significa que la página se renderizó limpiamente; una baja significa que algo se interpuso, como un desafío antibots, un muro de cookies, una pantalla de inicio de sesión, una página de error HTTP o el armazón vacío de una SPA. El contenido se sigue devolviendo, con warnings() y deductions() rellenos, para que tu pipeline pueda descartar o reintentar la lectura en lugar de alimentar un modelo con una página de desafío como si fuera el artículo.

¿Cómo convierto un sitio de documentación en fragmentos listos para RAG en Java?#

Llama a client.v2.ingest(IngestOptions.builder().mode("sitemap").url("https://docs.example.com").maxPages(100).chunk(new IngestChunkOptions(512, 1)).build()). El job es asíncrono, así que sondea getIngestJob(jobId) hasta que el estado sea completed y lee outputUrl() para obtener el JSONL, o bien establece webhookUrl y verifica la firma HMAC con el secreto de getWebhookSecret(). Para documentos locales en lugar de un sitio, usa ingestFiles.

¿Cómo gestiona el SDK las conversiones que superan el timeout del proxy?#

Antes de cada solicitud de URL individual o de subida de archivo genera un UUID y lo envía como job_id. Si la solicitud devuelve 5xx, sondea GET /v1/convert/status/{jobId} cada 3 segundos hasta que el job informe de success o failed, con un plazo de 5 minutos tras el cual lanza ApiException(504, "Conversion timed out"). Los envíos de lotes de sitios completos quedan excluidos, porque no tienen una fila por job que sondear.

¿Qué versión de Java requiere el SDK y qué arrastra consigo?#

Java 17 o posterior. HTTP pasa por el java.net.http.HttpClient del JDK, y Gson es el único artefacto de terceros en el classpath. Las respuestas son records de Java, así que un switch moderno o un pattern match sobre ellas funciona como cabe esperar. El cliente es seguro entre hilos: mantén una instancia y compártela.