C#- und .NET-SDK für Dateikonvertierung#

Enconvert ist der offizielle C#- und .NET-Client für die EnConvert API, veröffentlicht auf NuGet und ausgelegt auf .NET 8 oder neuer. Er konvertiert Dateien aus C# heraus: URL zu PDF, DOCX zu PDF, HEIC zu WebP, JSON zu YAML, jedes Dokument zu Markdown und ganze Websites in ein einziges ZIP. Derselbe Client trägt einen V2-Namensraum für Web Scraping und Web-Intelligence, ein Key deckt also perceive, discover, lookup, distill, ingest und watch ab. Jeder Aufruf ist asynchron und abbrechbar, jede Antwort ist ein typisiertes Record, und das Paket zieht keine Drittanbieter-Abhängigkeiten nach.

NuGet: Enconvert · Quelle: conversionapi/csharp-sdk · Laufzeit: .NET 8+ · Abhängigkeiten: keine über die BCL hinaus

Installation#

dotnet add package Enconvert

Die Assembly zielt auf net8.0 mit aktivierten Nullable Reference Types und baut ausschließlich auf System.Net.Http und System.Text.Json auf.

Schnellstart#

using Enconvert;

using var client = new EnconvertClient(Environment.GetEnvironmentVariable("ENCONVERT_API_KEY")!);

// Eine Live-Seite in ein PDF konvertieren und auf die Festplatte streamen.
var pdf = await client.ConvertUrlToPdfAsync("https://example.com", new UrlToPdfOptions { SaveTo = "page.pdf" });
Console.WriteLine(pdf.PresignedUrl);

// Lies eine Seite so, wie es dein Agent tun sollte, mit angehängtem Quality-Score.
var op = await client.V2.PerceiveAsync("https://example.com", new PerceiveOptions { Outputs = new[] { "markdown", "structured" } });
Console.WriteLine($"{op.Outputs["markdown"].Url} scored {op.RenderQuality}");

EnconvertClient implementiert IDisposable, deklariere ihn also mit using oder registriere ihn als Singleton. Er authentifiziert sich mit einem privaten API-Key, gehört also auf den Server: Liefere den Key niemals in einem Desktop-, Mobile- oder Blazor-WebAssembly-Build aus. Die Key-Einrichtung behandelt die Authentifizierung.


Was der Client bereitstellt#

