---
seo_title: Go SDK für Dateikonvertierung ohne Abhängigkeiten | EnConvert
meta_desc: Das offizielle EnConvert Go-SDK für Go 1.21+: nur Standardbibliothek, keine Fremdabhängigkeiten, typisierte Dateikonvertierung plus V2-Web-Intelligenz.
keywords: go sdk dateikonvertierung, dateien konvertieren go, url zu pdf go, go web scraping api, docx zu pdf go, enconvert go sdk, golang html zu pdf bibliothek, heic zu webp golang, go markdown aus webseite extrahieren, golang website screenshot api, go rag pipeline, strukturierte daten extrahieren golang
---

# Go SDK für Dateikonvertierung

`github.com/conversionapi/go-sdk` ist der offizielle EnConvert-Client für Go 1.21 und neuer. Er wird als Paket `enconvert` importiert und bringt keine einzige Fremdabhängigkeit mit: Alles läuft auf `net/http`, `encoding/json` und `mime/multipart`. Zwölf Methoden am Client decken Dateikonvertierung, URL-Rendering und Batches für ganze Websites ab (DOCX zu PDF, HEIC zu WebP, URL zu PDF, URL zu Markdown), und der Namensraum `client.V2` ergänzt dreiundzwanzig Methoden für Web-Intelligenz, um Seiten wahrzunehmen, zu ermitteln, nachzuschlagen, zu destillieren, zu ingestieren und zu überwachen. Jeder Aufruf nimmt zuerst einen `context.Context` entgegen, Abbruch und Fristen bleiben also in deiner Hand.

<div class="alert alert-info">
<strong>Modul:</strong> <code>github.com/conversionapi/go-sdk</code> · <strong>Paket:</strong> <code>enconvert</code> · <strong>Quelle:</strong> <a href="https://github.com/conversionapi/go-sdk">conversionapi/go-sdk</a> · <strong>Go:</strong> 1.21+ · <strong>Abhängigkeiten:</strong> keine
</div>

---

## Installation

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

Der Modulpfad endet auf `go-sdk`, das Paket heißt jedoch `enconvert`, importiere es also unter einem expliziten Namen: `import enconvert "github.com/conversionapi/go-sdk"`.

---

## Schnellstart

```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` liefert nur dann einen Fehler, wenn der API-Schlüssel leer ist; die funktionalen Optionen beschreibt der Abschnitt [Konfiguration](#konfiguration). Eine Seite so zu lesen, wie ein Agent es tun sollte, mit angehängtem Qualitätswert, ist ein einziger Aufruf im V2-Namensraum:

```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) // z. B. 0.93
```

Jedes Beispiel unten geht von einem `client` und einem `ctx` aus, die genau so gebaut wurden.

---

## Was der Client bereitstellt

Zwölf Methoden hängen an `*enconvert.Client` und bilden REST-Endpunkte 1:1 ab. Optionen werden immer als Struct-Wert übergeben, nie als Pointer, der Nullwert (`enconvert.URLToPDFOptions{}`) bedeutet also "alle Standardwerte". Optionale Zahlen und Wahrheitswerte sind durchgehend Pointer-Felder: Nimm dafür die Helfer `enconvert.Int`, `enconvert.Bool`, `enconvert.Float64` und `enconvert.String` statt einer Wegwerf-Variable.

| Methode | Endpunkt | Rückgabe |
|--------|----------|---------|
| `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}` (per Polling) | `BatchStatus` |

`client.V2` hält dreiundzwanzig weitere Methoden in sechs Fähigkeitsgruppen bereit:

| Gruppe | Methoden | Basispfad |
|-------|---------|-----------|
| 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` |

---

## Dateikonvertierung

Uploads akzeptieren jede `FileSource`: `enconvert.FilePath("report.docx")` für einen Pfad auf der Festplatte (der Basisname bestimmt Eingabeformat und MIME-Typ), `enconvert.FileBytes(buf)` für rohe Bytes ohne Namen (hochgeladen als `upload.bin`, `application/octet-stream`) oder `enconvert.FileInput{Data: buf, Filename: "report.docx"}` für rohe Bytes mit explizitem Dateinamen und optionalem `ContentType`.

### ConvertURLToPDF

Rendert jede erreichbare URL als PDF.

```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",
})
```

| Option | Typ | Standard | Beschreibung |
|--------|------|---------|-------------|
| `SaveTo` | `string` | -- | Lokaler Pfad, auf den das PDF gestreamt wird. Übergeordnete Verzeichnisse werden für dich angelegt. |
| `SinglePage` | `*bool` | `true` | `true` rendert eine durchgehende Seite. `false` paginiert anhand von `PDFOptions.PageSize`. |
| `PDFOptions` | `*PDFOptions` | -- | Seitengröße, Ausrichtung, Ränder, Skalierung, Graustufen, Kopfzeile, Fußzeile. Siehe [PDF-Optionen](#pdf-optionen). |
| `ViewportWidth`, `ViewportHeight` | `*int` | `1920`, `1080` | Größe des Browser-Viewports in Pixeln. |
| `LoadMedia` | `*bool` | `true` | Wartet vor der Aufnahme auf Bilder und Videos. |
| `EnableScroll` | `*bool` | `true` | Scrollt von oben nach unten, um Lazy Loader auszulösen. |
| `OutputFilename` | `string` | auto | Überschreibt den generierten Dateinamen. |
| `Auth` | `*HTTPBasicAuth` | -- | HTTP-Basic-Anmeldedaten für Seiten hinter einem Login. |
| `Cookies`, `Headers` | `[]BrowserCookie`, `map[string]string` | -- | Cookies, die vor dem Rendern gesetzt werden (max. 50), und zusätzliche Request-Header (max. 20, Hop-by-Hop wird abgelehnt). |

Alles ab `ViewportWidth` abwärts lebt im eingebetteten Struct `URLRenderOptions`, das sich alle URL-basierten Methoden teilen.

<div class="alert alert-warning">
<strong>Kombiniere <code>Auth</code> nicht mit einem <code>Authorization</code>-Header.</strong> Die API lehnt den Konflikt ab, statt zu raten, was du gemeint hast.
</div>

### ConvertURLToScreenshot und ConvertURLToMarkdown

Nimm ein PNG einer beliebigen URL auf oder extrahiere sauberes GitHub-Flavored Markdown mit YAML-Frontmatter (title, description, url, links, images). Der Markdown-Konverter entfernt Navigation, Fußzeilen, Werbung und Skripte und behält den Hauptartikeltext.

```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"})
```

Beide nehmen dieselben `URLRenderOptions` wie `ConvertURLToPDF` entgegen, plus `SaveTo`. Keine der beiden akzeptiert `SinglePage` oder `PDFOptions`.

### ConvertImage

Konvertiert zwischen `jpeg`, `png`, `svg`, `heic` und `webp` in jede Richtung oder rastert ein PDF nach JPEG.

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

// Aus Bytes, die schon im Speicher liegen
buf, _ := os.ReadFile("photo.heic")
result, err = client.ConvertImage(ctx, enconvert.FileInput{Data: buf, Filename: "photo.heic"},
    enconvert.ConvertImageOptions{OutputFormat: "webp", SaveTo: "photo.webp"})
```

