SDK de Conversión de Archivos para PHP#

enconvert/enconvert-php es el cliente oficial de PHP para la API de EnConvert. Instálalo con Composer, entrégale una clave de API y obtienes dos cosas: doce métodos de conversión que convierten URL, imágenes y documentos en PDF, PNG, Markdown y formatos de datos, y un espacio de nombres $client->v2 que lee la web en vivo y la transforma en Markdown, capturas de pantalla y JSON estructurado listos para agentes. Está dirigido a PHP 8.1+, se construye sobre Guzzle 7, usa arrays de opciones en camelCase de principio a fin y devuelve objetos de resultado tipados de solo lectura en lugar de arrays sueltos. Las conversiones largas que superan un timeout HTTP se recuperan automáticamente mediante sondeo del job.

Composer: enconvert/enconvert-php · Fuente: conversionapi/php-sdk · PHP: 8.1+ · Requiere: guzzlehttp/guzzle ^7.8

Instalación#

composer require enconvert/enconvert-php

El paquete se autocarga bajo el espacio de nombres PSR-4 Enconvert\ y no incluye CLI, ni archivo de configuración, ni service provider. Funciona sin cambios en PHP puro, Laravel, Symfony, WordPress y cualquier proyecto PSR-4.


Inicio rápido#

<?php

require __DIR__ . '/vendor/autoload.php';

use Enconvert\Client;

$client = new Client(getenv('ENCONVERT_API_KEY'));

// Convierte una página en vivo a PDF y transmítela directamente a disco.
$result = $client->convertUrlToPdf('https://example.com', [
    'saveTo' => 'page.pdf',
]);

echo $result->presignedUrl, ' ', $result->fileSize, ' bytes', PHP_EOL;

// Leer una página para un agente usa el mismo cliente y la misma clave.
$op = $client->v2->perceive('https://example.com', ['outputs' => ['markdown', 'structured']]);

echo $op->renderQuality, PHP_EOL;              // de 0.0 a 1.0, p. ej. 0.93
echo $op->outputs['markdown']->url, PHP_EOL;   // URL de descarga firmada

El SDK es solo del lado del servidor. Una clave privada (sk_live_...) nunca debe llegar a un navegador ni a una aplicación móvil.


Qué expone el cliente#

