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.
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 |
Sí | 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.
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. |
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.
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:
- Antes de cada solicitud de URL individual o de subida de archivo, el SDK genera un UUID y lo envía como
job_id. - Si la solicitud vuelve con 5xx, el SDK pasa a sondear
GET /v1/convert/status/{jobId}cada 3 segundos. - Ante
successdevuelve el resultado. AntefailedlanzaApiExceptioncon el mensaje de error del servidor. - 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.
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.
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#
- Maven Central:
com.enconvert:enconvert-sdk:0.0.1 - GitHub: conversionapi/java-sdk
- Licencia: MIT
- Otros clientes: todos los SDK · referencia de endpoints · precios
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.