| Option | Typ | Erforderlich | Beschreibung |
|--------|------|----------|-------------|
| `OutputFormat` | `string` | Ja | `jpeg`, `png`, `svg`, `heic` oder `webp`. `jpg` wird als Alias für `jpeg` akzeptiert. |
| `SaveTo` | `string` | -- | Lokaler Pfad, auf den das Ergebnis gestreamt wird. |
| `OutputFilename` | `string` | -- | Überschreibt den generierten Dateinamen. |

Das Eingabeformat ergibt sich aus der Dateiendung (`.jpg`, `.jpeg`, `.png`, `.svg`, `.heic`, `.webp`, `.pdf`). Nicht unterstützte Paare scheitern lokal, vor jedem Netzwerkaufruf, mit einem Fehler, der die verfügbaren Optionen auflistet:

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

### ConvertDocument

Konvertiert Dokumente und Datenformate. `OutputFormat` steht standardmäßig auf `pdf`, wenn es leer bleibt.

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

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

**Unterstützte Eingaben:** `.doc`, `.docx`, `.xls`, `.xlsx`, `.ppt`, `.pptx`, `.html`, `.htm`, `.odt`, `.ods`, `.odp`, `.ots`, `.pages`, `.numbers`, `.md`, `.markdown`, `.csv`, `.json`, `.xml`, `.yaml`, `.yml`, `.toml`.

| Option | Typ | Standard | Beschreibung |
|--------|------|---------|-------------|
| `OutputFormat` | `string` | `"pdf"` | Zielformat. `yml`, `htm`, `md` und `jpg` werden auf ihre kanonischen Namen normalisiert. |
| `SaveTo` | `string` | -- | Lokaler Pfad, auf den das Ergebnis gestreamt wird. |
| `OutputFilename` | `string` | -- | Überschreibt den generierten Dateinamen. |
| `PDFOptions` | `*PDFOptions` | -- | Seitenlayout. Nur sinnvoll, wenn die Ausgabe ein PDF ist. |

Die 43 implementierten Paare, verfügbar als Map `enconvert.ImplementedConversions`, sind: `json` nach `csv`, `toml`, `xml`, `yaml`; `xml` nach `csv`, `json`; `yaml` nach `json`; `csv` nach `json`, `xml`; `toml` nach `json`; `markdown` nach `html`, `pdf`; `html` nach `pdf`; `doc`, `excel`, `ppt`, `odt`, `ods`, `odp`, `ots`, `pages` und `numbers` nach `pdf`; alle 20 geordneten Paare unter `jpeg`, `png`, `svg`, `heic`, `webp`; sowie `pdf` nach `jpeg`.

Für EPUB gibt es kein eigenes Dokumentpaar. Schicke `.epub`-Dateien stattdessen durch `ConvertToPDF` oder `ConvertToMarkdown`.

### ConvertToMarkdown

Erkennt die Eingabe serverseitig automatisch und liefert sauberes Markdown zurück. Das ist der RAG-Ingestion-Baustein: PDF, DOCX, PPTX, XLSX, CSV, HTML, EPUB, TXT und MD sowie alte und ODF-Office-Formate.

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

`ConvertToMarkdownOptions` hat zwei Felder, `SaveTo` und `OutputFilename`. Bilder unterstützt dieser Endpunkt nicht, und PDF-Optionen gibt es hier ebenfalls keine. Die Ausgabe behält die Überschriftenhierarchie des Dokuments, sodass ein semantischer Chunker an Überschriften trennen kann statt an willkürlichen Zeichenzahlen.