Superficie Cómo se accede Qué hace
Conversión de archivos y URL $client->convert*() Doce métodos sobre POST /v1/convert/*, más sondeo de jobs y lotes.
Inteligencia web (V2) $client->v2 (o $client->v2()) Veintitrés métodos sobre /v2/*: perceive, discover, lookup, distill, ingest, watch.
Introspección de formatos Enconvert\Formats Los 43 pares {input}-to-{output} implementados, búsqueda de MIME y listas de salidas por entrada.
Errores Enconvert\Exception\* La base EnconvertException más ApiException, AuthenticationException, QuotaException, RateLimitException.
Resultados Enconvert\Model\* Objetos de valor de solo lectura con propiedades en camelCase.

$client->v2 es una propiedad pública de solo lectura. $client->v2() es un accesor idéntico para quien prefiera la sintaxis de método. Todos los endpoints V2 requieren una clave de API privada; las claves públicas se rechazan.


Conversión de archivos#

Cada método de un solo archivo devuelve un ConversionResult que lleva una URL de descarga prefirmada. Si pasas saveTo, el SDK además transmite los bytes a esa ruta local, creando los directorios padre que hagan falta.

convertUrlToPdf#

Renderiza a PDF cualquier URL accesible.

$result = $client->convertUrlToPdf('https://example.com', [
    'singlePage' => false,
    'pdfOptions' => ['pageSize' => 'A4', 'orientation' => 'landscape'],
    'viewportWidth' => 1440,
    'saveTo' => 'report.pdf',
]);
Opción Tipo Por defecto Descripción
saveTo string ninguno Ruta local a la que transmitir el PDF. Los directorios padre se crean.
singlePage bool true true genera una única página continua. false pagina usando pdfOptions.pageSize.
pdfOptions array ninguno Tamaño de página, orientación, márgenes, escala, escala de grises, encabezado, pie. Ver Opciones de PDF.
viewportWidth, viewportHeight int 1920, 1080 Tamaño del viewport del navegador en píxeles.
loadMedia bool true Espera a las imágenes y el vídeo antes de capturar.
enableScroll bool true Desplaza de arriba abajo para que se disparen los cargadores diferidos.
outputFilename string auto Sustituye el nombre de archivo generado.
auth, cookies, headers array ninguno Acceso a la página: Basic Auth ['username' => ..., 'password' => ...], cookies inyectadas, encabezados de solicitud personalizados.
No combines auth con un encabezado Authorization. La API rechaza el conflicto en lugar de adivinar qué credencial querías usar.

convertUrlToScreenshot#

Captura un PNG de cualquier URL.

$shot = $client->convertUrlToScreenshot('https://example.com', [
    'viewportWidth' => 1440,
    'saveTo' => 'shot.png',
]);

Acepta las mismas opciones de viewport, medios, desplazamiento, nombre de archivo y acceso a la página que convertUrlToPdf, menos singlePage y pdfOptions.

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).

$md = $client->convertUrlToMarkdown('https://example.com/article', [
    'saveTo' => 'article.md',
]);

El mismo conjunto de opciones que convertUrlToScreenshot.

convertImage#

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

// Desde una ruta.
$client->convertImage('photo.heic', ['outputFormat' => 'webp', 'saveTo' => 'photo.webp']);

// Desde bytes en crudo. El nombre de archivo resuelve el formato de entrada y el tipo MIME.
$client->convertImage(
    ['data' => file_get_contents('photo.heic'), 'filename' => 'photo.heic'],
    ['outputFormat' => 'webp', 'saveTo' => 'photo.webp']
);

$client->convertImage('scan.pdf', ['outputFormat' => 'jpeg', 'saveTo' => 'scan.jpeg']); // rasteriza

El formato de entrada procede de la extensión del archivo: .jpg, .jpeg, .png, .svg, .heic, .webp y .pdf. El formato de salida es obligatorio y se normaliza por ti, así que jpg se resuelve como jpeg.

Opción Tipo Obligatorio Descripción
outputFormat string Uno de jpeg, png, svg, heic, webp.
saveTo string No Ruta local a la que transmitir el resultado.
outputFilename string No Sustituye el nombre de archivo generado.

convertDocument#

Convierte documentos y formatos de datos. outputFormat es pdf por defecto.

// docx a pdf, luego json a yaml
$client->convertDocument('report.docx', ['saveTo' => 'report.pdf']);
$client->convertDocument('data.json', ['outputFormat' => 'yaml', 'saveTo' => 'data.yaml']);

// markdown a pdf con configuración de página
$client->convertDocument('README.md', [
    'outputFormat' => 'pdf',
    'pdfOptions' => ['pageSize' => 'A4', 'margins' => ['top' => 20, 'bottom' => 20]],
    'saveTo' => 'readme.pdf',
]);

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.

Opción Tipo Por defecto Descripción
outputFormat string "pdf" Formato de destino. Los alias jpg, yml, htm y md se resuelven.
saveTo string ninguno Ruta local a la que transmitir el resultado.
outputFilename string ninguno Sustituye el nombre de archivo generado.
pdfOptions array ninguno Configuración de página. Se respeta cuando la salida es PDF.

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

Pares de conversión admitidos#

Entrada Salidas
json csv, toml, xml, yaml
xml csv, json
yaml, toml json
csv json, xml
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

El SDK valida el par {input}-to-{output} contra esa tabla y lanza EnconvertException antes de que ninguna solicitud salga de tu proceso, con la lista de salidas válidas para esa entrada en el mensaje. Puedes consultar la misma tabla directamente:

use Enconvert\Formats;

Formats::validOutputsFor('json');   // ["csv", "toml", "xml", "yaml"]
Formats::validOutputsFor('pdf');    // ["jpeg"]
Formats::normalizeOutputFormat('JPG');   // "jpeg"
Formats::mimeFor('deck.pptx');           // el tipo MIME de PPTX
count(Formats::IMPLEMENTED_CONVERSIONS); // 43

convertToMarkdown#

Detecta automáticamente el formato de un documento en el servidor y devuelve Markdown. Este es el bloque de construcción para ingesta de RAG a partir de archivos que ya tienes en disco.

$client->convertToMarkdown('handbook.docx', ['saveTo' => 'handbook.md']);

Acepta PDF, DOCX, PPTX, XLSX, CSV, HTML, EPUB, TXT/MD y archivos ofimáticos heredados u ODF. Las imágenes no están admitidas. Las opciones son saveTo y outputFilename; este endpoint no tiene opciones de PDF.

convertToPdf#

Detecta automáticamente casi cualquier entrada y devuelve un PDF.

$client->convertToPdf('slides.pptx', ['saveTo' => 'slides.pdf']);

// Una entrada PDF pasa directamente, así que esto sirve también para normalizar a escala de grises.
$client->convertToPdf('scan.pdf', ['pdfOptions' => ['grayscale' => true], 'saveTo' => 'gray.pdf']);

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.

Aquí solo se respeta pdfOptions.grayscale. El formato se detecta en el servidor, así que la geometría de página viene del documento de origen. Usa convertDocument o convertUrlToPdf cuando necesites tamaño de página, márgenes, orientación, encabezados o pies.

convertWebsiteToPdf y convertWebsiteToScreenshot#

Descubren todas las páginas de un sitio, convierten cada una en segundo plano y reúnen un único ZIP. Ambos son asíncronos y devuelven un BatchSubmission en lugar de un ConversionResult. Requieren una clave de API privada con acceso de rastreo.

$batch = $client->convertWebsiteToPdf('https://example.com', [
    'crawlMode' => 'sitemap',              // "auto" (por defecto) | "sitemap" | "full"
    'excludePatterns' => ['/blog/tag/'],   // solo en modo de rastreo completo
    'notificationEmail' => '[email protected]',
]);

echo $batch->batchId, ' ', $batch->urlCount, ' ', $batch->discoveryMethod, PHP_EOL;

// Bloquea hasta que el lote salga de "processing", luego guarda el ZIP.
$status = $client->waitForBatch($batch->batchId, ['saveTo' => 'site.zip']);
echo $status->completed, ' of ', $status->total, ' pages converted', PHP_EOL;

// O sondéalo tú mismo con getBatchStatus().
$s = $client->getBatchStatus($batch->batchId);
echo $s->status === 'processing' ? 'still working' : $s->zipDownloadUrl, PHP_EOL;

convertWebsiteToScreenshot toma las mismas opciones menos singlePage y pdfOptions, y produce un ZIP de PNG.

Opción Tipo Por defecto Descripción
crawlMode string "auto" auto, sitemap o full.
includePatterns, excludePatterns string[] ninguno Conserva o descarta URL por patrón. Las exclusiones se aplican en modo de rastreo completo.
notificationEmail, callbackUrl string ninguno Dirección de correo y webhook a los que notificar al finalizar.
viewportWidth, viewportHeight, loadMedia, enableScroll mixto valor del servidor Opciones de renderizado por página. Se envían solo cuando las estableces.
auth, cookies, headers array ninguno Acceso a la página para sitios tras un inicio de sesión.
outputFilename string ninguno Sustituye el nombre de archivo del ZIP.

waitForBatch acepta intervalMs (por defecto 5000), timeoutMs (por defecto 1800000, es decir 30 minutos) y saveTo. Lanza ApiException con estado 504 si vence el plazo mientras el lote sigue procesándose.

getJobStatus#

Sondea un único job de conversión asíncrono o recuperado.

$status = $client->getJobStatus('job_abc123');

if ($status->status === 'success') {
    echo $status->presignedUrl, PHP_EOL;
} elseif ($status->status === 'failed') {
    echo $status->error, PHP_EOL;
}

Devuelve un JobStatus con status (processing, success o failed), presignedUrl, objectKey y error. Rara vez lo necesitas directamente, porque el SDK sondea en tu nombre. Ver Recuperación de timeouts.


Inteligencia web (V2)#

El espacio de nombres $client->v2 convierte páginas web en vivo en datos listos para agentes: renderizar, buscar, extraer, ingerir y monitorizar. Cada lectura lleva renderQuality, una puntuación de 0.0 a 1.0 que distingue un renderizado real de uno fallido. Una página de desafío, un muro de cookies, una pantalla de inicio de sesión o el armazón vacío de una SPA vuelven con una puntuación baja y con warnings rellenos, de modo que una mala lectura nunca entra en silencio en el contexto de un agente. El contenido se sigue devolviendo; queda marcado, no oculto. Lee los conceptos en la visión general de V2.

Perceive#

Renderiza una URL y materializa las salidas que hayas pedido a partir de ese único renderizado. Ver la referencia de perceive.

$op = $client->v2->perceive('https://example.com/pricing', [
    'outputs' => ['markdown', 'screenshot', 'structured'],
    'extract' => ['tables', 'metadata'],
    'onlyMainContent' => true,
    'waitFor' => '.price',
]);

echo $op->renderQuality, ' ', $op->statusCode, PHP_EOL;  // puntuación, estado HTTP del origen
print_r($op->deductions);                       // p. ej. ["login_wall" => 0.65]
echo $op->outputs['markdown']->url, PHP_EOL;    // URL firmada, 15 minutos
print_r($op->structured);

// Las URL firmadas caducan. Vuelve a firmarlas después sin renderizar de nuevo.
$again = $client->v2->getPerceiveOperation($op->operationId);
Opción Tipo Descripción
outputs string[] Cualquiera de markdown, html_cleaned, html_raw, screenshot, screenshot_full_page, pdf, links, images, structured.
extract string[] Campos estructurados que extraer, por ejemplo metadata, structured_data, headings, tables, main_content, all.
schema array Schema para la extracción estructurada respaldada por LLM.
onlyMainContent bool Elimina el armazón del sitio de la salida Markdown.
waitFor, waitTimeoutMs string, int Espera a un selector CSS o a una expresión JS tras la navegación.
jsCode string JavaScript que ejecutar en la página tras la navegación.
viewport, mobile, blockResources array, bool, string[] Geometría de renderizado y tipos de recurso que abortar antes de que carguen.
cacheMode string enabled, bypass o refresh.
headers, cookies, auth array Acceso a páginas autenticadas.
respectRobots bool Rechaza las URL desautorizadas por robots.txt.
pdfOptions array Configuración de página para la salida pdf.
directDownload bool Devuelve los bytes del artefacto en crudo. Solo lo acepta perceive().
proxyUrl, geolocation, actionChain mixto Serializados por el SDK, reservados por la API.

Percepción por lotes#

$batch = $client->v2->perceiveBatch(['https://a.com', 'https://b.com'], [
    'outputs' => ['markdown'],
    'outputMode' => 'zip',
]);

// Los lotes pequeños se completan en línea. Los más grandes vuelven en cola, así que sondea.
if ($batch->status !== 'completed') {
    $batch = $client->v2->getPerceiveBatch($batch->jobId);
}
foreach ($batch->items as $item) {
    echo $item->url, ' ', $item->renderQuality, PHP_EOL;
}
echo $batch->zip?->url, PHP_EOL;

Hasta 1000 URL comparten un único bloque de opciones. outputMode es manifest (por defecto) o zip. Aquí no se acepta directDownload.

Transmitir un único artefacto#

perceiveDirect() se salta el envoltorio JSON y te entrega los bytes. Hay que solicitar exactamente una salida que produzca artefacto, y el SDK lo impone antes de enviar, así que structured por sí solo lanza excepción.

$direct = $client->v2->perceiveDirect('https://example.com', [
    'outputs' => ['pdf'],
]);

file_put_contents($direct->filename ?? 'page.pdf', $direct->content);
echo $direct->renderQuality, ' ', $direct->contentType, PHP_EOL;
echo $direct->operationId, ' cache hit: ', var_export($direct->cacheHit, true), PHP_EOL;

// Vuelve a descargar después un artefacto almacenado. Omite el nombre de salida
// cuando la operación produjo exactamente un artefacto.
$saved = $client->v2->downloadPerceiveArtifact($direct->operationId, 'pdf');

PerceiveDirectResult lleva content, contentType, filename, operationId, objectKey, cacheHit, renderQuality, sourceStatusCode, contentHash y warningsCount. Una ApiException con estado 410 significa que el artefacto almacenado ya no está disponible.

Discover#

Enumera las URL de un sitio solo por HTTP. Sin renderizado en navegador, así que es rápido. Ver la referencia de discover.

$found = $client->v2->discover('https://example.com', [
    'mode' => 'hybrid',                 // "sitemap" | "crawl" | "hybrid"
    'maxUrls' => 200,
    'maxDepth' => 3,
    'excludePatterns' => ['/tag/'],
    'sameDomainOnly' => true,
]);

echo $found->total, ' urls, truncated: ', var_export($found->truncated, true), PHP_EOL;
print_r($found->sources);   // p. ej. ["sitemap" => 42, "crawl" => 30]

Las opciones son mode, maxUrls, maxDepth, includePatterns, excludePatterns, sameDomainOnly y respectRobots.

Lookup#

Ejecuta una búsqueda web por categorías y, opcionalmente, renderiza automáticamente los primeros resultados. Ver la referencia de lookup.

$search = $client->v2->lookup('best static site generators', [
    'category' => 'web',        // web | news | images | scholar | patents | maps
    'numResults' => 10,
    'country' => 'us',
    'timeFilter' => 'month',
    'perceiveTop' => 3,         // renderiza automáticamente los 3 primeros resultados
]);

foreach ($search->results as $hit) {
    echo $hit->position, '. ', $hit->title, ' ', $hit->url, PHP_EOL;
    echo '   quality ', $hit->perceive?->renderQuality ?? 'not perceived', PHP_EOL;
}

Las opciones son category, country, locale, timeFilter, numResults, page, location, autocorrect y perceiveTop.

Distill#

Extracción estructurada guiada por schema sobre una lista de URL o un sitio descubierto. Ver la referencia de distill.

$extraction = $client->v2->distill([
    'urls' => ['https://example.com/pricing'],
    'schema' => ['plans' => 'list of plan names with monthly prices'],
    'cssSchema' => [                     // pasada CSS gratuita opcional antes del nivel del LLM
        'baseSelector' => '.plan-card',
        'fields' => [
            ['name' => 'name', 'type' => 'text', 'selector' => 'h3'],
            ['name' => 'price', 'type' => 'text', 'selector' => '.price'],
        ],
    ],
]);

print_r($extraction->results[0]->data);
echo $extraction->results[0]->extractionTier, PHP_EOL;  // css | llm | mixed | none
echo $extraction->results[0]->fieldsFromCss, '/', $extraction->results[0]->fieldsFromLlm, PHP_EOL;

// O descubre primero el conjunto de URL.
$client->v2->distill([
    'discoverFrom' => ['url' => 'https://example.com', 'mode' => 'sitemap', 'maxPages' => 10],
    'schema' => ['title' => 'page title', 'summary' => 'one-line summary'],
]);

schema es obligatorio, y debe estar presente exactamente uno de urls o discoverFrom. Si incumples cualquiera de las dos reglas, el SDK lanza EnconvertException antes de enviar. Las demás opciones son waitFor, waitTimeoutMs, headers, cookies y respectRobots.

Ingest#

Convierte un sitio entero, o un montón de documentos subidos, en JSONL troceado y listo para RAG mediante un único pipeline. Ingest siempre es asíncrono. Ver la referencia de ingest.

// Desde un sitio.
$job = $client->v2->ingest([
    'mode' => 'sitemap',                 // "urls" (por defecto) | "sitemap" | "crawl"
    'url' => 'https://docs.example.com',
    'maxPages' => 100,
    'chunk' => ['maxWords' => 512, 'sentenceOverlap' => 1],
    'webhookUrl' => 'https://my.app/hooks/enconvert',
]);

// O desde archivos subidos: PDF, DOCX, PPTX, XLSX, CSV, HTML, EPUB, TXT/MD, ofimática heredada y ODF.
$fileJob = $client->v2->ingestFiles(['handbook.pdf', 'notes.docx'], [
    'chunk' => ['maxWords' => 512, 'sentenceOverlap' => 1],
]);

// Sondea hasta que aterrice.
do {
    sleep(5);
    $status = $client->v2->getIngestJob($job->jobId);
} while (in_array($status->status, ['queued', 'discovering', 'processing'], true));

if ($status->status === 'completed') {
    echo $status->totalChunks, ' chunks: ', $status->outputUrl, PHP_EOL;  // JSONL firmado
}

mode es urls por defecto, lo que exige un array urls no vacío y rechaza url. Los modos sitemap y crawl exigen una url semilla y rechazan urls. El SDK impone ambas reglas localmente. Las demás opciones son maxPages, maxDepth, sameDomainOnly, includePatterns, excludePatterns, respectRobots, waitFor, waitTimeoutMs, chunk y webhookUrl.

// Gestión de jobs.
$list = $client->v2->listIngestJobs(['limit' => 20, 'skip' => 0]);
foreach ($list->jobs as $summary) {
    echo $summary->jobId, ' ', $summary->status, ' ', $summary->totalChunks, PHP_EOL;
}
$client->v2->cancelIngestJob($job->jobId);        // idempotente

// Firma de webhooks.
$secret = $client->v2->getWebhookSecret();
echo $secret->signatureHeader, ' ', $secret->signatureScheme, PHP_EOL;
$client->v2->rotateWebhookSecret();               // las firmas antiguas dejan de verificarse
$client->v2->retryIngestWebhook($job->jobId);     // reenvía un callback de finalización

Watch#

Vuelve a renderizar una URL a una cadencia fija y te avisa cuando cambia. Ver la referencia de watch.

$watcher = $client->v2->createWatcher('https://example.com/pricing', [
    'frequencyMinutes' => 60,            // mínimo de una hora
    'diffMode' => 'auto',                // auto | text | structured | tables | metadata
    'webhookUrl' => 'https://my.app/hooks/changes',
    'notifyEmail' => true,
]);

echo $watcher->watcherId, ' next check ', $watcher->nextCheckAt, PHP_EOL;

$snapshots = $client->v2->getWatcherSnapshots($watcher->watcherId, ['limit' => 10]);
foreach ($snapshots->snapshots as $snap) {
    if ($snap->hasChanges) {
        echo $snap->checkedAt, ' similarity ', $snap->similarity,
             ' changes ', $snap->changeCount, PHP_EOL;
    }
}
$client->v2->updateWatcher($watcher->watcherId, ['status' => 'paused']);
$client->v2->updateWatcher($watcher->watcherId, ['webhookUrl' => '']);  // borra el webhook
$client->v2->deleteWatcher($watcher->watcherId);                        // borrado lógico, idempotente

createWatcher acepta frequencyMinutes, diffMode, trackFields, webhookUrl y notifyEmail. updateWatcher acepta esos cinco más status, y requiere al menos un campo o lanza EnconvertException. listWatchers acepta skip y limit.

Los diffs de snapshot contienen contenido de página no confiable. Las entradas `changes` de un `WatcherSnapshot` se copian de la página monitorizada. Escápalas con `htmlspecialchars()` antes de renderizarlas en tu propia interfaz.

Opciones de PDF#

Se pasan como el array pdfOptions en convertUrlToPdf, convertDocument, convertToPdf (solo escala de grises), convertWebsiteToPdf y el perceive de V2 con una salida pdf. El SDK serializa las claves camelCase al formato de transmisión de la API y envía solo las claves que estableces.

$client->convertUrlToPdf('https://example.com', [
    'pdfOptions' => [
        'pageSize' => 'A4',
        'orientation' => 'landscape',
        'margins' => ['top' => 10, 'bottom' => 10, 'left' => 15, 'right' => 15],
        'scale' => 0.9,
        'header' => ['content' => 'Quarterly Report', 'height' => 15],
        'footer' => ['content' => 'Page {{page}} of {{total_pages}}', 'height' => 12],
    ],
    'singlePage' => false,
    'saveTo' => 'report.pdf',
]);
Campo Tipo Descripción
pageSize string De A0 a A6, de B0 a B5, Letter, Legal, Tabloid, Ledger. Se ignora cuando pageWidth y pageHeight están ambos establecidos.
pageWidth, pageHeight float Tamaño de página personalizado en milímetros. Establécelos juntos.
orientation string portrait o landscape.
margins array ['top' => ..., 'bottom' => ..., 'left' => ..., 'right' => ...] en milímetros.
scale float Escala de renderizado, de 0.1 a 2.0. Solo para salida paginada.
grayscale bool Posprocesa el PDF a escala de grises.
header array ['content' => '<html>', 'height' => 15]. Altura en milímetros.
footer array La misma forma que header.

El contenido del encabezado y del pie admite las variables de plantilla {{page}}, {{total_pages}}, {{date}}, {{title}} y {{url}}. La referencia completa de parámetros vive en parámetros y opciones.


Manejo de errores#

Todo lo que lanza el SDK desciende de Enconvert\Exception\EnconvertException, así que un único bloque catch puede acotar toda la superficie.

use Enconvert\Exception\{
    ApiException, AuthenticationException, EnconvertException, QuotaException, RateLimitException
};

try {
    $client->convertUrlToPdf('https://example.com', ['saveTo' => 'page.pdf']);
} catch (AuthenticationException $e) {
    error_log('Check ENCONVERT_API_KEY: ' . $e->getMessage());
} catch (RateLimitException $e) {
    error_log('Too many requests, back off and retry.');
} catch (QuotaException $e) {
    error_log($e->getMessage());
} catch (ApiException $e) {
    error_log(sprintf('API error [%d]: %s', $e->getStatusCode(), $e->getMessage()));
} catch (EnconvertException $e) {
    error_log('Client-side or transport failure: ' . $e->getMessage());
}
Clase Se lanza en Código de estado
AuthenticationException Clave de API inválida, ausente o revocada respuestas 401 y 403
QuotaException HTTP 402 402
RateLimitException Demasiadas solicitudes 429
ApiException Cualquier otra respuesta 4xx o 5xx el código real
EnconvertException Clase base, y todo fallo del lado del cliente ninguno

ApiException::getStatusCode() devuelve el estado numérico; el mensaje lleva el prefijo [code]. Una respuesta 403 se traduce en AuthenticationException, cuyo getStatusCode() informa de 401, así que ramifica según la clase y no según el número cuando necesites distinguir ambos casos.

EnconvertException también se lanza sin ninguna ida y vuelta de red cuando el SDK detecta que la solicitud está condenada: una clave de API vacía, una ruta de archivo ausente, una extensión de archivo no reconocida, un par de conversión no implementado, una llamada a distill sin schema o con urls y discoverFrom a la vez, un desajuste entre el modo y la carga útil de ingest, una lista ingestFiles vacía, una llamada a perceiveDirect que no nombra exactamente una salida de artefacto, una llamada a updateWatcher sin campos y cualquier fallo de transporte de Guzzle (expuesto como HTTP request failed: ...).

El mapa completo de mensajes está en la referencia de códigos de error.


Recuperación de timeouts#

Las conversiones de documentos grandes y las páginas lentas pueden sobrevivir al timeout de un proxy inverso incluso cuando la conversión acaba teniendo éxito en el servidor. El SDK se recupera de eso por su cuenta:

  1. Antes de cada solicitud de conversión genera un id de job hexadecimal de 32 caracteres y lo envía como job_id en el cuerpo o en el formulario multiparte.
  2. Si esa solicitud vuelve con 5xx, el SDK deja de lanzar excepciones y empieza a sondear GET /v1/convert/status/{job_id} cada 3 segundos.
  3. En cuanto el job se lee como success, el SDK devuelve el resultado. Si se lee como failed, lanza ApiException con el mensaje de error del servidor.
  4. Un 404 durante el sondeo significa "todavía no registrado" y el sondeo continúa. El plazo es de 5 minutos, tras lo cual lanza ApiException(504, 'Conversion timed out').

No hay código que escribir para esto. Dos excepciones deliberadas: los envíos de lotes de sitios web (convertWebsiteToPdf, convertWebsiteToScreenshot) no tienen una fila por job, así que un 5xx ahí aflora de inmediato, y los métodos V2 no usan sondeo de jobs en absoluto.

Las respuestas correctas que omiten job_id se rellenan con el id generado por el cliente, de modo que $result->jobId siempre se puede usar con getJobStatus().


Configuración#

use Enconvert\Client;

$client = new Client(getenv('ENCONVERT_API_KEY'), [
    'timeout' => 300,                            // segundos
    'base_url' => 'https://api.enconvert.com',   // sobrescríbelo para un gateway autoalojado
]);
Opción Tipo Por defecto Descripción
$apiKey (primer argumento) string obligatorio Clave de API privada (sk_live_...). Una cadena vacía lanza EnconvertException.
timeout int\|float 300 Timeout de solicitud en segundos, la unidad idiomática de Guzzle. Se aplica a cada solicitud HTTP que hace el cliente. El plazo de sondeo del job es independiente y está fijado en 300 segundos.
base_url string https://api.enconvert.com Host de la API. Las barras finales se eliminan.

Fíjate en que la clave de opción es base_url en snake_case mientras que todas las demás opciones de solicitud del SDK van en camelCase. Las solicitudes se autentican con un encabezado X-API-Key; las descargas con saveTo van directamente al almacenamiento y deliberadamente no envían clave, porque la URL ya viene firmada.

Nunca incrustes la clave de API en el código. Léela desde una variable de entorno o desde tu gestor de secretos, y mantenla fuera del control de versiones y de cualquier cosa que llegue a un navegador. Genera y rota claves en el panel de control, y consulta la guía de autenticación para conocer los tipos de clave.

Forma del resultado#

Toda conversión de un solo archivo devuelve un Enconvert\Model\ConversionResult con propiedades públicas de solo lectura:

final class ConversionResult
{
    public readonly string $presignedUrl;              // URL de descarga firmada
    public readonly string $objectKey;                 // clave del objeto en almacenamiento
    public readonly string $filename;                  // nombre de archivo del servidor
    public readonly int|float|null $fileSize;          // bytes
    public readonly int|float|null $conversionTimeSeconds;
    public readonly ?string $jobId;                    // el id usado para la recuperación de timeouts
}

Las URL prefirmadas caducan a los 15 minutos y se pueden usar más de una vez antes de eso. Para acceso permanente, descarga los bytes (pasa saveTo, o descarga la URL tú mismo) y guárdalos en tu propio bucket.

Los demás tipos de resultado siguen el mismo patrón: JobStatus, BatchSubmission, BatchStatus con su BatchItem[] y, bajo Enconvert\Model\V2, PerceiveResult, PerceiveDirectResult, PerceiveBatchResult, OutputArtifact, DiscoverResult, LookupResult, LookupItem, DistillResult, DistillItem, IngestJob, IngestJobList, IngestJobSummary, Watcher, WatcherList, WatcherSummary, WatcherSnapshot, WatcherSnapshotList, WebhookSecret, WebhookRetryResult y Tokens. Los campos de transmisión llegan en snake_case y se mapean a propiedades camelCase; las cargas útiles proporcionadas por el usuario, como schemas, datos extraídos, campos rastreados y entradas de diff, pasan intactas.


Código fuente e incidencias#


Preguntas frecuentes#

¿Cómo convierto archivos en PHP con Composer?#

Ejecuta composer require enconvert/enconvert-php, construye new Enconvert\Client($apiKey) y llama a un método como convertUrlToPdf, convertImage, convertDocument, convertToPdf o convertToMarkdown. Añade saveTo a cualquiera de ellos y el SDK transmite los bytes convertidos a esa ruta local por ti, creando por el camino los directorios padre.

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

Llama a $client->convertDocument('report.docx', ['saveTo' => 'report.pdf']). El formato de salida es pdf por defecto, así que no hace falta outputFormat. El formato de entrada se lee de la extensión del archivo, y el SDK comprueba el par doc-to-pdf contra su tabla de 43 conversiones implementadas antes de enviar nada, de modo que un par no admitido falla al instante con un mensaje que enumera lo que es válido para esa entrada.

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

Llama a $client->convertUrlToPdf('https://example.com', ['saveTo' => 'page.pdf']). Por defecto la página se renderiza como una única página continua con un viewport de 1920x1080, con carga de medios y desplazamiento activados. Establece singlePage en false y pasa pdfOptions cuando quieras paginación real con tamaño de página, márgenes, encabezados y pies.

¿Cómo extraigo una página web en PHP y obtengo Markdown limpio?#

Usa el espacio de nombres V2: $client->v2->perceive($url, ['outputs' => ['markdown', 'structured']]). Renderiza la página en un navegador real, así que los sitios con mucho JavaScript funcionan, y devuelve una URL de Markdown firmada más datos estructurados en línea. Comprueba renderQuality en el resultado antes de confiar en el contenido; una puntuación baja con deductions rellenos significa que el renderizado topó con una página antibots, un muro de inicio de sesión o un armazón vacío. Para una ruta más ligera de un solo paso sin las funciones de V2, convertUrlToMarkdown también sirve.

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

Llama a $client->convertImage('photo.heic', ['outputFormat' => 'webp', 'saveTo' => 'photo.webp']). Se admite cualquier par entre jpeg, png, svg, heic y webp, más la rasterización de pdf a jpeg. También puedes pasar bytes en crudo como ['data' => $bytes, 'filename' => 'photo.heic'], donde el nombre de archivo es lo que resuelve el formato de entrada y el tipo MIME.

¿Funciona el SDK de PHP de EnConvert con Laravel o Symfony?#

Sí. El paquete es una librería PSR-4 corriente con Guzzle 7 como única dependencia y sin acoplamiento a ningún framework, así que encaja sin cambios en Laravel, Symfony, WordPress o un script pelado. Registra Enconvert\Client en tu contenedor con la clave de tu configuración de entorno e inyéctalo donde lo necesites.

¿Qué pasa cuando una conversión tarda más que el timeout HTTP?#

El SDK se recupera por su cuenta. Envía un job_id generado por el cliente con cada solicitud de conversión y, si la solicitud devuelve 5xx, sondea en silencio GET /v1/convert/status/{job_id} cada 3 segundos durante hasta 5 minutos, devolviendo el resultado en cuanto el job registra success. Pasado el plazo lanza ApiException(504, 'Conversion timed out'). Los envíos de lotes de sitios web quedan fuera de esto, ya que no tienen una fila por job.

¿Cuánto tiempo es válida la URL de descarga prefirmada?#

Las URL de descarga prefirmadas caducan a los 15 minutos y se pueden usar más de una vez antes de eso. Las URL de artefacto de V2 llevan un expiresIn de 900 segundos por la misma razón, y getPerceiveOperation($operationId) las vuelve a firmar a partir de las claves de objeto almacenadas sin renderizar la página de nuevo. Para cualquier cosa permanente, descarga los bytes y guárdalos en tu propio almacenamiento.

¿Qué versión de PHP requiere el SDK?#

PHP 8.1 o posterior. El SDK usa propiedades de solo lectura, tipos unión al estilo enum, argumentos nombrados y expresiones match de principio a fin, así que 8.0 y anteriores no están admitidos. Su única dependencia en tiempo de ejecución es guzzlehttp/guzzle ^7.8.