Mitglied Endpunkt Rückgabe
ConvertUrlToPdfAsync(url, opts?) POST /v1/convert/url-to-pdf ConversionResult
ConvertUrlToScreenshotAsync(url, opts?) POST /v1/convert/url-to-screenshot ConversionResult
ConvertUrlToMarkdownAsync(url, opts?) POST /v1/convert/url-to-markdown ConversionResult
ConvertImageAsync(file, opts) POST /v1/convert/{from}-to-{to} ConversionResult
ConvertDocumentAsync(file, opts?) POST /v1/convert/{from}-to-{to} ConversionResult
ConvertToMarkdownAsync(file, opts?) POST /v1/convert/anything-to-markdown ConversionResult
ConvertToPdfAsync(file, opts?) POST /v1/convert/anything-to-pdf ConversionResult
ConvertWebsiteToPdfAsync(url, opts?) POST /v1/convert/website-to-pdf BatchSubmission
ConvertWebsiteToScreenshotAsync(url, opts?) POST /v1/convert/website-to-screenshot BatchSubmission
GetJobStatusAsync(jobId) GET /v1/convert/status/{jobId} JobStatus
GetBatchStatusAsync(batchId) GET /v1/convert/batch/{batchId} BatchStatus
WaitForBatchAsync(batchId, opts?) GET /v1/convert/batch/{batchId} (mit Polling) BatchStatus
V2 die /v2/*-Oberfläche EnconvertV2

client.V2 bündelt 23 Methoden über sechs Fähigkeiten:

Fähigkeit Methoden Referenz
Perceive PerceiveAsync, GetPerceiveOperationAsync, PerceiveBatchAsync, GetPerceiveBatchAsync, PerceiveDirectAsync, DownloadPerceiveArtifactAsync /de/docs/v2-perceive
Discover DiscoverAsync /de/docs/v2-discover
Lookup LookupAsync /de/docs/v2-lookup
Distill DistillAsync /de/docs/v2-distill
Ingest IngestAsync, IngestFilesAsync, ListIngestJobsAsync, GetIngestJobAsync, CancelIngestJobAsync, RetryIngestWebhookAsync, GetWebhookSecretAsync, RotateWebhookSecretAsync /de/docs/v2-ingest
Watch CreateWatcherAsync, ListWatchersAsync, GetWatcherAsync, GetWatcherSnapshotsAsync, UpdateWatcherAsync, DeleteWatcherAsync /de/docs/v2-watch

Jede Methode nimmt ein optionales abschließendes CancellationToken. Optionen sind Records mit init-Eigenschaften, baue sie also mit einem Objektinitialisierer und verwende sie mit with weiter. Clients für andere Sprachen findest du im SDK-Index; die rohe REST-Oberfläche steht in der Endpunkt-Übersicht.


Datei-Konvertierung#

ConvertUrlToPdfAsync#

Rendere jede erreichbare URL zu einem PDF.

var result = await client.ConvertUrlToPdfAsync("https://example.com", new UrlToPdfOptions
{
    SinglePage = false,
    ViewportWidth = 1440,
    PdfOptions = new PdfOptions { PageSize = "A4", Orientation = "landscape" },
    SaveTo = "report.pdf",
});

Console.WriteLine($"{result.Filename} ({result.FileSize} bytes)");
Option Typ Standard Beschreibung
SaveTo string? -- Lokaler Pfad, in den das PDF gestreamt wird. Fehlende übergeordnete Verzeichnisse werden angelegt.
SinglePage bool? true true erzeugt eine einzige fortlaufende Seite. false paginiert anhand von PdfOptions.PageSize.
PdfOptions PdfOptions? -- Seitengröße, Ausrichtung, Ränder, Skalierung, Graustufen, Kopf- und Fußzeile. Siehe PDF-Optionen.
ViewportWidth, ViewportHeight int? 1920, 1080 Browser-Viewport in Pixeln.
LoadMedia, EnableScroll bool? true Vor der Erfassung auf Bilder und Videos warten; von oben nach unten scrollen, damit Lazy-Loader auslösen.
OutputFilename string? auto Überschreibt den generierten Dateinamen.
Auth HttpBasicAuth? -- HTTP-Basic-Zugangsdaten für eine Seite hinter einem Login.
Cookies, Headers IReadOnlyList<BrowserCookie>?, IReadOnlyDictionary<string, string>? -- Bis zu 50 Cookies, die vor dem Rendern eingeschleust werden, und bis zu 20 zusätzliche Request-Header. Hop-by-Hop-Header werden abgelehnt.
Kombiniere Auth nicht mit deinem eigenen Authorization-Header. Die API lehnt diesen Konflikt ab, statt zu raten, welche Zugangsdaten gewinnen. Entscheide dich für eines.

ConvertUrlToScreenshotAsync#

Erfasse ein PNG einer beliebigen URL.

await client.ConvertUrlToScreenshotAsync("https://example.com", new UrlToScreenshotOptions
{
    ViewportWidth = 1440,
    SaveTo = "screenshot.png",
});

UrlToScreenshotOptions akzeptiert dieselben Optionen für Viewport, Medien, Scrollen, Dateiname und Zugriff wie ConvertUrlToPdfAsync, ohne SinglePage und PdfOptions.

ConvertUrlToMarkdownAsync#

Extrahiere sauberes GitHub-Flavored Markdown aus einer URL. Navigation, Fußzeilen, Werbung und Skripte werden entfernt, der Haupttext des Artikels bleibt erhalten, und YAML-Frontmatter (Titel, Beschreibung, URL, Links, Bilder) wird vorangestellt.

await client.ConvertUrlToMarkdownAsync("https://example.com/article", new UrlToMarkdownOptions { SaveTo = "article.md" });

UrlToMarkdownOptions teilt sich dieselben Render- und Zugriffsoptionen. Für agentenorientierte Lesevorgänge, die zusätzlich einen Quality-Score und strukturierte Extraktion brauchen, nimm stattdessen Perceive.

ConvertImageAsync#

Konvertiere zwischen jpeg, png, svg, heic und webp in jede Richtung und rastere ein PDF zu JPEG.

// Von einem Pfad.
await client.ConvertImageAsync("photo.heic", new ConvertImageOptions { OutputFormat = "webp", SaveTo = "photo.webp" });

// Aus Bytes, mit explizitem Dateinamen, damit das Eingabeformat erkannt werden kann.
var bytes = await File.ReadAllBytesAsync("scan.pdf");
await client.ConvertImageAsync(new FileInput(bytes, "scan.pdf"), new ConvertImageOptions
{
    OutputFormat = "jpeg",
    SaveTo = "scan.jpeg",
});

Drei Überladungen akzeptieren einen Pfad als string, ein rohes byte[] oder ein FileInput-Record. Das Eingabeformat ergibt sich aus der Dateiendung, bevorzuge also FileInput, wenn du Bytes hältst: Die reine byte[]-Überladung sendet upload.bin, was nur an den automatisch erkennenden Endpunkten funktioniert.

Option Typ Erforderlich Beschreibung
OutputFormat string Ja Zielformat. jpg, yml, htm und md werden auf ihre kanonischen Namen normalisiert.
SaveTo string? -- Lokaler Pfad, in den das Ergebnis gestreamt wird.
OutputFilename string? -- Überschreibt den generierten Dateinamen.

Nicht unterstützte Paare werfen eine ArgumentException, bevor eine HTTP-Anfrage gestellt wird, ein Tippfehler kostet also nichts.

ConvertDocumentAsync#

Konvertiere Dokumente und Datenformate. OutputFormat ist standardmäßig "pdf".

// docx zu pdf
await client.ConvertDocumentAsync("report.docx", new ConvertDocumentOptions { SaveTo = "report.pdf" });

// json zu yaml
await client.ConvertDocumentAsync("data.json", new ConvertDocumentOptions { OutputFormat = "yaml", SaveTo = "data.yaml" });

// markdown zu pdf mit Seitengeometrie
await client.ConvertDocumentAsync("README.md", new ConvertDocumentOptions
{
    PdfOptions = new PdfOptions { PageSize = "A4", Margins = new PdfMargins { Top = 20, Bottom = 20 } },
    SaveTo = "readme.pdf",
});

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. EPUB hat kein eigenes Dokumentpaar, schicke .epub also durch ConvertToPdfAsync oder ConvertToMarkdownAsync.

Option Typ Standard Beschreibung
OutputFormat string? "pdf" Zielformat.
SaveTo string? -- Lokaler Pfad, in den das Ergebnis gestreamt wird.
OutputFilename string? -- Überschreibt den generierten Dateinamen.
PdfOptions PdfOptions? -- Seiteneinrichtung. Nur sinnvoll, wenn die Ausgabe ein PDF ist.

Wie ConvertImageAsync hat diese Methode Überladungen für Pfad, byte[] und FileInput.

ConvertToMarkdownAsync#

Schicke fast jedes Dokument an den automatisch erkennenden Markdown-Endpunkt: PDF, DOCX, PPTX, XLSX, CSV, HTML, EPUB, TXT und MD sowie ältere und ODF-Office-Formate. Bilder werden hier nicht unterstützt.

await client.ConvertToMarkdownAsync("handbook.docx", new ConvertToMarkdownOptions { SaveTo = "handbook.md" });

Das Format wird serverseitig erkannt, es gibt also keine clientseitige Prüfung der Erweiterung und keine PDF-Optionen an diesem Endpunkt. ConvertToMarkdownOptions trägt nur SaveTo und OutputFilename. Die Ausgabe ist eine einzelne, überschriften-bewusste .md-Datei, was sie zu einer soliden ersten Stufe in einer RAG-Pipeline macht: Ein semantischer Chunker kann anhand der Überschriften-Hierarchie des Dokuments trennen statt anhand willkürlicher Zeichenzahlen.

ConvertToPdfAsync#

Der andere automatisch erkennende Endpunkt: Office, ODF, Pages, Numbers, RTF, CSV, HTML, Markdown, reiner Text, Rasterbilder, SVG, EPUB oder ein bestehendes PDF, das zur Normalisierung durchgereicht wird.

// Folien zu PDF.
await client.ConvertToPdfAsync("slides.pptx", new ConvertToPdfOptions { SaveTo = "slides.pdf" });

// PDF-Durchreichung, in Graustufen umgewandelt.
await client.ConvertToPdfAsync("scan.pdf", new ConvertToPdfOptions
{
    PdfOptions = new PdfOptions { Grayscale = true },
    SaveTo = "gray.pdf",
});
Nur Grayscale wird hier berücksichtigt. Der anything-to-pdf-Endpunkt ignoriert den Rest von PdfOptions. Wenn du Seitengröße, Ausrichtung, Ränder oder eine Kopf- und Fußzeile brauchst, leite HTML und Markdown über ConvertDocumentAsync, oder rendere die Seite mit ConvertUrlToPdfAsync.

ConvertToPdfOptions trägt SaveTo, OutputFilename und PdfOptions und hat dieselben drei Eingabe-Überladungen wie die Methoden oben.

Batches für ganze Websites#

ConvertWebsiteToPdfAsync und ConvertWebsiteToScreenshotAsync ermitteln jede Seite einer Website, konvertieren jede einzelne im Hintergrund und bündeln die Ergebnisse in einem einzigen ZIP. Beide arbeiten ausschließlich asynchron und benötigen einen privaten API-Key.

var batch = await client.ConvertWebsiteToPdfAsync("https://example.com", new WebsiteToPdfOptions
{
    CrawlMode = "sitemap",
    ExcludePatterns = new[] { "/blog/tag/" },
    NotificationEmail = "[email protected]",
});
Console.WriteLine($"{batch.BatchId}: {batch.UrlCount} pages via {batch.DiscoveryMethod}");

// Blockiert, bis das ZIP bereit ist, dann speichern.
var status = await client.WaitForBatchAsync(batch.BatchId, new WaitForBatchOptions { SaveTo = "site.zip" });
Console.WriteLine($"{status.Completed} of {status.Total} converted, {status.Failed} failed");

Wenn du stattdessen nach eigenem Zeitplan abfragen willst, rufe GetBatchStatusAsync(batchId) auf und lies ZipDownloadUrl, sobald Status den Wert "processing" verlässt.

Option Typ Standard Beschreibung
CrawlMode string? "auto" "auto", "sitemap" (nur sitemap.xml) oder "full" (Sitemap plus Breitensuche).
IncludePatterns, ExcludePatterns IReadOnlyList<string>? -- Nur URLs crawlen, die zu diesen Mustern passen, oder sie überspringen. Full-Crawl-Modus.
NotificationEmail, CallbackUrl string? Projektinhaber, -- Adresse, die bei Abschluss benachrichtigt wird, und ein Webhook, der bei Abschluss per POST aufgerufen wird.
SinglePage, PdfOptions bool?, PdfOptions? -- Nur für PDF-Batches. Optionen für Viewport, Medien, Scrollen und Zugriff werden mit den Einzel-URL-Methoden geteilt.

WaitForBatchOptions nimmt IntervalMs (Standard 5_000), TimeoutMs (Standard 1_800_000, also 30 Minuten) und SaveTo. Wird die Frist überschritten, wirft es eine ApiException mit Status 504.

GetJobStatusAsync#

Frage einen einzelnen Konvertierungsjob per ID ab.

var status = await client.GetJobStatusAsync("job_abc123");

if (status.Status == "success") Console.WriteLine(status.PresignedUrl);
else if (status.Status == "failed") Console.Error.WriteLine(status.Error);
Du brauchst das selten direkt. Der Client fragt den Job-Status bereits für dich ab, wenn eine synchrone Anfrage am Proxy stirbt. Siehe Timeout-Recovery.

Unterstützte Konvertierungspaare#

Das SDK spiegelt die Konverter-Map des Gateways und implementiert 43 typisierte {input}-to-{output}-Paare.

Eingabe Ausgaben
json csv, toml, xml, yaml
xml csv, json
yaml json
csv json, xml
toml json
markdown html, pdf
html pdf
doc, excel, ppt, odt, ods, odp, ots, pages, numbers pdf
jpeg, png, svg, heic, webp untereinander, alle 20 Paare
pdf jpeg

Die statische Klasse Formats stellt dieselbe Tabelle bereit, du kannst also validieren, bevor du eine Oberfläche oder eine Job-Queue baust:

Formats.ValidOutputsFor("json");        // ["csv", "toml", "xml", "yaml"]
Formats.ValidOutputsFor("pdf");         // ["jpeg"]
Formats.ImplementedConversions;         // die vollständige Menge der 43 Endpunktnamen
Formats.NormalizeOutputFormat(".JPG");  // "jpeg"

Alles außerhalb dieser Tabelle läuft über ConvertToPdfAsync oder ConvertToMarkdownAsync, die das Format serverseitig erkennen. Die vollständige Parameter-Referenz steht unter Parameter und Optionen.


Web-Intelligence (V2)#

Jeder V2-Lesevorgang trägt RenderQuality, einen Wert von 0.0 bis 1.0, der angibt, wie ehrlich die Seite gerendert wurde. Eine Bot-Challenge, eine Cookie- oder Login-Wall, ein HTTP-Fehler oder eine leere Single-Page-App-Hülle kommt mit niedrigem Wert, einer gefüllten Deductions-Map, die benennt, welche Prüfungen angeschlagen haben, und einer Warnings-Liste zurück, während der Inhalt trotzdem geliefert wird. Genau das ist der Punkt: Ein schlechter Lesevorgang wird markiert, statt still in den Kontext deines Agenten zu gelangen. Derselbe Wert erscheint an Perceive-Ergebnissen, Distill-Einträgen, automatisch geperceiveten Lookup-Treffern und Watcher-Snapshots. Die Konzepte behandelt die V2-Übersicht. V2-Endpunkte verlangen einen privaten API-Key; öffentliche Keys werden abgelehnt.

Perceive#

Rendere eine URL in agentenfertige Artefakte.

var op = await client.V2.PerceiveAsync("https://example.com", new PerceiveOptions
{
    Outputs = new[] { "markdown", "screenshot", "structured" },
    Extract = new[] { "tables", "metadata" },
    OnlyMainContent = true,
});

Console.WriteLine(op.RenderQuality);            // 0.0 bis 1.0
Console.WriteLine(op.StatusCode);               // HTTP-Status der Ursprungsseite
Console.WriteLine(op.Outputs["markdown"].Url);  // 15 Minuten lang signiert
Console.WriteLine(op.Structured);               // JsonObject, die Struktur bestimmst du

foreach (var (check, penalty) in op.Deductions) Console.WriteLine($"deduction {check}: {penalty}");
Option Typ Standard Beschreibung
Outputs IReadOnlyList<string>? ["markdown", "structured"] Beliebige aus markdown, html_cleaned, html_raw, screenshot, screenshot_full_page, pdf, links, images, structured.
Extract IReadOnlyList<string>? -- Heuristische Ziele: tables, prices, contacts, metadata, main_content, headings, structured_data, technologies, all.
Schema JsonObject? -- JSON-Schema, das die strukturierte Extraktion der LLM-Stufe steuert.
WaitFor, WaitTimeoutMs string?, int? --, 30000 Ein CSS-Selektor (optional als css:...) oder js:<expr>, auf den vor der Erfassung gewartet wird, begrenzt auf 0 bis 60000 ms.
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 siehe oben -- Zusätzliche Header, eingeschleuste Cookies, HTTP-Basic-Zugangsdaten.
CacheMode string? "enabled" "enabled" nutzt einen Cache von 1 Stunde erneut, "bypass" überspringt ihn, "refresh" rendert neu.
PdfOptions PdfOptions? -- Nur sinnvoll, wenn Outputs den Wert pdf enthält.
BlockResources IReadOnlyList<string>? -- Ressourcentypen, die übersprungen werden: image, media, font, stylesheet, script.
RespectRobots, Mobile bool? -- robots.txt berücksichtigen; ein Mobilgerät emulieren.
OnlyMainContent bool? true Entfernt Navigation, Header, Footer und Cookie-Banner aus dem Markdown-Artefakt und dem main_content-Extrakt.
DirectDownload bool? -- Streamt Artefakt-Bytes statt des JSON-Envelopes. Bevorzuge PerceiveDirectAsync.

ProxyUrl, Geolocation und ActionChain existieren in PerceiveOptions, sind serverseitig aber noch nicht verfügbar und kommen derzeit als 422 zurück.

Signiere Artefakt-URLs später neu, bündle bis zu 1000 URLs oder streame Bytes ohne den Umweg über die signierte URL:

// Artefakt-URLs werden bei jedem Abruf der Operation neu signiert.
var again = await client.V2.GetPerceiveOperationAsync(op.OperationId);

// Batches: Kleine laufen inline, größere geben "queued" zurück, also abfragen.
var batch = await client.V2.PerceiveBatchAsync(
    new[] { "https://a.example.com", "https://b.example.com" },
    new PerceiveBatchOptions { Outputs = new[] { "markdown" }, OutputMode = "zip" });

var done = await client.V2.GetPerceiveBatchAsync(batch.JobId);
Console.WriteLine($"{done.Completed}/{done.Total} done, {done.Failed} failed, zip {done.Zip?.Url}");

// Direkter Download: genau eine artefakterzeugende Ausgabe erforderlich.
var direct = await client.V2.PerceiveDirectAsync("https://example.com", new PerceiveOptions { Outputs = new[] { "pdf" } });
await File.WriteAllBytesAsync(direct.Filename ?? "page.pdf", direct.Content);

// Ein gespeichertes Artefakt einer früheren Operation erneut herunterladen.
var artifact = await client.V2.DownloadPerceiveArtifactAsync(op.OperationId, "markdown");

PerceiveDirectAsync wirft lokal eine ArgumentException, sofern nicht genau eines von markdown, html_cleaned, html_raw, screenshot, screenshot_full_page, pdf, links oder images angefordert wird, da structured inline-JSON und keine gespeicherte Datei ist. Beide direkten Methoden geben ein PerceiveDirectResult zurück, das Content, ContentType, Filename, OperationId, ObjectKey, CacheHit, RenderQuality, SourceStatusCode, ContentHash und WarningsCount trägt. DownloadPerceiveArtifactAsync nimmt den Ausgabenamen nur, wenn die Operation mehr als ein Artefakt erzeugt hat, und gibt 410 zurück, sobald das Artefakt seine Aufbewahrungsfrist überschritten hat.

Discover#

Zähle die URLs einer Website ohne Browser-Rendering auf, was deutlich günstiger ist als ein Crawl.

var found = await client.V2.DiscoverAsync("https://example.com", new DiscoverOptions
{
    Mode = "hybrid",
    MaxUrls = 200,
    ExcludePatterns = new[] { "/tag/" },
});

Console.WriteLine($"{found.Total} urls, truncated: {found.Truncated}");
foreach (var url in found.Urls) Console.WriteLine(url);
Option Typ Standard Beschreibung
Mode string? "hybrid" "sitemap", "crawl" oder "hybrid" (Sitemap plus HTTP-Crawl).
MaxUrls int? 100 1 bis 1000.
MaxDepth int? 2 Crawl-Tiefe, 1 bis 5.
IncludePatterns, ExcludePatterns IReadOnlyList<string>? -- Regex-Positivliste und Sperrliste, jeweils maximal 50 Einträge. Die Sperrliste wird als zweites angewendet.
SameDomainOnly, RespectRobots bool? true, -- Auf der Ausgangsdomain bleiben; robots.txt während der Ermittlung berücksichtigen.

DiscoverResult meldet Total, Urls, PagesCrawled, Truncated, RobotsRespected, eine Zählung pro Quelle in Sources sowie Warnings.

Lookup#

Führe eine kategorisierte Websuche aus und rendere die besten Treffer auf Wunsch im selben Aufruf.

var search = await client.V2.LookupAsync("best static site generators", new LookupOptions
{
    Category = "web",
    NumResults = 10,
    TimeFilter = "month",
    PerceiveTop = 3,
});

foreach (var hit in search.Results)
{
    Console.WriteLine($"{hit.Position}. {hit.Title} {hit.Url}");
    if (hit.Perceive is { } page)
        Console.WriteLine($"   quality {page.RenderQuality}, markdown {page.Outputs["markdown"].Url}");
}
Option Typ Standard Beschreibung
Category string? "web" "web", "news", "images", "scholar", "patents", "maps".
Country, Locale string? -- Google-gl-Ländercode ("us", "in") und hl-Oberflächensprache ("en").
TimeFilter string? -- "hour", "day", "week", "month", "year".
NumResults, Page int? 10, 1 1 bis 100 Ergebnisse; Seite 1 bis 10.
Location, Autocorrect string?, bool? --, true Freitext-Ort wie "Austin, Texas"; offensichtliche Tippfehler vom Anbieter korrigieren lassen.
PerceiveTop int? 0 0 bis 10. Perceive die URLs der ersten N Treffer automatisch mit vollem Browser-Rendering.

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

Distill#

Schemagesteuerte strukturierte Extraktion. Ein kostenloser CSS-Durchlauf läuft zuerst, wenn du einen angibst, und nur die Felder, die er verfehlt, steigen in die LLM-Stufe auf.

using System.Text.Json.Nodes;

var extraction = await client.V2.DistillAsync(new DistillOptions
{
    Urls = new[] { "https://example.com/pricing" },
    Schema = new JsonObject { ["plans"] = "list of plan names with monthly prices" },
    CssSchema = new CssSchema
    {
        BaseSelector = ".plan-card",
        Fields = new[]
        {
            new CssField { Name = "name", Type = "text", Selector = "h3" },
            new CssField { Name = "price", Type = "text", Selector = ".price" },
        },
    },
});

var first = extraction.Results[0];
Console.WriteLine($"{first.Data} via {first.ExtractionTier}");
Console.WriteLine($"css fields {first.FieldsFromCss}, llm fields {first.FieldsFromLlm}");

// Oder ermittle zuerst eine Website und destilliere dann jede gefundene Seite.
await client.V2.DistillAsync(new DistillOptions
{
    DiscoverFrom = new DistillDiscoverFrom { Url = "https://example.com", Mode = "sitemap", MaxPages = 10 },
    Schema = new JsonObject { ["title"] = "page title" },
});
Option Typ Standard Beschreibung
Schema JsonObject erforderlich Ein JSON-Schema-Objekt oder eine flache {field: description}-Map. Das Data der Antwort entspricht dieser Struktur.
Urls IReadOnlyList<string>? -- Explizite URLs, maximal 50. Genau eines von Urls oder DiscoverFrom.
DiscoverFrom DistillDiscoverFrom? -- Url, Mode ("sitemap", "crawl", "hybrid") und MaxPages 1 bis 50, Standard 10.
CssSchema CssSchema? -- BaseSelector plus Fields, mit optionalem Name und TargetField.
WaitFor, WaitTimeoutMs string?, int? --, 30000 Wartebedingung vor der Extraktion.
Headers, Cookies, RespectRobots -- -- Gleiche Strukturen wie bei den Render-Optionen oben.

CssField.Type ist eines von text, attribute, html, regex, nested, list oder nested_list, mit einer Verschachtelung von bis zu fünf Ebenen. Urls und DiscoverFrom gemeinsam oder keines von beiden zu übergeben, wirft eine ArgumentException, bevor eine Anfrage rausgeht.

Ingest#

Verwandle eine ganze Website oder einen Stapel hochgeladener Dokumente in gechunktes JSONL, das ein Vektorspeicher lesen kann. Ingest arbeitet immer asynchron.

// Von einer Website.
var job = await client.V2.IngestAsync(new IngestOptions
{
    Mode = "sitemap",
    Url = "https://docs.example.com",
    MaxPages = 100,
    Chunk = new IngestChunkOptions { MaxWords = 512 },
    WebhookUrl = "https://my.app/hooks/enconvert",
});

// Aus hochgeladenen Dateien: PDF, DOCX, PPTX, XLSX, CSV, HTML, EPUB, TXT und MD, ältere und ODF-Office.
var fileJob = await client.V2.IngestFilesAsync(
    new[] { new FileInput(await File.ReadAllBytesAsync("handbook.pdf"), "handbook.pdf") },
    new IngestFilesOptions { Chunk = new IngestChunkOptions { MaxWords = 512 } });

// Abfragen, bis das JSONL bereit ist.
var status = await client.V2.GetIngestJobAsync(job.JobId);
Console.WriteLine($"{status.Status}: {status.PagesProcessed}/{status.PagesDiscovered} pages, {status.TotalChunks} chunks");
if (status.Status == "completed") Console.WriteLine(status.OutputUrl);

var recent = await client.V2.ListIngestJobsAsync(new V2ListOptions { Limit = 20 });
await client.V2.CancelIngestJobAsync(job.JobId); // idempotent
Option Typ Standard Beschreibung
Mode string? "urls" "urls", "sitemap" oder "crawl".
Url string? -- Start-URL. Erforderlich für sitemap und crawl, abgelehnt bei urls.
Urls IReadOnlyList<string>? -- Explizite URLs, maximal 1000. Erforderlich für urls, sonst abgelehnt.
MaxPages, MaxDepth, SameDomainOnly int?, int?, bool? 50, 2, true Ermittlungsgrenze 1 bis 1000 für sitemap und crawl, Crawl-Tiefe 1 bis 5, auf der Ausgangsdomain bleiben.
IncludePatterns, ExcludePatterns IReadOnlyList<string>? -- Regex-Positivliste und Sperrliste.
RespectRobots, WaitFor, WaitTimeoutMs -- -- Render-Steuerung pro Seite.
Chunk IngestChunkOptions? -- MaxWords 32 bis 4000, Standard 512. SentenceOverlap 0 bis 10, Standard 1.
WebhookUrl string? -- Abschluss-Webhook, HMAC-signiert.

Die Modusregeln werden clientseitig erzwungen, ein urls-Job, der zusätzlich Url setzt, wirft also sofort eine ArgumentException. Ein Job durchläuft queued, discovering, processing und dann completed, failed oder canceled. Die Webhook-Zustellung ist Ende zu Ende überprüfbar:

var secret = await client.V2.GetWebhookSecretAsync();
Console.WriteLine($"{secret.SignatureHeader} using {secret.SignatureScheme}, replay tolerance {secret.ReplayToleranceSeconds}s");

await client.V2.RotateWebhookSecretAsync();  // alte Signaturen verifizieren nicht mehr
var retry = await client.V2.RetryIngestWebhookAsync(job.JobId);
Console.WriteLine($"delivered: {retry.Delivered} after {retry.Attempts} attempts");

Watch#

Rendere eine Seite nach Zeitplan erneut und lass dich benachrichtigen, wenn sie sich ändert.

var watcher = await client.V2.CreateWatcherAsync("https://example.com/pricing", new WatchCreateOptions
{
    FrequencyMinutes = 60,
    DiffMode = "auto",
    WebhookUrl = "https://my.app/hooks/changes",
});

var page = await client.V2.ListWatchersAsync(new V2ListOptions { Limit = 20 });
var one = await client.V2.GetWatcherAsync(watcher.WatcherId);
var history = await client.V2.GetWatcherSnapshotsAsync(watcher.WatcherId, new SnapshotListOptions { Limit = 10 });
foreach (var snap in history.Snapshots)
    Console.WriteLine($"{snap.CheckedAt} changed: {snap.HasChanges} similarity: {snap.Similarity}");

await client.V2.UpdateWatcherAsync(watcher.WatcherId, new WatcherUpdate { Status = "paused" });
await client.V2.UpdateWatcherAsync(watcher.WatcherId, new WatcherUpdate { WebhookUrl = "" }); // löscht ihn
await client.V2.DeleteWatcherAsync(watcher.WatcherId); // Soft Delete, idempotent
Option Typ Standard Beschreibung
FrequencyMinutes int? 60 60 bis 43200. Die Untergrenze von einer Stunde ist fest.
DiffMode string? "auto" "auto", "text", "structured", "tables", "metadata".
TrackFields JsonObject? -- Feld- oder Selektor-Teilmenge für die Diff-Engine.
WebhookUrl string? -- Änderungs-Webhook, HMAC-signiert. Setze ihn in einem Update auf "", um ihn zu löschen.
NotifyEmail bool? true Benachrichtigt den Projektinhaber per E-Mail über Änderungen.

WatcherUpdate akzeptiert außerdem Status ("active" oder "paused") und wirft eine ArgumentException, wenn du ein Update ohne gesetzte Felder übergibst.

Snapshot-Diffs enthalten nicht vertrauenswürdige Seiteninhalte. WatcherSnapshot.Changes ist rohes JSON aus der überwachten Seite. Escape es, bevor du es in einem Dashboard oder einer E-Mail darstellst.

PDF-Optionen#

PdfOptions wird von ConvertUrlToPdfAsync, ConvertDocumentAsync, ConvertToPdfAsync (nur Graustufen) und PerceiveOptions gemeinsam genutzt, wenn pdf unter den Ausgaben ist.

await client.ConvertUrlToPdfAsync("https://internal.example.com/report", new UrlToPdfOptions
{
    PdfOptions = new PdfOptions
    {
        PageSize = "A4",
        Orientation = "landscape",
        Margins = new PdfMargins { Top = 10, Bottom = 10, Left = 15, Right = 15 },
        Scale = 0.9,
        Header = new PdfHeaderFooter { Content = "Quarterly Report", Height = 15 },
        Footer = new PdfHeaderFooter { Content = "Confidential", Height = 12 },
    },
    Auth = new HttpBasicAuth { Username = "user", Password = "pass" },
    SaveTo = "report.pdf",
});
Feld Typ Beschreibung
PageSize string? "A4", "A3", "Letter", "Legal" und Verwandte.
PageWidth, PageHeight double? Eigene Maße. Gemeinsam gesetzt überschreiben sie PageSize.
Orientation string? "portrait" oder "landscape".
Margins PdfMargins? Top, Bottom, Left, Right, alle optional.
Scale double? Render-Skalierung, zum Beispiel 0.9 für 90 Prozent.
Grayscale bool? Wandelt das PDF nachträglich in Graustufen um.
Header, Footer PdfHeaderFooter? Content bis zu 2000 Zeichen, plus Height.

Fehlerbehandlung#

Jeder Fehler erscheint als typisierte Exception, catch-Blöcke laufen also vom Spezifischsten zum Allgemeinsten.

using Enconvert;

try
{
    await client.V2.PerceiveAsync("https://example.com");
}
catch (AuthenticationException) { Console.Error.WriteLine("Invalid or missing API key."); }
catch (QuotaException) { Console.Error.WriteLine("Request rejected with 402."); }
catch (RateLimitException) { Console.Error.WriteLine("Too many requests, back off and retry."); }
catch (ApiException e) { Console.Error.WriteLine($"API error [{e.StatusCode}]: {e.Message}"); }
Klasse Ausgelöst bei Statuscode
AuthenticationException Ungültiger, fehlender oder widerrufener Key 401, 403 (gemeldet als 401)
QuotaException Ausgelöst bei HTTP 402 402
RateLimitException Rate-Limit überschritten 429
ApiException Jede andere 4xx oder 5xx der tatsächliche Code
EnconvertException Basisklasse für alle obigen --

Die Hierarchie verläuft von EnconvertException über ApiException (das StatusCode trägt) zu den drei spezifischen Klassen, ein einzelnes catch (EnconvertException) fängt also alles ab, was das SDK auslöst. Meldungen werden aus dem Feld detail oder error des Response-Body übernommen, sofern vorhanden. Validierung, die das SDK lokal durchführt, etwa ein nicht unterstütztes Konvertierungspaar oder eine fehlerhafte distill-Anfrage, wirft stattdessen eine ArgumentException und erreicht nie das Netzwerk. Die Antwortcodes sind unter Fehlercodes katalogisiert.

Timeout-Recovery#

Lange URL-Renders und große Dokumentkonvertierungen können ein Reverse-Proxy-Timeout von 60 bis 120 Sekunden überdauern, selbst wenn die Konvertierung selbst gelingt. Der Client fängt das für dich auf:

  1. Vor jeder Konvertierung einer einzelnen Datei oder einer einzelnen URL erzeugt das SDK eine Job-ID und sendet sie mit der Anfrage.
  2. Kommt diese Anfrage mit 5xx zurück, wechselt das SDK dazu, GET /v1/convert/status/{jobId} alle 3 Sekunden abzufragen, statt zu scheitern.
  3. Ein als success erfasster Job gibt das normale ConversionResult zurück. Ein als failed erfasster Job wirft eine ApiException mit Status 500 und der Fehlermeldung des Servers.
  4. Die Polling-Frist beträgt 5 Minuten, danach wirft das SDK ApiException(504, "Conversion timed out").

Das gilt für ConvertUrlToPdfAsync, ConvertUrlToScreenshotAsync, ConvertUrlToMarkdownAsync und alle vier Datei-Upload-Methoden. Website-Batch-Übermittlungen nehmen bewusst nicht teil, weil eine fehlgeschlagene Übermittlung keine Job-Zeile zum Abfragen hat und sofort gemeldet werden muss. Wenn eine Antwort job_id weglässt, trägt das SDK die selbst erzeugte ID nach, ConversionResult.JobId ist also immer mit GetJobStatusAsync verwendbar.

Konfiguration#

using var client = new EnconvertClient(
    apiKey: Environment.GetEnvironmentVariable("ENCONVERT_API_KEY")!,
    baseUrl: null,       // Standard ist https://api.enconvert.com
    timeoutMs: 300_000);
Parameter Typ Standard Beschreibung
apiKey string erforderlich Privater API-Key. Ein leerer Wert wirft eine ArgumentException.
baseUrl string? https://api.enconvert.com Überschreibt die Basis-URL der API. Abschließende Schrägstriche werden entfernt.
timeoutMs int 300_000 Anfrage-Timeout in Millisekunden, standardmäßig 5 Minuten.

Der Key reist bei jeder Anfrage in einem X-API-Key-Header. Artefakt-Downloads gehen direkt an signierte Speicher-URLs über einen zweiten HttpClient, der keinen Key sendet und kein Timeout anwendet, ein großes ZIP kann also so lange streamen, wie es braucht. Entsorge den Client einmal am Ende deines Prozesses oder registriere ihn als Singleton, statt pro Anfrage einen neuen zu bauen.

Schreibe den Key niemals fest in den Code. Lies ihn aus einer Umgebungsvariablen, aus User Secrets oder aus deinem Secret-Manager. Wer deinen privaten Key hat, kann Konvertierungen in deinem Projekt ausführen.

Ergebnisform#

Jede Konvertierungsmethode gibt ein ConversionResult zurück:

public sealed record ConversionResult
{
    public required string PresignedUrl { get; init; }  // signierte Download-URL
    public required string ObjectKey { get; init; }     // Objekt-Key im Speicher
    public required string Filename { get; init; }      // serverseitiger Dateiname
    public int? FileSize { get; init; }                 // Bytes
    public double? ConversionTimeSeconds { get; init; }
    public string? JobId { get; init; }                 // mit GetJobStatusAsync verwendbar
}

Asynchrone Arbeit gibt eigene Records zurück: JobStatus (Status, PresignedUrl, ObjectKey, Error), BatchSubmission (BatchId, Status, UrlCount, TotalDiscovered, DiscoveryMethod, OutputFormat) und BatchStatus (Summenzähler, OutputMode, ZipDownloadUrl und die Items pro URL).

V2-Artefakte kommen als V2OutputArtifact-Werte an, nach Ausgabenamen indexiert, jeweils mit Url (15 Minuten lang signiert), ObjectKey, SizeBytes, ContentType und ExpiresIn (standardmäßig 900 Sekunden). Signierte URLs laufen ab: Lade für dauerhaften Zugriff die Bytes herunter (übergib SaveTo, nutze PerceiveDirectAsync oder rufe die URL selbst ab) und lege sie in deinem eigenen Bucket ab. Ein erneuter Aufruf von GetPerceiveOperationAsync signiert die Artefakt-URLs einer Operation neu, solange sie noch innerhalb ihrer Aufbewahrungsfrist ist.

Quelle und Issues#


Häufig gestellte Fragen#

Wie konvertiere ich Dateien in C# mit einem NuGet-Paket?#

Führe dotnet add package Enconvert aus, erzeuge einen Client mit deinem Key (new EnconvertClient(apiKey)) und rufe eine asynchrone Methode wie ConvertUrlToPdfAsync, ConvertImageAsync oder ConvertDocumentAsync auf. Übergib SaveTo im Options-Record, um die Ausgabe direkt an einen lokalen Pfad zu streamen, oder lies result.PresignedUrl, um sie selbst herunterzuladen.

Wie konvertiere ich eine URL in .NET in ein PDF?#

Rufe await client.ConvertUrlToPdfAsync("https://example.com", new UrlToPdfOptions { SaveTo = "page.pdf" }) auf. Setze SinglePage = false, um zu paginieren, und übergib ein PdfOptions-Record für Seitengröße, Ausrichtung, Ränder, Skalierung, Graustufen, Kopf- und Fußzeile. Der Viewport ist standardmäßig 1920 x 1080.

Wie konvertiere ich DOCX in C# in ein PDF?#

Rufe ConvertDocumentAsync("report.docx", new ConvertDocumentOptions { SaveTo = "report.pdf" }) auf. PDF ist das Standard-Ausgabeformat, OutputFormat ist hier also optional. Dieselbe Methode verarbeitet XLSX, PPTX, ODF, Pages, Numbers, HTML, Markdown, CSV, JSON, XML, YAML und TOML als Eingabe.

Wie scrape ich eine Webseite mit dem C# SDK?#

Nutze den V2-Namensraum: await client.V2.PerceiveAsync(url, new PerceiveOptions { Outputs = new[] { "markdown", "structured" } }). Du bekommst Markdown, bereinigtes oder rohes HTML, Screenshots, PDF, Links, Bilder und strukturierte Extraktion, jeweils als signiertes Artefakt, plus einen RenderQuality-Wert für den Lesevorgang. Um die URLs einer Website zuerst ohne Rendering aufzuzählen, rufe DiscoverAsync auf.

Was bedeutet Render Quality und warum steht sie an jedem Lesevorgang?#

RenderQuality ist ein Ehrlichkeitswert von 0.0 bis 1.0, der an jedem V2-Lesevorgang hängt. Ein niedriger Wert bedeutet, dass die Seite nicht sauber gerendert wurde: eine Bot-Challenge, eine Cookie- oder Login-Wall, ein HTTP-Fehler oder eine leere Single-Page-App-Hülle. Der Inhalt kommt trotzdem zurück, zusammen mit einer Deductions-Map, die benennt, welche Prüfungen angeschlagen haben, und einer Warnings-Liste, sodass dein Agent einen schlechten Lesevorgang ablehnen kann, statt ihn als Tatsache zu behandeln.

Behandelt das SDK Konvertierungen, die das Proxy-Timeout überdauern?#

Ja. Es sendet mit jeder Konvertierung einer einzelnen Datei oder einer einzelnen URL eine erzeugte Job-ID, und wenn die Anfrage 5xx zurückgibt, fragt es GET /v1/convert/status/{jobId} alle 3 Sekunden für bis zu 5 Minuten ab. Erfolg gibt das normale Ergebnis zurück, ein erfasster Fehlschlag wirft eine ApiException, und das Überschreiten der Frist wirft ApiException(504, "Conversion timed out"). Website-Batch-Übermittlungen sind bewusst ausgenommen.

Kann ich dieses SDK aus Blazor WebAssembly oder einer Mobile-App nutzen?#

Nein. Der Client authentifiziert sich mit einem privaten API-Key, der niemals in Code ausgeliefert werden darf, den ein Nutzer lesen kann. Betreibe ihn aus ASP.NET Core, einem Worker Service, einer Azure Function oder einem beliebigen anderen serverseitigen .NET-8-Host, und lass dein Frontend stattdessen deinen eigenen Endpunkt aufrufen.

Welche Bildkonvertierungen werden unterstützt?#

Jedes Paar aus jpeg, png, svg, heic und webp, also 20 Kombinationen, plus die Rasterung von pdf zu jpeg. Das Eingabeformat ergibt sich aus der Dateiendung, und nicht unterstützte Paare werfen eine ArgumentException, bevor ein Netzwerkaufruf stattfindet. Prüfe die Tabelle selbst mit Formats.ValidOutputsFor("heic").

Wie verwandle ich eine Doku-Website in RAG-fertige Chunks?#

Rufe client.V2.IngestAsync(new IngestOptions { Mode = "sitemap", Url = "https://docs.example.com", MaxPages = 100 }) auf, frage dann GetIngestJobAsync ab, bis Status den Wert "completed" hat, und lies OutputUrl für das JSONL. Für lokale Dokumente schickt IngestFilesAsync Uploads durch denselben Chunker. Stimme Chunk.MaxWords und Chunk.SentenceOverlap auf dein Embedding-Modell ab.

Wie lange sind die Download-URLs gültig?#

V2-Artefakt-URLs sind 15 Minuten lang signiert (ExpiresIn beträgt 900 Sekunden) und werden bei jedem Abruf der Operation mit GetPerceiveOperationAsync neu signiert. Konvertierungsergebnisse liefern ebenfalls eine vorsignierte URL. In beiden Fällen gilt: Wenn die Datei die Signatur überdauern soll, lade sie herunter und lege sie in deinem eigenen Bucket ab.