### ConvertToPDF

Erkennt die Eingabe serverseitig automatisch und liefert ein PDF zurück: Office, ODF, Pages, Numbers, RTF, CSV, HTML, Markdown, Klartext, Rasterbilder, SVG, EPUB oder ein bestehendes PDF, das zur Normalisierung durchgereicht wird.

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

// PDF durchgereicht und in Graustufen umgewandelt
_, 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>Nur <code>PDFOptions.Grayscale</code> wird hier berücksichtigt.</strong> Jedes andere Geometriefeld wird bei <code>anything-to-pdf</code> ignoriert. Nimm <code>ConvertDocument</code> oder <code>ConvertURLToPDF</code>, wenn du Seitengröße, Ausrichtung, Ränder, Skalierung, Kopf- oder Fußzeilen brauchst.
</div>

`ConvertToPDFOptions` hat drei Felder: `SaveTo`, `OutputFilename` und `PDFOptions`.

### ConvertWebsiteToPDF und ConvertWebsiteToScreenshot

Ermittelt jede Seite einer Website, konvertiert jede im Hintergrund und sammelt alles in einem einzigen ZIP. Beide sind ausschließlich asynchron: Sie liefern eine `BatchSubmission` zurück, und du fragst mit `GetBatchStatus` ab oder blockierst mit `WaitForBatch`. Erforderlich ist ein privater API-Schlüssel mit Crawl-Zugriff.

```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")
```

| Option | Typ | Standard | Beschreibung |
|--------|------|---------|-------------|
| `CrawlMode` | `CrawlMode` | `auto` | `CrawlModeAuto`, `CrawlModeSitemap` (nur sitemap.xml) oder `CrawlModeFull` (Sitemap plus BFS-Crawl). |
| `IncludePatterns` | `[]string` | -- | Crawlt nur URLs, die zu diesen Mustern passen, im Full-Crawl-Modus. |
| `ExcludePatterns` | `[]string` | -- | Überspringt URLs, die zu diesen Mustern passen, im Full-Crawl-Modus. |
| `NotificationEmail` | `string` | Projektinhaber | Adresse, die benachrichtigt wird, sobald der Batch fertig ist. |
| `CallbackURL` | `string` | -- | Webhook, der per POST aufgerufen wird, sobald der Batch fertig ist. |
| `SinglePage` | `*bool` | Server-Standard | Nur PDF. Durchgehende Seite oder paginiert. |
| `PDFOptions` | `*PDFOptions` | -- | Nur PDF. Wird auf jede Seite angewandt. |

Alles aus `URLRenderOptions` wird hier ebenfalls akzeptiert und nur dann pro Seite gesendet, wenn du es setzt. `WaitForBatchOptions` nimmt `Interval` (Standard 5 Sekunden), `Timeout` (Standard 30 Minuten) und `SaveTo` entgegen. Ein überschrittenes Timeout liefert einen `*APIError` mit Status `504`; ein fertiger Batch ohne ZIP liefert einen mit Status `500`. `ConvertWebsiteToScreenshot` ist identisch, nur ohne `SinglePage` und `PDFOptions`, und erzeugt ein ZIP mit PNGs.

---

## Web-Intelligenz (V2)

Jeder V2-Lesevorgang trägt einen `RenderQuality`-Wert zwischen 0.0 und 1.0, bereitgestellt als `*float64` auf `PerceiveResult`, `DistillItem`, `WatcherSnapshot` und dem Ergebnis des direkten Downloads. Ein niedriger Wert bedeutet, dass die Seite nicht sauber gerendert hat: eine Challenge-Seite, eine Cookie-Wand, eine leere SPA-Hülle, ein HTTP-Fehler. Der Inhalt kommt trotzdem zurück, markiert, zusammen mit einer `Deductions`-Map, die jeden ausgelösten Abzug benennt, und einem `Warnings`-Slice. So gelangt ein schlechter Lesevorgang nie unbemerkt in den Kontext deines Agenten. Alle V2-Methoden verlangen einen privaten API-Schlüssel; öffentliche Schlüssel werden abgelehnt. Prüfe den Wert, bevor du irgendetwas glaubst:

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

### Perceive

Rendert eine URL in die Artefakte, die du anforderst. Synchron, mit Artefakt-URLs, die 15 Minuten lang signiert sind.

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

