---
seo_title: SDK Go de Conversión de Archivos: Cliente API sin Dependencias
meta_desc: SDK oficial de EnConvert para Go 1.21+. Solo librería estándar, sin dependencias de terceros, con conversión tipada y el espacio de nombres de inteligencia web V2.
keywords: sdk de conversión de archivos en go, convertir archivos con go, url a pdf en go, api de web scraping en go, docx a pdf en go, sdk de enconvert para go, librería golang de html a pdf, heic a webp en golang, api de extracción a markdown en go, api de captura de pantalla web en golang, pipeline de ingesta rag en go, extracción de datos estructurados en golang
---

# SDK de Conversión de Archivos para Go

`github.com/conversionapi/go-sdk` es el cliente oficial de EnConvert para Go 1.21 y posteriores. Se importa como paquete `enconvert` y no arrastra ninguna dependencia de terceros: todo funciona sobre `net/http`, `encoding/json` y `mime/multipart`. Doce métodos del cliente cubren conversión de archivos, renderizado de URLs y lotes de sitios completos (DOCX a PDF, HEIC a WebP, URL a PDF, URL a Markdown), y el espacio de nombres `client.V2` añade veintitrés métodos de inteligencia web para percibir, descubrir, buscar, destilar, ingerir y vigilar páginas. Cada llamada recibe primero un `context.Context`, así que la cancelación y los plazos siguen en tus manos.

<div class="alert alert-info">
<strong>Módulo:</strong> <code>github.com/conversionapi/go-sdk</code> · <strong>Paquete:</strong> <code>enconvert</code> · <strong>Fuente:</strong> <a href="https://github.com/conversionapi/go-sdk">conversionapi/go-sdk</a> · <strong>Go:</strong> 1.21+ · <strong>Dependencias:</strong> ninguna
</div>

---

## Instalación

```bash
go get github.com/conversionapi/go-sdk
```

La ruta del módulo termina en `go-sdk` pero el paquete se llama `enconvert`, así que impórtalo con un nombre explícito: `import enconvert "github.com/conversionapi/go-sdk"`.

---

## Inicio rápido

```go
package main

import (
    "context"
    "fmt"
    "log"
    "os"

    enconvert "github.com/conversionapi/go-sdk"
)

func main() {
    client, err := enconvert.New(os.Getenv("ENCONVERT_API_KEY"))
    if err != nil {
        log.Fatal(err)
    }

    ctx := context.Background()

    result, err := client.ConvertURLToPDF(ctx, "https://example.com", enconvert.URLToPDFOptions{SaveTo: "page.pdf"})
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println(result.Filename, result.PresignedURL)
}
```