// Die Artefakt-URLs später neu signieren
again, err := client.V2.GetPerceiveOperation(ctx, op.OperationID)
```

| Option | Typ | Standard | Beschreibung |
|--------|------|---------|-------------|
| `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` | -- | JSON-Schema für die strukturierte Extraktion über die LLM-Stufe. |
| `WaitFor`, `WaitTimeoutMs` | `string`, `*int` | `30000` ms | Ein CSS-Selektor, optional mit `css:` vorangestellt, oder `js:<expr>`, samt Zeitbudget (0 bis 60000). |
| `JSCode` | `string` | -- | JavaScript, das nach der Navigation ausgeführt wird, maximal 20000 Zeichen. |
| `Viewport` | `*PerceiveViewport` | 1920 x 1080 | `Width` 320 bis 3840, `Height` 240 bis 2160. |
| `Headers`, `Cookies`, `Auth` | `map[string]string`, `[]BrowserCookie`, `*HTTPBasicAuth` | -- | Request-Header, gesetzte Cookies, HTTP-Basic-Anmeldedaten. |
| `CacheMode` | `PerceiveCacheMode` | `enabled` | `enabled` (Cache für 1 Stunde), `bypass`, `refresh`. |
| `PDFOptions` | `*PDFOptions` | -- | Nur sinnvoll, wenn `Outputs` den Wert `pdf` enthält. |
| `BlockResources` | `[]PerceiveResourceType` | -- | `image`, `media`, `font`, `stylesheet`, `script`, `xhr`, `fetch`, `websocket`, `manifest`, `other`. |
| `RespectRobots`, `Mobile` | `*bool` | Server-Standard | `robots.txt` beachten; ein Mobilgerät emulieren. |
| `OnlyMainContent` | `*bool` | `true` | Entfernt Navigation, Header, Footer und Cookie-Banner aus dem Markdown-Artefakt und dem `main_content`-Extrakt. Setze `false` für die vollständige Seite. |
| `DirectDownload` | `*bool` | `false` | Streamt rohe Bytes statt eines JSON-Umschlags. Nimm besser `PerceiveDirect`. |

<div class="alert alert-warning">
<strong>Drei Optionen sind deklariert, aber noch nicht aktiv.</strong> <code>ProxyURL</code>, <code>Geolocation</code> und <code>ActionChain</code> werden vom Go-Struct akzeptiert und serialisiert, der Server antwortet auf alle drei aber derzeit mit <code>422</code>. Lass sie ungesetzt.
</div>

Ein einzelnes Artefakt direkt auf die Festplatte zu streamen spart den JSON-Umschlag. `PerceiveDirect` prüft lokal, dass du genau eine artefakterzeugende Ausgabe angefordert hast:

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

// Ein gespeichertes Artefakt später erneut laden. Übergib "", wenn die Operation nur eines erzeugt hat.
saved, err := client.V2.DownloadPerceiveArtifact(ctx, direct.OperationID, enconvert.PerceiveOutputPDF)
```

`PerceiveDirectResult` trägt `Content`, `ContentType`, `Filename`, `OperationID`, `ObjectKey`, `CacheHit`, `RenderQuality`, `SourceStatusCode`, `ContentHash` und `WarningsCount`, alle aus den Antwort-Headern gelesen. Ein `410`-`*APIError` von `DownloadPerceiveArtifact` bedeutet, dass das Artefakt aus seiner Aufbewahrungsfrist gefallen ist.

Batches nehmen bis zu 1000 URLs mit einem gemeinsamen Optionsblock entgegen. Kleine Batches werden inline fertig; größere kommen als `queued` zurück, frage also `GetPerceiveBatch` ab, bis `Status` den Wert `PerceiveBatchStatusCompleted` hat, und lies dann `Zip` oder `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` ist `PerceiveBatchOutputManifest` (Standard) oder `PerceiveBatchOutputZip`. `DirectDownload` wird bei Batches mit `422` abgelehnt.

### Discover

Zählt die URLs einer Website aus ihrer Sitemap, einem HTTP-Crawl oder beidem auf. Es wird kein Browser gestartet, das macht es schnell und günstig.

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

| Option | Typ | Standard | Beschreibung |
|--------|------|---------|-------------|
| `Mode` | `DiscoverMode` | `hybrid` | `DiscoverModeSitemap`, `DiscoverModeCrawl` oder `DiscoverModeHybrid` (Sitemap plus HTTP-Crawl). |
| `MaxURLs`, `MaxDepth` | `*int` | `100`, `2` | 1 bis 1000 URLs, Crawl-Tiefe 1 bis 5. |
| `IncludePatterns`, `ExcludePatterns` | `[]string` | -- | Regex-Positivliste, dann Negativliste, `re.search`-Semantik, je maximal 50. |
| `SameDomainOnly` | `*bool` | `true` | Bleibt auf der Domain der Start-URL. |
| `RespectRobots` | `*bool` | Server-Standard | Beachtet `robots.txt`. |

`DiscoverResult` liefert dir `URL`, `Mode`, `Total`, `URLs`, `PagesCrawled`, `Truncated`, `RobotsRespected`, `Sources` (rohe Zählungen pro Quelle vor der Deduplizierung) und `Warnings`.

### Lookup

Kategorisierte Websuche, die auf Wunsch die besten Treffer im selben Roundtrip rendert.

```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)
    }
}
```

| Option | Typ | Standard | Beschreibung |
|--------|------|---------|-------------|
| `Category` | `LookupCategory` | `web` | `web`, `news`, `images`, `scholar`, `patents`, `maps`. |
| `Country`, `Locale` | `string` | -- | Google-`gl`-Ländercode (`us`, `in`) und `hl`-Oberflächensprache (`en`). |
| `TimeFilter` | `LookupTimeFilter` | -- | `hour`, `day`, `week`, `month`, `year`. |
| `NumResults`, `Page` | `*int` | `10`, `1` | 1 bis 100 Treffer, Seite 1 bis 10. |
| `Location` | `string` | -- | Freitext-Ort, zum Beispiel `Austin, Texas`. |
| `Autocorrect` | `*bool` | `true` | Lässt den Anbieter Tippfehler in der Suchanfrage korrigieren. |
| `PerceiveTop` | `*int` | `0` | 0 bis 10. Führt ein vollständiges Browser-Rendering der Top-N-Ergebnis-URLs aus und hängt jedes `PerceiveResult` inline an. |

`LookupResult` trägt außerdem `AnswerBox`, `KnowledgeGraph`, `PerceiveOperationIDs` und `Warnings`.

### Distill

Schemagesteuerte strukturierte Extraktion. Gib genau eines von `URLs` (maximal 50) oder `DiscoverFrom` an; `Schema` ist immer erforderlich. Beide Regeln werden lokal geprüft, bevor eine Anfrage rausgeht.

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

Das optionale `CSSSchema` läuft zuerst und beantwortet mit einfachen Selektoren alles, was es kann; nur die Felder, die es verfehlt, eskalieren auf die LLM-Stufe, und `ExtractionTier` meldet, welche Stufen tatsächlich geantwortet haben (`css`, `llm`, `mixed` oder `none`). `CSSField.Type` ist einer der Werte `text`, `attribute`, `html`, `regex`, `nested`, `list` oder `nested_list`, verschachtelt bis zu 5 Ebenen tief.

Tausche `URLs` gegen `DiscoverFrom`, um in einem Aufruf zu ermitteln und dann zu destillieren. `DistillDiscoverFrom` nimmt `URL`, `Mode` (Standard `hybrid`) und `MaxPages` (1 bis 50, Standard 10, begrenzt sowohl Ermittlung als auch Destillation) entgegen:

```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

Verwandelt eine Website, eine URL-Liste oder einen Stapel hochgeladener Dokumente in gechunktes, RAG-fertiges JSONL. Immer asynchron.

```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)
}
```

| Option | Typ | Standard | Beschreibung |
|--------|------|---------|-------------|
| `Mode` | `IngestMode` | `urls` | `IngestModeURLs`, `IngestModeSitemap`, `IngestModeCrawl`, `IngestModeFiles`. |
| `URL` | `string` | -- | Start-URL. Erforderlich für `sitemap` und `crawl`, verboten für `urls`. |
| `URLs` | `[]string` | -- | Explizite URLs, maximal 1000. Erforderlich für `urls`, sonst verboten. |
| `MaxPages`, `MaxDepth` | `*int` | `50`, `2` | Ermittlungs-Limit 1 bis 1000 für `sitemap` und `crawl`, Tiefe 1 bis 5. |
| `SameDomainOnly` | `*bool` | `true` | Bleibt auf der Domain der Start-URL. |
| `IncludePatterns`, `ExcludePatterns` | `[]string` | -- | Regex-Positivliste, dann Negativliste. |
| `RespectRobots` | `*bool` | Server-Standard | Beachtet `robots.txt`. |
| `WaitFor`, `WaitTimeoutMs` | `string`, `*int` | `30000` ms | Selektor oder `js:`-Ausdruck, auf den pro Seite gewartet wird, samt Zeitbudget (0 bis 60000). |
| `Chunk` | `*IngestChunkOptions` | -- | `MaxWords` 32 bis 4000, Standard 512. `SentenceOverlap` 0 bis 10, Standard 1. |
| `WebhookURL` | `string` | -- | Webhook bei Fertigstellung, HMAC-signiert. |

Die obigen Regeln zu Modus und URL werden clientseitig durchgesetzt: `Ingest` liefert einen einfachen Go-Fehler statt eines API-Roundtrips, wenn du `URLs` zusammen mit `Mode: IngestModeSitemap` sendest.

Hochgeladene Dateien durchlaufen dieselbe Pipeline und denselben Job-Lebenszyklus:

```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)}})
```

Akzeptiert werden PDF, DOCX, PPTX, XLSX, CSV, HTML, EPUB, TXT und MD sowie alte oder ODF-Office-Dateien; mindestens eine Datei ist erforderlich. Job-Verwaltung und Webhook-Verkabelung:

```go
list, err := client.V2.ListIngestJobs(ctx, enconvert.V2ListOptions{Limit: enconvert.Int(20)})
canceled, err := client.V2.CancelIngestJob(ctx, job.JobID) // idempotent
secret, err := client.V2.GetWebhookSecret(ctx)             // Secret, SignatureHeader, SignatureScheme, ...
rotated, err := client.V2.RotateWebhookSecret(ctx)         // alte Signaturen gelten sofort nicht mehr
retry, err := client.V2.RetryIngestWebhook(ctx, job.JobID)
```

`RetryIngestWebhook` antwortet mit `409`, wenn der Job nicht abgeschlossen ist, und mit `400`, wenn kein Webhook konfiguriert ist. `V2ListOptions` nimmt `Skip` und `Limit` entgegen (1 bis 100, Standard 20).

### Watch

Rendert eine Seite in festem Takt neu und benachrichtigt dich, wenn sie sich ändert.

```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)
}
```

| Option | Typ | Standard | Beschreibung |
|--------|------|---------|-------------|
| `FrequencyMinutes` | `*int` | `60` | 60 bis 43200. Die Stundengrenze ist hart. |
| `DiffMode` | `WatchDiffMode` | `auto` | `WatchDiffAuto`, `WatchDiffText`, `WatchDiffStructured`, `WatchDiffTables`, `WatchDiffMetadata`. |
| `TrackFields` | `map[string]any` | -- | Feld- oder Selektor-Auswahl, die an die Diff-Engine übergeben wird. |
| `WebhookURL`, `NotifyEmail` | `string`, `*bool` | --, `true` | HMAC-signierter Änderungs-Webhook und die Frage, ob der Projektinhaber eine E-Mail bekommt. |

```go
// Ein Pointer auf den leeren String löscht den Webhook; nil lässt ihn unangetastet.
_, err = client.V2.UpdateWatcher(ctx, watcher.WatcherID, enconvert.WatcherUpdate{
    Status: enconvert.WatcherStatusPaused, WebhookURL: enconvert.String(""),
})
_, err = client.V2.DeleteWatcher(ctx, watcher.WatcherID) // Soft Delete, idempotent
```

`UpdateWatcher` verlangt mindestens ein Feld und liefert einen einfachen Go-Fehler, wenn du ihm ein leeres Struct übergibst. `Status` akzeptiert nur `WatcherStatusActive` oder `WatcherStatusPaused`; gelöscht wird über `DeleteWatcher`, das den stillgelegten Watcher mit Status `deleted` zurückgibt. `ListWatchers` und `GetWatcher` runden die Gruppe ab.

<div class="alert alert-warning">
<strong>Snapshot-Diffs enthalten nicht vertrauenswürdige Seiteninhalte.</strong> <code>WatcherSnapshot.Changes</code> ist ein Slice roher Maps, die von der überwachten Seite stammen. Escape die Werte, bevor du sie irgendwo renderst.
</div>

---

## PDF-Optionen

`PDFOptions` teilen sich `ConvertURLToPDF`, `ConvertDocument`, `ConvertWebsiteToPDF`, `PerceiveOptions` und (nur für `Grayscale`) `ConvertToPDF`. Gesendet werden ausschließlich die Felder, die du setzt.

```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)},
}
```

| Feld | Typ | Beschreibung |
|-------|------|-------------|
| `PageSize` | `string` | `"A4"`, `"A3"`, `"Letter"`, `"Legal"` und Verwandte. |
| `PageWidth`, `PageHeight` | `*float64` | Gemeinsam gesetzt überschreiben sie `PageSize`. |
| `Orientation` | `string` | `"portrait"` oder `"landscape"`. Standard ist Hochformat. |
| `Margins` | `*PDFMargins` | `Top`, `Bottom`, `Left`, `Right`, jeweils ein `*float64` in Punkt. Alle vier optional. |
| `Scale` | `*float64` | Render-Skalierung, zum Beispiel `0.9` für 90 %. |
| `Grayscale` | `*bool` | Wandelt das PDF per Nachbearbeitung in Graustufen um. |
| `Header`, `Footer` | `*PDFHeaderFooter` | Beide haben `Content` (maximal 2000 Zeichen) und `Height`. |

---

## Fehlerbehandlung

Go kennt keine Exception-Klassen, jeder API-Fehlschlag ist daher ein konkreter Typ, `*enconvert.APIError`, plus drei Prädikate. `Error()` wird als `"[<status>] <message>"` dargestellt.

```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) // Netzwerkfehler, Context-Abbruch, lokale Validierung
    }
}
```

| Prüfung | Ausgelöst bei | Statuscode |
|-------|-----------|-------------|
| `IsAuthenticationError(err)` | Ungültiger, fehlender oder widerrufener Schlüssel | `401`, `403` (beide werden als `401` verbucht) |
| `IsQuotaError(err)` | Jede Antwort, die die API mit `402` beantwortet | `402` |
| `IsRateLimitError(err)` | Rate-Limit überschritten | `429` |
| `errors.As(err, &apiErr)` | Jeder andere 4xx- oder 5xx-Fehler | der tatsächliche Code |

Fehler, die das Netzwerk nie erreichen, etwa ein nicht unterstütztes Konvertierungspaar, ein `Distill`-Aufruf mit `URLs` und `DiscoverFrom` gleichzeitig oder ein `PerceiveDirect`-Aufruf, der zwei Artefakte anfordert, kommen als einfache `error`-Werte aus `errors.New` oder `fmt.Errorf` zurück, nicht als `*APIError`. Die Antwortcodes sind in der [Fehlercode-Referenz](/de/docs/error-codes) katalogisiert.

---

## Timeout-Recovery

Lange URL-Renderings und große Dokumentkonvertierungen können die Obergrenze eines Reverse Proxy von 60 bis 120 Sekunden überdauern, selbst wenn der Job auf dem Server sauber durchläuft. Das SDK pollt sich da heraus, ganz ohne Code von dir:

1. Vor jeder Einzeldatei- und Einzel-URL-Konvertierung erzeugt der Client eine UUIDv4 und sendet sie als `job_id`.
2. Kommt diese Anfrage mit `>= 500` zurück, wechselt der Client still auf `GET /v1/convert/status/{job_id}` und fragt alle 3 Sekunden ab.
3. Bei `success` liefert er das Ergebnis. Bei `failed` liefert er `*APIError` mit Status `500` und der Meldung des Servers.
4. Die Polling-Frist beträgt 5 Minuten, danach bekommst du einen `*APIError` mit Status `504` und der Meldung `Conversion timed out`.

`ConversionResult.JobID` ist immer gefüllt, auch wenn der synchrone Pfad geklappt hat und die Antwort das Feld weggelassen hat, sodass du sie selbst an `GetJobStatus` übergeben kannst:

```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>Website-Batches steigen mit Absicht aus.</strong> <code>ConvertWebsiteToPDF</code> und <code>ConvertWebsiteToScreenshot</code> haben keine eigene Job-Zeile zum Abfragen, ein 5xx kommt dort also sofort an die Oberfläche, statt wiederholt zu werden. V2-Methoden nutzen den Job-Fallback ebenfalls nicht. Das Polling läuft innerhalb des <code>context.Context</code>, den du übergibst, ein Abbruch des Contexts beendet das Warten also sofort.
</div>