`New` devuelve un error solo cuando la clave de API está vacía; consulta [Configuración](#configuracion) para las opciones funcionales. Leer una página como debería leerla un agente, con una puntuación de calidad adjunta, es una sola llamada al espacio de nombres V2:

```go
op, err := client.V2.Perceive(ctx, "https://example.com", enconvert.PerceiveOptions{
    Outputs: []enconvert.PerceiveOutputName{enconvert.PerceiveOutputMarkdown},
})
fmt.Println(op.Outputs["markdown"].URL, *op.RenderQuality) // p. ej. 0.93
```

Cada fragmento de abajo asume un `client` y un `ctx` construidos exactamente así.

---

## Qué expone el cliente

Doce métodos cuelgan de `*enconvert.Client` y se corresponden 1:1 con endpoints REST. Las opciones siempre se pasan como un valor de struct, nunca como un puntero, así que el valor cero (`enconvert.URLToPDFOptions{}`) significa "todos los valores predeterminados", y los números y booleanos opcionales son campos puntero en toda la superficie: usa los ayudantes `enconvert.Int`, `enconvert.Bool`, `enconvert.Float64` y `enconvert.String` en lugar de una variable local desechable.

| Método | Endpoint | Devuelve |
|--------|----------|---------|
| `ConvertURLToPDF(ctx, url, opts)` | `POST /v1/convert/url-to-pdf` | `ConversionResult` |
| `ConvertURLToScreenshot(ctx, url, opts)` | `POST /v1/convert/url-to-screenshot` | `ConversionResult` |
| `ConvertURLToMarkdown(ctx, url, opts)` | `POST /v1/convert/url-to-markdown` | `ConversionResult` |
| `ConvertImage(ctx, file, opts)` | `POST /v1/convert/{from}-to-{to}` | `ConversionResult` |
| `ConvertDocument(ctx, file, opts)` | `POST /v1/convert/{from}-to-{to}` | `ConversionResult` |
| `ConvertToMarkdown(ctx, file, opts)` | `POST /v1/convert/anything-to-markdown` | `ConversionResult` |
| `ConvertToPDF(ctx, file, opts)` | `POST /v1/convert/anything-to-pdf` | `ConversionResult` |
| `ConvertWebsiteToPDF(ctx, url, opts)` | `POST /v1/convert/website-to-pdf` | `BatchSubmission` |
| `ConvertWebsiteToScreenshot(ctx, url, opts)` | `POST /v1/convert/website-to-screenshot` | `BatchSubmission` |
| `GetJobStatus(ctx, jobID)` | `GET /v1/convert/status/{jobID}` | `JobStatus` |
| `GetBatchStatus(ctx, batchID)` | `GET /v1/convert/batch/{batchID}` | `BatchStatus` |
| `WaitForBatch(ctx, batchID, opts)` | `GET /v1/convert/batch/{batchID}` (sondeado) | `BatchStatus` |

`client.V2` guarda veintitrés métodos más repartidos en seis grupos de capacidades:

| Grupo | Métodos | Ruta base |
|-------|---------|-----------|
| Perceive | `Perceive`, `PerceiveDirect`, `GetPerceiveOperation`, `DownloadPerceiveArtifact`, `PerceiveBatch`, `GetPerceiveBatch` | `/v2/perceive` |
| Discover | `Discover` | `/v2/discover` |
| Lookup | `Lookup` | `/v2/lookup` |
| Distill | `Distill` | `/v2/distill` |
| Ingest | `Ingest`, `IngestFiles`, `GetIngestJob`, `ListIngestJobs`, `CancelIngestJob`, `RetryIngestWebhook`, `GetWebhookSecret`, `RotateWebhookSecret` | `/v2/ingest` |
| Watch | `CreateWatcher`, `ListWatchers`, `GetWatcher`, `GetWatcherSnapshots`, `UpdateWatcher`, `DeleteWatcher` | `/v2/watch` |

---

## Conversión de archivos

Las subidas aceptan cualquier `FileSource`: `enconvert.FilePath("report.docx")` para una ruta en disco (el nombre base decide el formato de entrada y el tipo MIME), `enconvert.FileBytes(buf)` para bytes en bruto sin nombre (se suben como `upload.bin`, `application/octet-stream`), o `enconvert.FileInput{Data: buf, Filename: "report.docx"}` para bytes en bruto con un nombre de archivo explícito y un `ContentType` opcional.

### ConvertURLToPDF

Renderiza a PDF cualquier URL accesible.

```go
result, err := client.ConvertURLToPDF(ctx, "https://example.com", enconvert.URLToPDFOptions{
    SinglePage:       enconvert.Bool(false),
    PDFOptions:       &enconvert.PDFOptions{PageSize: "A4", Orientation: "landscape"},
    URLRenderOptions: enconvert.URLRenderOptions{ViewportWidth: enconvert.Int(1440)},
    SaveTo:           "report.pdf",
})
```

| Opción | Tipo | Predeterminado | Descripción |
|--------|------|---------|-------------|
| `SaveTo` | `string` | -- | Ruta local a la que transmitir el PDF. Los directorios padre se crean por ti. |
| `SinglePage` | `*bool` | `true` | `true` renderiza una única página continua. `false` pagina usando `PDFOptions.PageSize`. |
| `PDFOptions` | `*PDFOptions` | -- | Tamaño de página, orientación, márgenes, escala, escala de grises, encabezado, pie. Consulta [Opciones de PDF](#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` | Hace scroll de arriba abajo para disparar la carga diferida. |
| `OutputFilename` | `string` | automático | Sustituye el nombre de archivo generado. |
| `Auth` | `*HTTPBasicAuth` | -- | Credenciales HTTP Basic para páginas tras un inicio de sesión. |
| `Cookies`, `Headers` | `[]BrowserCookie`, `map[string]string` | -- | Cookies inyectadas antes de renderizar (máximo 50) y encabezados de solicitud adicionales (máximo 20, los hop-by-hop se rechazan). |

Todo lo que va de `ViewportWidth` hacia abajo vive en el struct embebido `URLRenderOptions`, compartido por todos los métodos basados en URL.

<div class="alert alert-warning">
<strong>No combines <code>Auth</code> con un encabezado <code>Authorization</code>.</strong> La API rechaza el conflicto en lugar de adivinar cuál de los dos querías.
</div>

### ConvertURLToScreenshot y ConvertURLToMarkdown

Captura un PNG de cualquier URL, o extrae Markdown limpio con sabor GitHub y frontmatter YAML (título, descripción, url, enlaces, imágenes). El conversor de Markdown elimina navegación, pies de página, anuncios y scripts, y conserva el cuerpo principal del artículo.

```go
shot, err := client.ConvertURLToScreenshot(ctx, "https://example.com", enconvert.URLToScreenshotOptions{
    URLRenderOptions: enconvert.URLRenderOptions{ViewportWidth: enconvert.Int(1440)},
    SaveTo:           "shot.png",
})

article, err := client.ConvertURLToMarkdown(ctx, "https://example.com/article",
    enconvert.URLToMarkdownOptions{SaveTo: "article.md"})
```

Ambos toman las mismas `URLRenderOptions` que `ConvertURLToPDF`, más `SaveTo`. Ninguno acepta `SinglePage` ni `PDFOptions`.

### ConvertImage

Convierte entre `jpeg`, `png`, `svg`, `heic` y `webp` en cualquier dirección, o rasteriza un PDF a JPEG.

```go
// Desde una ruta
result, err := client.ConvertImage(ctx, enconvert.FilePath("photo.heic"),
    enconvert.ConvertImageOptions{OutputFormat: "webp", SaveTo: "photo.webp"})

// Desde bytes que ya están en memoria
buf, _ := os.ReadFile("photo.heic")
result, err = client.ConvertImage(ctx, enconvert.FileInput{Data: buf, Filename: "photo.heic"},
    enconvert.ConvertImageOptions{OutputFormat: "webp", SaveTo: "photo.webp"})
```

| Opción | Tipo | Obligatorio | Descripción |
|--------|------|----------|-------------|
| `OutputFormat` | `string` | Sí | `jpeg`, `png`, `svg`, `heic` o `webp`. `jpg` se acepta como alias de `jpeg`. |
| `SaveTo` | `string` | -- | Ruta local a la que transmitir el resultado. |
| `OutputFilename` | `string` | -- | Sustituye el nombre de archivo generado. |

El formato de entrada sale de la extensión del nombre de archivo (`.jpg`, `.jpeg`, `.png`, `.svg`, `.heic`, `.webp`, `.pdf`). Los pares no admitidos fallan localmente, antes de cualquier llamada de red, con un error que enumera lo que sí está disponible:

```go
enconvert.ValidOutputsFor("pdf")  // []string{"jpeg"}
enconvert.ValidOutputsFor("json") // []string{"csv", "toml", "xml", "yaml"}
```

### ConvertDocument

Convierte documentos y formatos de datos. `OutputFormat` vale `pdf` por defecto cuando se deja vacío.

```go
// docx a pdf
_, err := client.ConvertDocument(ctx, enconvert.FilePath("report.docx"),
    enconvert.ConvertDocumentOptions{SaveTo: "report.pdf"})

// json a yaml
_, err = client.ConvertDocument(ctx, enconvert.FilePath("data.json"),
    enconvert.ConvertDocumentOptions{OutputFormat: "yaml", SaveTo: "data.yaml"})
```

**Entradas admitidas:** `.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 | Predeterminado | Descripción |
|--------|------|---------|-------------|
| `OutputFormat` | `string` | `"pdf"` | Formato de destino. `yml`, `htm`, `md` y `jpg` se normalizan a sus nombres canónicos. |
| `SaveTo` | `string` | -- | Ruta local a la que transmitir el resultado. |
| `OutputFilename` | `string` | -- | Sustituye el nombre de archivo generado. |
| `PDFOptions` | `*PDFOptions` | -- | Configuración de página. Solo tiene sentido cuando la salida es PDF. |

Los 43 pares implementados, expuestos como el mapa `enconvert.ImplementedConversions`, son: `json` a `csv`, `toml`, `xml`, `yaml`; `xml` a `csv`, `json`; `yaml` a `json`; `csv` a `json`, `xml`; `toml` a `json`; `markdown` a `html`, `pdf`; `html` a `pdf`; `doc`, `excel`, `ppt`, `odt`, `ods`, `odp`, `ots`, `pages` y `numbers` a `pdf`; los 20 pares ordenados entre `jpeg`, `png`, `svg`, `heic`, `webp`; y `pdf` a `jpeg`.

EPUB no tiene un par de documentos propio. Envía los archivos `.epub` a través de `ConvertToPDF` o `ConvertToMarkdown` en su lugar.

### ConvertToMarkdown

Detecta automáticamente la entrada en el servidor y devuelve Markdown limpio. Este es el bloque de construcción de ingesta para RAG: PDF, DOCX, PPTX, XLSX, CSV, HTML, EPUB, TXT y MD, más formatos de ofimática heredados y ODF.

```go
_, err := client.ConvertToMarkdown(ctx, enconvert.FilePath("handbook.pdf"),
    enconvert.ConvertToMarkdownOptions{SaveTo: "handbook.md"})
```

`ConvertToMarkdownOptions` tiene dos campos, `SaveTo` y `OutputFilename`. Este endpoint no admite imágenes y no tiene opciones de PDF. La salida conserva la jerarquía de encabezados del documento, así que un chunker semántico puede dividir por encabezados en lugar de por recuentos arbitrarios de caracteres.

### ConvertToPDF

Detecta automáticamente la entrada en el servidor y devuelve un PDF: ofimática, ODF, Pages, Numbers, RTF, CSV, HTML, Markdown, texto plano, imágenes rasterizadas, SVG, EPUB o un PDF existente pasado directamente para normalizarlo.

```go
_, err := client.ConvertToPDF(ctx, enconvert.FilePath("slides.pptx"),
    enconvert.ConvertToPDFOptions{SaveTo: "slides.pdf"})

// Paso directo de PDF, convertido a escala de grises
_, err = client.ConvertToPDF(ctx, enconvert.FilePath("scan.pdf"), enconvert.ConvertToPDFOptions{
    PDFOptions: &enconvert.PDFOptions{Grayscale: enconvert.Bool(true)},
    SaveTo:     "scan-gray.pdf",
})
```

<div class="alert alert-warning">
<strong>Aquí solo se respeta <code>PDFOptions.Grayscale</code>.</strong> Cualquier otro campo de geometría se ignora en <code>anything-to-pdf</code>. Usa <code>ConvertDocument</code> o <code>ConvertURLToPDF</code> cuando necesites tamaño de página, orientación, márgenes, escala, encabezados o pies.
</div>

`ConvertToPDFOptions` tiene tres campos: `SaveTo`, `OutputFilename` y `PDFOptions`.

### ConvertWebsiteToPDF y ConvertWebsiteToScreenshot

Descubre todas las páginas de un sitio, convierte cada una en segundo plano y recoge un único ZIP. Ambos son solo asíncronos: devuelven un `BatchSubmission`, y sondeas con `GetBatchStatus` o bloqueas con `WaitForBatch`. Hace falta una clave de API privada con acceso de rastreo.

```go
batch, err := client.ConvertWebsiteToPDF(ctx, "https://example.com", enconvert.WebsiteToPDFOptions{
    WebsiteConversionOptions: enconvert.WebsiteConversionOptions{CrawlMode: enconvert.CrawlModeSitemap},
})

status, err := client.WaitForBatch(ctx, batch.BatchID, enconvert.WaitForBatchOptions{SaveTo: "site.zip"})
fmt.Println(status.Completed, "of", status.Total, "pages converted")
```

| Opción | Tipo | Predeterminado | Descripción |
|--------|------|---------|-------------|
| `CrawlMode` | `CrawlMode` | `auto` | `CrawlModeAuto`, `CrawlModeSitemap` (solo sitemap.xml) o `CrawlModeFull` (sitemap más rastreo BFS). |
| `IncludePatterns` | `[]string` | -- | Rastrea solo las URLs que casan con estos patrones, en modo de rastreo completo. |
| `ExcludePatterns` | `[]string` | -- | Omite las URLs que casan con estos patrones, en modo de rastreo completo. |
| `NotificationEmail` | `string` | propietario del proyecto | Dirección avisada cuando termina el lote. |
| `CallbackURL` | `string` | -- | Webhook al que se hace POST cuando termina el lote. |
| `SinglePage` | `*bool` | valor del servidor | Solo PDF. Página continua frente a paginada. |
| `PDFOptions` | `*PDFOptions` | -- | Solo PDF. Se aplica a cada página. |

Todo lo de `URLRenderOptions` se acepta aquí también, y se envía por página solo cuando lo fijas. `WaitForBatchOptions` toma `Interval` (predeterminado 5 segundos), `Timeout` (predeterminado 30 minutos) y `SaveTo`. Reventar el tiempo de espera devuelve un `*APIError` con estado `504`; un lote terminado sin ZIP devuelve uno con estado `500`. `ConvertWebsiteToScreenshot` es idéntico menos `SinglePage` y `PDFOptions`, y produce un ZIP de PNGs.

---

## Inteligencia web (V2)

Cada lectura de V2 lleva una puntuación `RenderQuality` de 0.0 a 1.0, expuesta como un `*float64` en `PerceiveResult`, `DistillItem`, `WatcherSnapshot` y el resultado de descarga directa. Una puntuación baja significa que la página no se renderizó limpiamente: una página de desafío, un muro de cookies, el shell vacío de una SPA, un error HTTP. El contenido vuelve igualmente, marcado, junto a un mapa `Deductions` que nombra cada penalización que se activó y un slice `Warnings`, de modo que una lectura defectuosa nunca entra sin ruido en el contexto de tu agente. Todos los métodos de V2 requieren una clave de API privada; las claves públicas se rechazan. Usa la puntuación como barrera antes de confiar en nada:

```go
if op.RenderQuality != nil && *op.RenderQuality < 0.6 {
    log.Printf("low quality read of %s: %v", op.URL, op.Deductions)
}
```

### Perceive

Renderiza una URL y produce los artefactos que pidas. Síncrono, con URLs de artefacto firmadas durante 15 minutos.

```go
op, err := client.V2.Perceive(ctx, "https://example.com", enconvert.PerceiveOptions{
    Outputs:  []enconvert.PerceiveOutputName{enconvert.PerceiveOutputMarkdown, enconvert.PerceiveOutputStructured},
    Extract:  []enconvert.PerceiveExtractName{enconvert.PerceiveExtractTables, enconvert.PerceiveExtractMetadata},
    Viewport: &enconvert.PerceiveViewport{Width: enconvert.Int(1440)},
})
fmt.Println(op.OperationID, op.Outputs["markdown"].URL, op.Structured, op.ExtractionTier)

// Vuelve a firmar las URLs de los artefactos más tarde
again, err := client.V2.GetPerceiveOperation(ctx, op.OperationID)
```

| Opción | Tipo | Predeterminado | Descripción |
|--------|------|---------|-------------|
| `Outputs` | `[]PerceiveOutputName` | `markdown`, `structured` | `markdown`, `html_cleaned`, `html_raw`, `screenshot`, `screenshot_full_page`, `pdf`, `links`, `images`, `structured`. |
| `Extract` | `[]PerceiveExtractName` | -- | `tables`, `prices`, `contacts`, `metadata`, `main_content`, `headings`, `structured_data`, `technologies`, `all`. |
| `Schema` | `map[string]any` | -- | Esquema JSON para la extracción estructurada a través del nivel de LLM. |
| `WaitFor`, `WaitTimeoutMs` | `string`, `*int` | `30000` ms | Un selector CSS, opcionalmente con el prefijo `css:`, o `js:<expr>`, y su presupuesto (de 0 a 60000). |
| `JSCode` | `string` | -- | JavaScript ejecutado tras la navegación, máximo 20000 caracteres. |
| `Viewport` | `*PerceiveViewport` | 1920 x 1080 | `Width` de 320 a 3840, `Height` de 240 a 2160. |
| `Headers`, `Cookies`, `Auth` | `map[string]string`, `[]BrowserCookie`, `*HTTPBasicAuth` | -- | Encabezados de solicitud, cookies inyectadas, credenciales HTTP Basic. |
| `CacheMode` | `PerceiveCacheMode` | `enabled` | `enabled` (caché de 1 hora), `bypass`, `refresh`. |
| `PDFOptions` | `*PDFOptions` | -- | Solo tiene sentido cuando `Outputs` incluye `pdf`. |
| `BlockResources` | `[]PerceiveResourceType` | -- | `image`, `media`, `font`, `stylesheet`, `script`, `xhr`, `fetch`, `websocket`, `manifest`, `other`. |
| `RespectRobots`, `Mobile` | `*bool` | valor del servidor | Respeta `robots.txt`; emula un dispositivo móvil. |
| `OnlyMainContent` | `*bool` | `true` | Elimina navegación, encabezado, pie y avisos de cookies del artefacto markdown y del extracto `main_content`. Ponlo en `false` para la página completa. |
| `DirectDownload` | `*bool` | `false` | Transmite bytes en bruto en lugar de un sobre JSON. Es preferible `PerceiveDirect`. |

<div class="alert alert-warning">
<strong>Tres opciones están declaradas pero aún no operativas.</strong> <code>ProxyURL</code>, <code>Geolocation</code> y <code>ActionChain</code> las acepta el struct de Go y se serializan, pero el servidor responde actualmente <code>422</code> para las tres. Déjalas sin fijar.
</div>

Transmitir un único artefacto directamente al disco se salta el sobre JSON. `PerceiveDirect` valida localmente que pediste exactamente una salida que produce artefacto:

```go
direct, err := client.V2.PerceiveDirect(ctx, "https://example.com", enconvert.PerceiveOptions{
    Outputs: []enconvert.PerceiveOutputName{enconvert.PerceiveOutputPDF},
})
os.WriteFile(direct.Filename, direct.Content, 0o644)

// Vuelve a descargar más tarde un artefacto almacenado. Pasa "" cuando la operación produjo solo uno.
saved, err := client.V2.DownloadPerceiveArtifact(ctx, direct.OperationID, enconvert.PerceiveOutputPDF)
```

`PerceiveDirectResult` lleva `Content`, `ContentType`, `Filename`, `OperationID`, `ObjectKey`, `CacheHit`, `RenderQuality`, `SourceStatusCode`, `ContentHash` y `WarningsCount`, todos leídos de los encabezados de la respuesta. Un `*APIError` con `410` desde `DownloadPerceiveArtifact` significa que el artefacto ha superado su ventana de retención.

Los lotes aceptan hasta 1000 URLs con un único bloque de opciones compartido. Los lotes pequeños terminan inline; los mayores vuelven como `queued`, así que sondea `GetPerceiveBatch` hasta que `Status` sea `PerceiveBatchStatusCompleted` y lee `Zip` o `Items`:

```go
batch, err := client.V2.PerceiveBatch(ctx, []string{"https://a.example", "https://b.example"},
    enconvert.PerceiveBatchOptions{OutputMode: enconvert.PerceiveBatchOutputZip})
done, err := client.V2.GetPerceiveBatch(ctx, batch.JobID)
```

`OutputMode` es `PerceiveBatchOutputManifest` (predeterminado) o `PerceiveBatchOutputZip`. `DirectDownload` se rechaza con `422` en los lotes.

### Discover

Enumera las URLs de un sitio desde su sitemap, un rastreo HTTP o ambos. No se arranca ningún navegador, así que es rápido y barato.

```go
found, err := client.V2.Discover(ctx, "https://example.com", enconvert.DiscoverOptions{
    Mode: enconvert.DiscoverModeHybrid, MaxURLs: enconvert.Int(200), ExcludePatterns: []string{"/tag/"},
})
fmt.Println(found.Total, found.Truncated, found.Sources)
```

| Opción | Tipo | Predeterminado | Descripción |
|--------|------|---------|-------------|
| `Mode` | `DiscoverMode` | `hybrid` | `DiscoverModeSitemap`, `DiscoverModeCrawl` o `DiscoverModeHybrid` (sitemap más rastreo HTTP). |
| `MaxURLs`, `MaxDepth` | `*int` | `100`, `2` | De 1 a 1000 URLs, profundidad de rastreo de 1 a 5. |
| `IncludePatterns`, `ExcludePatterns` | `[]string` | -- | Lista de permitidos por regex y luego de denegados, con semántica de `re.search`, máximo 50 cada una. |
| `SameDomainOnly` | `*bool` | `true` | Se queda en el dominio de la URL semilla. |
| `RespectRobots` | `*bool` | valor del servidor | Respeta `robots.txt`. |

`DiscoverResult` te da `URL`, `Mode`, `Total`, `URLs`, `PagesCrawled`, `Truncated`, `RobotsRespected`, `Sources` (recuentos brutos por fuente antes de deduplicar) y `Warnings`.

### Lookup

Búsqueda web categorizada, renderizando opcionalmente los primeros resultados en el mismo viaje de ida y vuelta.

```go
search, err := client.V2.Lookup(ctx, "best static site generators", enconvert.LookupOptions{
    Category: enconvert.LookupCategoryWeb, NumResults: enconvert.Int(10), PerceiveTop: enconvert.Int(3),
})
for _, hit := range search.Results {
    fmt.Println(hit.Position, hit.Title, hit.URL)
    if hit.Perceive != nil {
        fmt.Println(hit.Perceive.Outputs["markdown"].URL)
    }
}
```

| Opción | Tipo | Predeterminado | Descripción |
|--------|------|---------|-------------|
| `Category` | `LookupCategory` | `web` | `web`, `news`, `images`, `scholar`, `patents`, `maps`. |
| `Country`, `Locale` | `string` | -- | Código de país `gl` de Google (`us`, `in`) e idioma de interfaz `hl` (`en`). |
| `TimeFilter` | `LookupTimeFilter` | -- | `hour`, `day`, `week`, `month`, `year`. |
| `NumResults`, `Page` | `*int` | `10`, `1` | De 1 a 100 resultados, página de 1 a 10. |
| `Location` | `string` | -- | Ubicación en texto libre, por ejemplo `Austin, Texas`. |
| `Autocorrect` | `*bool` | `true` | Deja que el proveedor corrija erratas de la consulta. |
| `PerceiveTop` | `*int` | `0` | De 0 a 10. Ejecuta un renderizado completo en el navegador de las N primeras URLs de resultados y adjunta cada `PerceiveResult` inline. |

`LookupResult` lleva además `AnswerBox`, `KnowledgeGraph`, `PerceiveOperationIDs` y `Warnings`.

### Distill

Extracción estructurada guiada por esquema. Proporciona exactamente uno de `URLs` (máximo 50) o `DiscoverFrom`; `Schema` siempre es obligatorio. Ambas reglas se comprueban localmente antes de que salga ninguna solicitud.

```go
extraction, err := client.V2.Distill(ctx, enconvert.DistillOptions{
    URLs:   []string{"https://example.com/pricing"},
    Schema: map[string]any{"plans": "list of plan names with monthly prices"},
    CSSSchema: &enconvert.CSSSchema{
        BaseSelector: ".plan-card",
        Fields: []enconvert.CSSField{
            {Name: "name", Type: enconvert.CSSFieldText, Selector: "h3"},
            {Name: "price", Type: enconvert.CSSFieldText, Selector: ".price"},
        },
    },
})
first := extraction.Results[0]
fmt.Println(first.Data, first.ExtractionTier, first.FieldsFromCSS, first.FieldsFromLLM)
```

El `CSSSchema` opcional se ejecuta primero y responde todo lo que puede con selectores simples; solo los campos que no cubre escalan al nivel de LLM, y `ExtractionTier` informa de qué niveles respondieron realmente (`css`, `llm`, `mixed` o `none`). `CSSField.Type` es uno de `text`, `attribute`, `html`, `regex`, `nested`, `list` o `nested_list`, anidado hasta 5 niveles de profundidad.

Cambia `URLs` por `DiscoverFrom` para descubrir y luego destilar en una sola llamada. `DistillDiscoverFrom` toma `URL`, `Mode` (predeterminado `hybrid`) y `MaxPages` (de 1 a 50, predeterminado 10, que limita tanto el descubrimiento como la destilación):

```go
_, err = client.V2.Distill(ctx, enconvert.DistillOptions{
    DiscoverFrom: &enconvert.DistillDiscoverFrom{URL: "https://example.com", MaxPages: enconvert.Int(10)},
    Schema:       map[string]any{"title": "page title", "summary": "one-line summary"},
})
```

### Ingest

Convierte un sitio, una lista de URLs o una pila de documentos subidos en JSONL fragmentado y listo para RAG. Siempre asíncrono.

```go
job, err := client.V2.Ingest(ctx, enconvert.IngestOptions{
    Mode:       enconvert.IngestModeSitemap,
    URL:        "https://docs.example.com",
    MaxPages:   enconvert.Int(100),
    Chunk:      &enconvert.IngestChunkOptions{MaxWords: enconvert.Int(512), SentenceOverlap: enconvert.Int(1)},
    WebhookURL: "https://my.app/hooks/enconvert",
})

status, err := client.V2.GetIngestJob(ctx, job.JobID)
if status.Status == enconvert.IngestStatusCompleted {
    fmt.Println(status.TotalChunks, status.OutputURL)
}
```

| Opción | Tipo | Predeterminado | Descripción |
|--------|------|---------|-------------|
| `Mode` | `IngestMode` | `urls` | `IngestModeURLs`, `IngestModeSitemap`, `IngestModeCrawl`, `IngestModeFiles`. |
| `URL` | `string` | -- | URL semilla. Obligatoria para `sitemap` y `crawl`, prohibida para `urls`. |
| `URLs` | `[]string` | -- | URLs explícitas, máximo 1000. Obligatorias para `urls`, prohibidas en los demás casos. |
| `MaxPages`, `MaxDepth` | `*int` | `50`, `2` | Tope de descubrimiento de 1 a 1000 para `sitemap` y `crawl`, profundidad de 1 a 5. |
| `SameDomainOnly` | `*bool` | `true` | Se queda en el dominio de la URL semilla. |
| `IncludePatterns`, `ExcludePatterns` | `[]string` | -- | Lista de permitidos por regex y luego de denegados. |
| `RespectRobots` | `*bool` | valor del servidor | Respeta `robots.txt`. |
| `WaitFor`, `WaitTimeoutMs` | `string`, `*int` | `30000` ms | Selector o expresión `js:` que se espera por página, y su presupuesto (de 0 a 60000). |
| `Chunk` | `*IngestChunkOptions` | -- | `MaxWords` de 32 a 4000, predeterminado 512. `SentenceOverlap` de 0 a 10, predeterminado 1. |
| `WebhookURL` | `string` | -- | Webhook de finalización, firmado con HMAC. |

Las reglas de modo y URL anteriores se comprueban en el cliente: `Ingest` devuelve un error simple de Go, no un viaje a la API, si envías `URLs` con `Mode: IngestModeSitemap`.

Los archivos subidos recorren el mismo pipeline y el mismo ciclo de vida de trabajo:

```go
fileJob, err := client.V2.IngestFiles(ctx,
    []enconvert.FileSource{enconvert.FilePath("handbook.pdf"), enconvert.FilePath("notes.docx")},
    enconvert.IngestFilesOptions{Chunk: &enconvert.IngestChunkOptions{MaxWords: enconvert.Int(512)}})
```

Se aceptan PDF, DOCX, PPTX, XLSX, CSV, HTML, EPUB, TXT y MD, y archivos de ofimática heredados u ODF; hace falta al menos un archivo. Gestión de trabajos y fontanería de webhooks:

```go
list, err := client.V2.ListIngestJobs(ctx, enconvert.V2ListOptions{Limit: enconvert.Int(20)})
canceled, err := client.V2.CancelIngestJob(ctx, job.JobID) // idempotente
secret, err := client.V2.GetWebhookSecret(ctx)             // Secret, SignatureHeader, SignatureScheme, ...
rotated, err := client.V2.RotateWebhookSecret(ctx)         // las firmas antiguas dejan de verificarse al instante
retry, err := client.V2.RetryIngestWebhook(ctx, job.JobID)
```

`RetryIngestWebhook` responde `409` cuando el trabajo no está completado y `400` cuando no tiene webhook configurado. `V2ListOptions` toma `Skip` y `Limit` (de 1 a 100, predeterminado 20).

### Watch

Vuelve a renderizar una página con una cadencia fija y recibe un aviso cuando cambia.

```go
watcher, err := client.V2.CreateWatcher(ctx, "https://example.com/pricing", enconvert.WatchCreateOptions{
    FrequencyMinutes: enconvert.Int(60),
    DiffMode:         enconvert.WatchDiffAuto,
    WebhookURL:       "https://my.app/hooks/changes",
})

history, err := client.V2.GetWatcherSnapshots(ctx, watcher.WatcherID, enconvert.SnapshotListOptions{Limit: enconvert.Int(10)})
for _, snap := range history.Snapshots {
    fmt.Println(snap.CheckedAt, snap.HasChanges, snap.ChangeCount, snap.Changes)
}
```

| Opción | Tipo | Predeterminado | Descripción |
|--------|------|---------|-------------|
| `FrequencyMinutes` | `*int` | `60` | De 60 a 43200. El mínimo de una hora es estricto. |
| `DiffMode` | `WatchDiffMode` | `auto` | `WatchDiffAuto`, `WatchDiffText`, `WatchDiffStructured`, `WatchDiffTables`, `WatchDiffMetadata`. |
| `TrackFields` | `map[string]any` | -- | Subconjunto de campos o selectores entregado al motor de diff. |
| `WebhookURL`, `NotifyEmail` | `string`, `*bool` | --, `true` | Webhook de cambios firmado con HMAC, y si se envía correo al propietario del proyecto. |

```go
// Un puntero a la cadena vacía limpia el webhook; nil lo deja como está.
_, err = client.V2.UpdateWatcher(ctx, watcher.WatcherID, enconvert.WatcherUpdate{
    Status: enconvert.WatcherStatusPaused, WebhookURL: enconvert.String(""),
})
_, err = client.V2.DeleteWatcher(ctx, watcher.WatcherID) // borrado suave, idempotente
```

`UpdateWatcher` requiere al menos un campo y devuelve un error simple de Go si le entregas un struct vacío. `Status` solo acepta `WatcherStatusActive` o `WatcherStatusPaused`; el borrado se hace con `DeleteWatcher`, que devuelve el vigilante marcado como eliminado con estado `deleted`. `ListWatchers` y `GetWatcher` completan el grupo.

<div class="alert alert-warning">
<strong>Los diffs de instantáneas contienen contenido de página no confiable.</strong> <code>WatcherSnapshot.Changes</code> es un slice de mapas en bruto tomados de la página vigilada. Escapa los valores antes de renderizarlos en cualquier parte.
</div>

---

## Opciones de PDF

`PDFOptions` lo comparten `ConvertURLToPDF`, `ConvertDocument`, `ConvertWebsiteToPDF`, `PerceiveOptions` y (solo para `Grayscale`) `ConvertToPDF`. Solo se envían los campos que fijas.

```go
opts := &enconvert.PDFOptions{
    PageSize:    "A4",
    Orientation: "landscape",
    Margins:     &enconvert.PDFMargins{Top: enconvert.Float64(10), Bottom: enconvert.Float64(10)},
    Scale:       enconvert.Float64(0.9),
    Footer:      &enconvert.PDFHeaderFooter{Content: "Confidential", Height: enconvert.Float64(20)},
}
```

| Campo | Tipo | Descripción |
|-------|------|-------------|
| `PageSize` | `string` | `"A4"`, `"A3"`, `"Letter"`, `"Legal"` y compañía. |
| `PageWidth`, `PageHeight` | `*float64` | Fíjalos juntos para anular `PageSize`. |
| `Orientation` | `string` | `"portrait"` o `"landscape"`. Por defecto es vertical. |
| `Margins` | `*PDFMargins` | `Top`, `Bottom`, `Left`, `Right`, cada uno un `*float64` en puntos. Los cuatro son opcionales. |
| `Scale` | `*float64` | Escala de renderizado, por ejemplo `0.9` para el 90%. |
| `Grayscale` | `*bool` | Posprocesa el PDF a escala de grises. |
| `Header`, `Footer` | `*PDFHeaderFooter` | Cada uno tiene `Content` (máximo 2000 caracteres) y `Height`. |

---

## Manejo de errores

Go no tiene clases de excepción, así que todo fallo de la API es un único tipo concreto, `*enconvert.APIError`, más tres predicados. `Error()` se representa como `"[<status>] <message>"`.

```go
result, err := client.ConvertURLToPDF(ctx, "https://example.com", enconvert.URLToPDFOptions{})
switch {
case err == nil:
    fmt.Println(result.PresignedURL)
case enconvert.IsAuthenticationError(err):
    log.Println("invalid or missing API key")
case enconvert.IsRateLimitError(err):
    log.Println("too many requests, back off and retry")
case enconvert.IsQuotaError(err):
    log.Println("request rejected with 402")
default:
    var apiErr *enconvert.APIError
    if errors.As(err, &apiErr) {
        log.Printf("api error [%d]: %s", apiErr.StatusCode, apiErr.Message)
    } else {
        log.Println(err) // fallo de red, cancelación del contexto, validación local
    }
}
```

| Comprobación | Se lanza en | Código de estado |
|-------|-----------|-------------|
| `IsAuthenticationError(err)` | Clave inválida, ausente o revocada | `401`, `403` (ambos registrados como `401`) |
| `IsQuotaError(err)` | Cualquier respuesta que la API conteste con `402` | `402` |
| `IsRateLimitError(err)` | Límite de tasa superado | `429` |
| `errors.As(err, &apiErr)` | Cualquier otro 4xx o 5xx | el código real |

Los errores que nunca llegan a la red, como un par de conversión no admitido, una llamada a `Distill` con `URLs` y `DiscoverFrom` a la vez, o una llamada a `PerceiveDirect` que pide dos artefactos, vuelven como valores `error` simples de `errors.New` o `fmt.Errorf`, no como `*APIError`. Los códigos de respuesta están catalogados en la [referencia de códigos de error](/es/docs/error-codes).

---

## Recuperación de tiempos de espera

Los renderizados largos de URLs y las conversiones de documentos grandes pueden sobrevivir al techo de 60 a 120 segundos de un proxy inverso incluso cuando el trabajo termina bien en el servidor. El SDK se abre paso a base de sondeo, sin código por tu parte:

1. Antes de cada conversión de un solo archivo y de una sola URL, el cliente genera un UUIDv4 y lo envía como `job_id`.
2. Si esa solicitud vuelve con `>= 500`, el cliente pasa en silencio a `GET /v1/convert/status/{job_id}`, sondeando cada 3 segundos.
3. Con `success` devuelve el resultado. Con `failed` devuelve `*APIError` con estado `500` y el mensaje del servidor.
4. El plazo de sondeo es de 5 minutos, tras los cuales recibes un `*APIError` con estado `504` y el mensaje `Conversion timed out`.

`ConversionResult.JobID` siempre queda poblado, incluso cuando la vía síncrona tuvo éxito y la respuesta lo omitió, así que puedes entregárselo tú mismo a `GetJobStatus`:

```go
status, err := client.GetJobStatus(ctx, result.JobID)
switch status.Status {
case enconvert.JobStatusSuccess:
    fmt.Println(status.PresignedURL)
case enconvert.JobStatusFailed:
    log.Println(status.Error)
}
```

<div class="alert alert-info">
<strong>Los lotes de sitios web se quedan fuera a propósito.</strong> <code>ConvertWebsiteToPDF</code> y <code>ConvertWebsiteToScreenshot</code> no tienen fila por trabajo que sondear, así que un 5xx ahí sale a la superficie de inmediato en lugar de reintentarse. Los métodos de V2 tampoco usan la vía alternativa de trabajos. El sondeo se ejecuta dentro del <code>context.Context</code> que pasas, así que cancelar el contexto aborta la espera al instante.
</div>

---

## Configuración

```go
client, err := enconvert.New(os.Getenv("ENCONVERT_API_KEY"),
    enconvert.WithTimeout(300*time.Second),
    enconvert.WithBaseURL("https://api.enconvert.com"),
)
```

| Entrada del constructor | Tipo | Predeterminado | Descripción |
|-------------------|------|---------|-------------|
| `apiKey` (primer argumento) | `string` | obligatorio | Clave de API privada. `New` devuelve un error cuando está vacía. |
| `WithTimeout(d)` | `time.Duration` | `300 * time.Second` | Fija `Timeout` en el `*http.Client` interno, cubriendo también las descargas. |
| `WithBaseURL(u)` | `string` | `https://api.enconvert.com` | Sustitución para un gateway autoalojado. Las barras finales se eliminan. |

Los plazos por llamada se superponen al tiempo de espera del cliente a través del contexto: envuélvelo con `context.WithTimeout(context.Background(), 30*time.Second)` y pásalo como primer argumento.

<div class="alert alert-warning">
<strong>Nunca incrustes la clave de API en el código.</strong> Léela de una variable de entorno o de tu gestor de secretos, y mantenla en el servidor. Cualquiera que tenga tu clave privada puede ejecutar conversiones contra tu proyecto.
</div>

El cliente se puede compartir entre goroutines con seguridad: guarda un `*http.Client` y ningún estado mutable por solicitud. Construye uno al arrancar y reutilízalo. Consulta [autenticación](/es/docs/authentication) para los tipos de clave y la rotación.

---

## Forma del resultado

Las conversiones de un solo archivo y de una sola URL devuelven un `ConversionResult`:

```go
type ConversionResult struct {
    PresignedURL          string   // URL de descarga firmada de la salida
    ObjectKey             string   // clave del objeto en el almacenamiento
    Filename              string   // nombre de archivo del lado del servidor
    FileSize              *int64   // bytes, nil cuando la API lo omite
    ConversionTimeSeconds *float64 // nil cuando la API lo omite
    JobID                 string   // siempre lo fija el cliente
}
```

Las URLs prefirmadas son de corta duración. Pasa `SaveTo` para que el SDK transmita los bytes al disco por ti, o busca la URL tú mismo y guarda el archivo en tu propio bucket si necesitas acceso a largo plazo. La descarga deliberadamente no lleva tu clave de API, ya que una URL prefirmada se autentica sola y reenviar la clave a un host de almacenamiento la filtraría.

Los artefactos de V2 llegan como valores `V2OutputArtifact` indexados por nombre de salida, cada uno con `URL` (prefirmada durante 15 minutos y vuelta a firmar en cada GET de estado), `ObjectKey`, `SizeBytes`, `ContentType` y `ExpiresIn` (900 segundos por defecto). `PerceiveResult` los envuelve con los metadatos de honestidad: `RenderQuality`, `StatusCode`, `Deductions`, `CacheHit`, `Warnings`, `ContentHash`, `URLFinal`, `Structured`, `ExtractionTier`, `Tokens`, `CostCents`, `DurationMs` y `OptionsEcho`, que devuelve como eco las opciones que el servidor respetó realmente, con los secretos reducidos a booleanos.

---

## Código fuente e incidencias

- **Módulo y fuente:** [github.com/conversionapi/go-sdk](https://github.com/conversionapi/go-sdk)
- **Versión:** expuesta en tiempo de ejecución como la constante `enconvert.Version`
- **Licencia:** MIT, sin dependencias de terceros

Lectura relacionada: [todos los SDKs](/es/docs/sdks), [resumen de V2](/es/docs/v2-overview), [perceive](/es/docs/v2-perceive), [discover](/es/docs/v2-discover), [lookup](/es/docs/v2-lookup), [distill](/es/docs/v2-distill), [ingest](/es/docs/v2-ingest), [watch](/es/docs/v2-watch), [resumen de endpoints](/es/docs/endpoints-overview), [parámetros y opciones](/es/docs/parameters-options) y tu [panel de control](/es/dashboard) para las claves.

---

## Preguntas frecuentes

### ¿Cómo convierto archivos en Go?

Ejecuta `go get github.com/conversionapi/go-sdk`, construye un cliente con `enconvert.New(os.Getenv("ENCONVERT_API_KEY"))` y luego llama a un método tipado como `ConvertDocument`, `ConvertImage` o `ConvertURLToPDF`. Pasa `SaveTo` en el struct de opciones y el SDK transmite el archivo terminado directamente a esa ruta, creando los directorios padre que hagan falta.

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

Llama a `client.ConvertURLToPDF(ctx, url, enconvert.URLToPDFOptions{SaveTo: "page.pdf"})`. Fija `SinglePage: enconvert.Bool(false)` para paginar en lugar de producir una única página continua, y pasa `PDFOptions` para el tamaño de página, la orientación, los márgenes, la escala, la escala de grises, los encabezados y los pies.

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

`client.ConvertDocument(ctx, enconvert.FilePath("report.docx"), enconvert.ConvertDocumentOptions{SaveTo: "report.pdf"})`. El formato de salida vale `pdf` por defecto, así que puedes dejar `OutputFormat` vacío. El mismo método gestiona entradas XLSX, PPTX, ODT, ODS, ODP, OTS, Pages, Numbers, HTML, Markdown, CSV, JSON, XML, YAML y TOML.

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

`client.ConvertImage(ctx, enconvert.FilePath("photo.heic"), enconvert.ConvertImageOptions{OutputFormat: "webp", SaveTo: "photo.webp"})`. El formato de entrada se lee de la extensión del nombre de archivo, y los 20 pares ordenados entre `jpeg`, `png`, `svg`, `heic` y `webp` funcionan igual. Los pares no admitidos fallan localmente antes de enviar ninguna solicitud.

### ¿El SDK de Go arrastra alguna dependencia de terceros?

No. `go.mod` declara el módulo y un mínimo de Go 1.21, y nada más. El cliente está construido sobre `net/http`, `encoding/json`, `mime/multipart` y `crypto/rand` de la librería estándar, así que no añade ninguna cadena de suministro transitiva a tu build.

### ¿Cómo extraigo una página web a Markdown limpio en Go?

Dos opciones. `client.ConvertURLToMarkdown` devuelve Markdown con sabor GitHub y frontmatter YAML, y es la vía más simple. `client.V2.Perceive` con `Outputs: []enconvert.PerceiveOutputName{enconvert.PerceiveOutputMarkdown}` te da el mismo Markdown más una puntuación `RenderQuality`, `Deductions`, `Warnings` y la opción de añadir capturas de pantalla, enlaces o extracción estructurada en el mismo renderizado.

### ¿Qué significa la calidad de renderizado y por qué debería revisarla?

`RenderQuality` es un `*float64` de 0.0 a 1.0 adjunto a cada lectura de V2. Baja cuando la página no se renderizó con honestidad: un desafío antibot, un muro de inicio de sesión, un aviso de cookies sobre un shell vacío o un estado de error HTTP. El contenido se sigue devolviendo en lugar de tragárselo, así que revisa la puntuación (y el mapa `Deductions` que nombra cada penalización) antes de darle el texto a un modelo.

### ¿Cómo fijo un tiempo de espera por solicitud o cancelo una conversión en Go?

Todos los métodos reciben primero un `context.Context`. Envuélvelo con `context.WithTimeout` o `context.WithCancel` para controlarlo llamada a llamada; `WithTimeout` en el constructor fija el tiempo de espera de base del cliente HTTP para todas las llamadas, incluida la descarga de un archivo con `SaveTo`.

### ¿Qué pasa cuando una conversión larga choca con el tiempo de espera del proxy?

El SDK envía un `job_id` generado por el cliente con cada conversión de un solo archivo y de una sola URL. Si la solicitud devuelve `>= 500`, sondea `GET /v1/convert/status/{job_id}` cada 3 segundos durante hasta 5 minutos, devolviendo el resultado con `success` y un `*APIError` con `failed`. Superar el plazo produce un `*APIError` con estado `504`. Los envíos de lotes de sitios web se saltan deliberadamente esta vía alternativa.

### ¿Puedo enviar el SDK de Go dentro de un cliente de escritorio o móvil?

No. Se autentica con una clave de API privada, y los endpoints de V2 rechazan las claves públicas sin más. Mantén el cliente en un servidor que controles y deja que tu aplicación hable con él. Consulta [autenticación](/es/docs/authentication) para el modelo de claves.

### ¿El cliente de Go es seguro de usar desde varias goroutines?

Sí. `*enconvert.Client` envuelve un único `*http.Client` y no mantiene estado mutable por solicitud, así que construye uno al arrancar y compártelo en todas partes.