---

## Konfiguration

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

| Konstruktor-Eingabe | Typ | Standard | Beschreibung |
|-------------------|------|---------|-------------|
| `apiKey` (erstes Argument) | `string` | erforderlich | Privater API-Schlüssel. `New` liefert einen Fehler, wenn er leer ist. |
| `WithTimeout(d)` | `time.Duration` | `300 * time.Second` | Setzt `Timeout` am internen `*http.Client`, was auch Downloads einschließt. |
| `WithBaseURL(u)` | `string` | `https://api.enconvert.com` | Überschreibung für ein selbst gehostetes Gateway. Schrägstriche am Ende werden entfernt. |

Fristen pro Aufruf legen sich über den Context auf das Client-Timeout: Verpacke ihn mit `context.WithTimeout(context.Background(), 30*time.Second)` und übergib ihn als erstes Argument.

<div class="alert alert-warning">
<strong>Hardcode den API-Schlüssel niemals.</strong> Lies ihn aus einer Umgebungsvariable oder deinem Secret-Manager und halte ihn auf dem Server. Wer deinen privaten Schlüssel besitzt, kann Konvertierungen gegen dein Projekt ausführen.
</div>

Der Client lässt sich gefahrlos über Goroutinen hinweg teilen: Er hält einen `*http.Client` und keinen veränderlichen Zustand pro Anfrage. Baue einen beim Start und verwende ihn wieder. Schlüsseltypen und Rotation beschreibt die [Authentifizierung](/de/docs/authentication).

---

## Form des Ergebnisses

Einzeldatei- und Einzel-URL-Konvertierungen liefern ein `ConversionResult`:

```go
type ConversionResult struct {
    PresignedURL          string   // signierte Download-URL für die Ausgabe
    ObjectKey             string   // Objektschlüssel im Speicher
    Filename              string   // serverseitiger Dateiname
    FileSize              *int64   // Bytes, nil wenn die API das Feld weglässt
    ConversionTimeSeconds *float64 // nil wenn die API das Feld weglässt
    JobID                 string   // wird immer vom Client gesetzt
}
```

Vorsignierte URLs sind kurzlebig. Übergib `SaveTo`, damit das SDK die Bytes für dich auf die Festplatte streamt, oder rufe die URL selbst ab und lege die Datei in deinem eigenen Bucket ab, wenn du langfristigen Zugriff brauchst. Der Download trägt bewusst deinen API-Schlüssel nicht mit, denn eine vorsignierte URL authentifiziert sich selbst, und den Schlüssel an einen Storage-Host weiterzureichen würde ihn preisgeben.

V2-Artefakte kommen als `V2OutputArtifact`-Werte an, geschlüsselt nach Ausgabename, und tragen jeweils `URL` (15 Minuten lang vorsigniert und bei jedem Status-GET neu signiert), `ObjectKey`, `SizeBytes`, `ContentType` und `ExpiresIn` (standardmäßig 900 Sekunden). `PerceiveResult` umhüllt sie mit den Ehrlichkeits-Metadaten: `RenderQuality`, `StatusCode`, `Deductions`, `CacheHit`, `Warnings`, `ContentHash`, `URLFinal`, `Structured`, `ExtractionTier`, `Tokens`, `CostCents`, `DurationMs` und `OptionsEcho`, das die vom Server tatsächlich berücksichtigten Optionen zurückspiegelt, wobei Geheimnisse auf Wahrheitswerte reduziert werden.

---

## Quellcode und Issues

- **Modul und Quelle:** [github.com/conversionapi/go-sdk](https://github.com/conversionapi/go-sdk)
- **Version:** zur Laufzeit als Konstante `enconvert.Version` verfügbar
- **Lizenz:** MIT, keine Fremdabhängigkeiten

Weiterführend: [alle SDKs](/de/docs/sdks), [V2-Übersicht](/de/docs/v2-overview), [perceive](/de/docs/v2-perceive), [discover](/de/docs/v2-discover), [lookup](/de/docs/v2-lookup), [distill](/de/docs/v2-distill), [ingest](/de/docs/v2-ingest), [watch](/de/docs/v2-watch), [Endpunkt-Übersicht](/de/docs/endpoints-overview), [Parameter und Optionen](/de/docs/parameters-options) sowie dein [Dashboard](/de/dashboard) für die Schlüssel.

---

## Häufig gestellte Fragen

### Wie konvertiere ich Dateien in Go?

Führe `go get github.com/conversionapi/go-sdk` aus, baue einen Client mit `enconvert.New(os.Getenv("ENCONVERT_API_KEY"))` und rufe dann eine typisierte Methode wie `ConvertDocument`, `ConvertImage` oder `ConvertURLToPDF` auf. Übergib `SaveTo` im Options-Struct, dann streamt das SDK die fertige Datei direkt an diesen Pfad und legt übergeordnete Verzeichnisse bei Bedarf an.

### Wie konvertiere ich in Go eine URL nach PDF?

Rufe `client.ConvertURLToPDF(ctx, url, enconvert.URLToPDFOptions{SaveTo: "page.pdf"})` auf. Setze `SinglePage: enconvert.Bool(false)`, um zu paginieren statt eine durchgehende Seite zu erzeugen, und übergib `PDFOptions` für Seitengröße, Ausrichtung, Ränder, Skalierung, Graustufen, Kopf- und Fußzeilen.

### Wie konvertiere ich in Go DOCX nach PDF?

`client.ConvertDocument(ctx, enconvert.FilePath("report.docx"), enconvert.ConvertDocumentOptions{SaveTo: "report.pdf"})`. Das Ausgabeformat steht standardmäßig auf `pdf`, du kannst `OutputFormat` also leer lassen. Dieselbe Methode bewältigt Eingaben in XLSX, PPTX, ODT, ODS, ODP, OTS, Pages, Numbers, HTML, Markdown, CSV, JSON, XML, YAML und TOML.

### Wie konvertiere ich in Go HEIC nach WebP?

`client.ConvertImage(ctx, enconvert.FilePath("photo.heic"), enconvert.ConvertImageOptions{OutputFormat: "webp", SaveTo: "photo.webp"})`. Das Eingabeformat wird aus der Dateiendung gelesen, und alle 20 geordneten Paare unter `jpeg`, `png`, `svg`, `heic` und `webp` funktionieren genauso. Nicht unterstützte Paare scheitern lokal, bevor eine Anfrage gesendet wird.

### Zieht das Go-SDK Fremdabhängigkeiten nach sich?

Nein. `go.mod` deklariert das Modul und eine Untergrenze von Go 1.21, sonst nichts. Der Client baut auf `net/http`, `encoding/json`, `mime/multipart` und `crypto/rand` aus der Standardbibliothek auf, er fügt deinem Build also keine transitive Lieferkette hinzu.

### Wie lese ich eine Webseite in Go als sauberes Markdown aus?

Es gibt zwei Wege. `client.ConvertURLToMarkdown` liefert GitHub-Flavored Markdown mit YAML-Frontmatter und ist der einfachste Pfad. `client.V2.Perceive` mit `Outputs: []enconvert.PerceiveOutputName{enconvert.PerceiveOutputMarkdown}` gibt dir dasselbe Markdown plus einen `RenderQuality`-Wert, `Deductions`, `Warnings` und die Möglichkeit, im selben Rendering Screenshots, Links oder eine strukturierte Extraktion zu ergänzen.

### Was bedeutet Render-Qualität und warum sollte ich sie prüfen?

`RenderQuality` ist ein `*float64` zwischen 0.0 und 1.0, das an jedem V2-Lesevorgang hängt. Der Wert sinkt, wenn die Seite nicht ehrlich gerendert hat: eine Bot-Challenge, eine Login-Schranke, ein Cookie-Banner über einer leeren Hülle oder ein HTTP-Fehlerstatus. Der Inhalt wird trotzdem zurückgegeben statt verschluckt, prüfe also den Wert (und die `Deductions`-Map, die jeden Abzug benennt), bevor du den Text an ein Modell gibst.

### Wie setze ich in Go ein Timeout pro Anfrage oder breche eine Konvertierung ab?

Jede Methode nimmt zuerst einen `context.Context` entgegen. Verpacke ihn mit `context.WithTimeout` oder `context.WithCancel` für die Kontrolle pro Aufruf; `WithTimeout` am Konstruktor setzt das grundlegende HTTP-Client-Timeout für alle Aufrufe, einschließlich des Downloads einer `SaveTo`-Datei.

### Was passiert, wenn eine lange Konvertierung ins Proxy-Timeout läuft?

Das SDK sendet bei jeder Einzeldatei- und Einzel-URL-Konvertierung eine clientseitig erzeugte `job_id` mit. Liefert die Anfrage `>= 500`, fragt es bis zu 5 Minuten lang alle 3 Sekunden `GET /v1/convert/status/{job_id}` ab und gibt bei `success` das Ergebnis zurück, bei `failed` einen `*APIError`. Wird die Frist überschritten, bekommst du einen `*APIError` mit Status `504`. Website-Batch-Einreichungen überspringen diesen Fallback bewusst.

### Kann ich das Go-SDK in einem Desktop- oder Mobil-Client ausliefern?

Nein. Es authentifiziert sich mit einem privaten API-Schlüssel, und V2-Endpunkte lehnen öffentliche Schlüssel rundweg ab. Halte den Client auf einem Server, den du kontrollierst, und lass deine App mit diesem sprechen. Das Schlüsselmodell beschreibt die [Authentifizierung](/de/docs/authentication).

### Lässt sich der Go-Client aus mehreren Goroutinen nutzen?

Ja. `*enconvert.Client` umhüllt einen einzelnen `*http.Client` und hält keinen veränderlichen Zustand pro Anfrage, baue also einen beim Start und teile ihn überall.
