SDK C# et .NET de conversion de fichiers#

Enconvert est le client C# et .NET officiel de l'API EnConvert, publié sur NuGet et ciblant .NET 8 ou plus récent. Il convertit des fichiers depuis C# : URL vers PDF, DOCX vers PDF, HEIC vers WebP, JSON vers YAML, n'importe quel document vers Markdown, et des sites web entiers en une seule archive ZIP. Le même client porte un espace de noms V2 pour le scraping et la web intelligence : une seule clé couvre donc perceive, discover, lookup, distill, ingest et watch. Chaque appel est asynchrone et annulable, chaque réponse est un record typé, et le package n'embarque aucune dépendance tierce.

NuGet : Enconvert · Source : conversionapi/csharp-sdk · Runtime : .NET 8+ · Dépendances : aucune au-delà de la BCL

Installation#

dotnet add package Enconvert

L'assembly cible net8.0 avec les types référence nullables activés, et repose uniquement sur System.Net.Http et System.Text.Json.

Démarrage rapide#

using Enconvert;

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

// Convertir une page en direct en PDF et l'écrire sur le disque.
var pdf = await client.ConvertUrlToPdfAsync("https://example.com", new UrlToPdfOptions { SaveTo = "page.pdf" });
Console.WriteLine(pdf.PresignedUrl);

// Lire une page comme votre agent devrait le faire, avec un score de qualité attaché.
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 implémente IDisposable : déclarez-le donc avec using ou enregistrez-le en singleton. Il s'authentifie avec une clé API privée, ce qui signifie qu'il a sa place sur le serveur : ne livrez jamais la clé dans un build desktop, mobile ou Blazor WebAssembly. La mise en place des clés est traitée dans Authentification.


Ce que le client expose#

Membre Endpoint Renvoie
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} (interrogé en boucle) BatchStatus
V2 la surface /v2/* EnconvertV2

client.V2 regroupe 23 méthodes réparties sur six capacités :

Capacité Méthodes Référence
Perceive PerceiveAsync, GetPerceiveOperationAsync, PerceiveBatchAsync, GetPerceiveBatchAsync, PerceiveDirectAsync, DownloadPerceiveArtifactAsync /fr/docs/v2-perceive
Discover DiscoverAsync /fr/docs/v2-discover
Lookup LookupAsync /fr/docs/v2-lookup
Distill DistillAsync /fr/docs/v2-distill
Ingest IngestAsync, IngestFilesAsync, ListIngestJobsAsync, GetIngestJobAsync, CancelIngestJobAsync, RetryIngestWebhookAsync, GetWebhookSecretAsync, RotateWebhookSecretAsync /fr/docs/v2-ingest
Watch CreateWatcherAsync, ListWatchersAsync, GetWatcherAsync, GetWatcherSnapshotsAsync, UpdateWatcherAsync, DeleteWatcherAsync /fr/docs/v2-watch

Chaque méthode prend un CancellationToken final facultatif. Les options sont des records à propriétés init : construisez-les donc avec un initialiseur d'objet et réutilisez-les avec with. Les clients pour les autres langages se trouvent dans l'index des SDK ; la surface REST brute est décrite dans la vue d'ensemble des endpoints.


Conversion de fichiers#

ConvertUrlToPdfAsync#

Rend en PDF n'importe quelle URL accessible.

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 Type Par défaut Description
SaveTo string? -- Chemin local vers lequel écrire le PDF. Les répertoires parents manquants sont créés.
SinglePage bool? true true produit une seule page continue. false pagine en utilisant PdfOptions.PageSize.
PdfOptions PdfOptions? -- Taille de page, orientation, marges, échelle, niveaux de gris, en-tête, pied de page. Voir Options PDF.
ViewportWidth, ViewportHeight int? 1920, 1080 Fenêtre d'affichage du navigateur, en pixels.
LoadMedia, EnableScroll bool? true Attend les images et les vidéos avant la capture ; fait défiler la page de haut en bas pour déclencher les chargements différés.
OutputFilename string? auto Remplace le nom de fichier généré.
Auth HttpBasicAuth? -- Identifiants HTTP Basic pour une page derrière une authentification.
Cookies, Headers IReadOnlyList<BrowserCookie>?, IReadOnlyDictionary<string, string>? -- Jusqu'à 50 cookies injectés avant le rendu, et jusqu'à 20 en-têtes de requête supplémentaires. Les en-têtes hop-by-hop sont rejetés.
Ne combinez pas Auth avec votre propre en-tête Authorization. L'API rejette ce conflit plutôt que de deviner quel identifiant l'emporte. Choisissez-en un.

ConvertUrlToScreenshotAsync#

Capture un PNG de n'importe quelle URL.

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

UrlToScreenshotOptions accepte les mêmes options de fenêtre d'affichage, de médias, de défilement, de nom de fichier et d'accès que ConvertUrlToPdfAsync, à l'exception de SinglePage et PdfOptions.

ConvertUrlToMarkdownAsync#

Extrait du Markdown GitHub-Flavored propre à partir d'une URL. La navigation, les pieds de page, les publicités et les scripts sont supprimés, le corps principal de l'article est conservé, et un frontmatter YAML (title, description, url, links, images) est ajouté en tête.

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

UrlToMarkdownOptions partage les mêmes options de rendu et d'accès. Pour les lectures destinées à un agent qui ont aussi besoin d'un score de qualité et d'une extraction structurée, utilisez plutôt Perceive.

ConvertImageAsync#

Convertit entre jpeg, png, svg, heic et webp dans n'importe quel sens, et rastérise un PDF en JPEG.

// Depuis un chemin.
await client.ConvertImageAsync("photo.heic", new ConvertImageOptions { OutputFormat = "webp", SaveTo = "photo.webp" });

// Depuis des octets, avec un nom de fichier explicite pour que le format d'entrée soit détectable.
var bytes = await File.ReadAllBytesAsync("scan.pdf");
await client.ConvertImageAsync(new FileInput(bytes, "scan.pdf"), new ConvertImageOptions
{
    OutputFormat = "jpeg",
    SaveTo = "scan.jpeg",
});

Trois surcharges acceptent un chemin string, un byte[] brut, ou un record FileInput. Le format d'entrée vient de l'extension du nom de fichier : préférez donc FileInput quand vous détenez des octets, car la surcharge byte[] nue envoie upload.bin, ce qui ne fonctionne que sur les endpoints à détection automatique.

Option Type Requis Description
OutputFormat string Oui Format cible. jpg, yml, htm et md sont normalisés vers leurs noms canoniques.
SaveTo string? -- Chemin local vers lequel écrire le résultat.
OutputFilename string? -- Remplace le nom de fichier généré.

Les paires non prises en charge lèvent une ArgumentException avant qu'aucune requête HTTP ne soit émise : une faute de frappe ne coûte donc rien.

ConvertDocumentAsync#

Convertit des documents et des formats de données. OutputFormat vaut "pdf" par défaut.

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

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

// markdown vers pdf avec géométrie de page
await client.ConvertDocumentAsync("README.md", new ConvertDocumentOptions
{
    PdfOptions = new PdfOptions { PageSize = "A4", Margins = new PdfMargins { Top = 20, Bottom = 20 } },
    SaveTo = "readme.pdf",
});

Entrées prises en charge : .doc, .docx, .xls, .xlsx, .ppt, .pptx, .html, .htm, .odt, .ods, .odp, .ots, .pages, .numbers, .md, .markdown, .csv, .json, .xml, .yaml, .yml, .toml. L'EPUB n'a pas de paire de conversion documentaire dédiée : faites donc passer les .epub par ConvertToPdfAsync ou ConvertToMarkdownAsync.

Option Type Par défaut Description
OutputFormat string? "pdf" Format cible.
SaveTo string? -- Chemin local vers lequel écrire le résultat.
OutputFilename string? -- Remplace le nom de fichier généré.
PdfOptions PdfOptions? -- Mise en page. Utile uniquement lorsque la sortie est un PDF.

Comme ConvertImageAsync, cette méthode dispose de surcharges chemin, byte[] et FileInput.

ConvertToMarkdownAsync#

Envoyez presque n'importe quel document vers l'endpoint Markdown à détection automatique : PDF, DOCX, PPTX, XLSX, CSV, HTML, EPUB, TXT et MD, ainsi que les formats bureautiques hérités et ODF. Les images ne sont pas prises en charge ici.

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

Le format est détecté côté serveur : il n'y a donc aucune vérification d'extension côté client et aucune option PDF sur cet endpoint. ConvertToMarkdownOptions ne porte que SaveTo et OutputFilename. La sortie est un unique fichier .md qui respecte la hiérarchie des titres, ce qui en fait une solide première étape dans un pipeline RAG : un découpeur sémantique peut segmenter sur la hiérarchie de titres du document plutôt que sur un nombre de caractères arbitraire.

ConvertToPdfAsync#

L'autre endpoint à détection automatique : bureautique, ODF, Pages, Numbers, RTF, CSV, HTML, Markdown, texte brut, images matricielles, SVG, EPUB, ou un PDF existant transmis tel quel pour normalisation.

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

// PDF transmis tel quel, converti en niveaux de gris.
await client.ConvertToPdfAsync("scan.pdf", new ConvertToPdfOptions
{
    PdfOptions = new PdfOptions { Grayscale = true },
    SaveTo = "gray.pdf",
});
Seul Grayscale est pris en compte ici. L'endpoint anything-to-pdf ignore le reste de PdfOptions. Lorsque vous avez besoin d'une taille de page, d'une orientation, de marges ou d'un en-tête et d'un pied de page, faites passer le HTML et le Markdown par ConvertDocumentAsync, ou rendez la page avec ConvertUrlToPdfAsync.

ConvertToPdfOptions porte SaveTo, OutputFilename et PdfOptions, et dispose des trois mêmes surcharges d'entrée que les méthodes ci-dessus.

Lots sur un site entier#

ConvertWebsiteToPdfAsync et ConvertWebsiteToScreenshotAsync découvrent chaque page d'un site, convertissent chacune d'elles en arrière-plan, et regroupent les résultats dans une seule archive ZIP. Les deux sont uniquement asynchrones et exigent une clé API privée.

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

// Bloque jusqu'à ce que le ZIP soit prêt, puis l'enregistre.
var status = await client.WaitForBatchAsync(batch.BatchId, new WaitForBatchOptions { SaveTo = "site.zip" });
Console.WriteLine($"{status.Completed} of {status.Total} converted, {status.Failed} failed");

Pour interroger à votre propre rythme, appelez plutôt GetBatchStatusAsync(batchId) et lisez ZipDownloadUrl une fois que Status a quitté l'état "processing".

Option Type Par défaut Description
CrawlMode string? "auto" "auto", "sitemap" (sitemap.xml uniquement), ou "full" (sitemap plus un crawl en largeur).
IncludePatterns, ExcludePatterns IReadOnlyList<string>? -- Ne crawler, ou au contraire ignorer, que les URL correspondant à ces motifs. Mode full crawl.
NotificationEmail, CallbackUrl string? propriétaire du projet, -- Adresse prévenue à la fin du lot, et webhook appelé en POST à la fin du traitement.
SinglePage, PdfOptions bool?, PdfOptions? -- Lots PDF uniquement. Les options de fenêtre d'affichage, de médias, de défilement et d'accès sont partagées avec les méthodes portant sur une URL unique.

WaitForBatchOptions prend IntervalMs (5_000 par défaut), TimeoutMs (1_800_000 par défaut, soit 30 minutes) et SaveTo. Dépasser le délai lève une ApiException avec le statut 504.

GetJobStatusAsync#

Interroge un job de conversion unique par son identifiant.

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);
Vous en avez rarement besoin directement. Le client interroge déjà le statut du job pour vous quand une requête synchrone meurt au niveau du proxy. Voir Récupération des timeouts.

Paires de conversion prises en charge#

Le SDK reflète la carte des convertisseurs de la passerelle et implémente 43 paires {input}-to-{output} typées.

Entrée Sorties
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 les uns vers les autres, les 20 paires
pdf jpeg

La classe statique Formats expose le même tableau, ce qui vous permet de valider avant de construire une interface ou une file de jobs :

Formats.ValidOutputsFor("json");        // ["csv", "toml", "xml", "yaml"]
Formats.ValidOutputsFor("pdf");         // ["jpeg"]
Formats.ImplementedConversions;         // l'ensemble complet des 43 noms d'endpoints
Formats.NormalizeOutputFormat(".JPG");  // "jpeg"

Tout ce qui sort de ce tableau passe par ConvertToPdfAsync ou ConvertToMarkdownAsync, qui détectent le format côté serveur. La référence complète des paramètres se trouve dans Paramètres et options.


Web intelligence (V2)#

Chaque lecture V2 porte RenderQuality, un score de 0.0 à 1.0 qui indique avec quelle honnêteté la page s'est rendue. Un challenge anti-bot, un mur de cookies ou de connexion, une erreur HTTP ou une coquille d'application monopage vide reviennent avec un score bas, une carte Deductions renseignée nommant les contrôles déclenchés, et une liste Warnings, le contenu restant tout de même renvoyé. C'est tout l'intérêt : une mauvaise lecture est signalée au lieu d'entrer silencieusement dans le contexte de votre agent. Le même score apparaît sur les résultats de perceive, les éléments de distill, les résultats de lookup perçus automatiquement, et les snapshots de watchers. Les concepts sont traités dans la vue d'ensemble V2. Les endpoints V2 exigent une clé API privée ; les clés publiques sont rejetées.

Perceive#

Rend une URL vers des artefacts prêts pour un agent.

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);            // de 0.0 à 1.0
Console.WriteLine(op.StatusCode);               // statut HTTP amont
Console.WriteLine(op.Outputs["markdown"].Url);  // signée pour 15 minutes
Console.WriteLine(op.Structured);               // JsonObject, la forme est la vôtre

foreach (var (check, penalty) in op.Deductions) Console.WriteLine($"deduction {check}: {penalty}");
Option Type Par défaut Description
Outputs IReadOnlyList<string>? ["markdown", "structured"] Une valeur parmi markdown, html_cleaned, html_raw, screenshot, screenshot_full_page, pdf, links, images, structured.
Extract IReadOnlyList<string>? -- Cibles heuristiques : tables, prices, contacts, metadata, main_content, headings, structured_data, technologies, all.
Schema JsonObject? -- Schéma JSON pilotant l'extraction structurée de niveau LLM.
WaitFor, WaitTimeoutMs string?, int? --, 30000 Un sélecteur CSS (éventuellement css:...) ou js:<expr> à attendre avant la capture, borné de 0 à 60000 ms.
JsCode string? -- JavaScript exécuté après la navigation, 20000 caractères maximum.
Viewport PerceiveViewport? 1920 x 1080 Width de 320 à 3840, Height de 240 à 2160.
Headers, Cookies, Auth voir ci-dessus -- En-têtes supplémentaires, cookies injectés, identifiants HTTP Basic.
CacheMode string? "enabled" "enabled" réutilise un cache d'une heure, "bypass" le contourne, "refresh" refait le rendu.
PdfOptions PdfOptions? -- Utile uniquement lorsque Outputs contient pdf.
BlockResources IReadOnlyList<string>? -- Types de ressources à ignorer : image, media, font, stylesheet, script.
RespectRobots, Mobile bool? -- Respecte robots.txt ; émule un appareil mobile.
OnlyMainContent bool? true Supprime la navigation, l'en-tête, le pied de page et les bandeaux cookies de l'artefact markdown et de l'extrait main_content.
DirectDownload bool? -- Renvoie les octets de l'artefact au lieu de l'enveloppe JSON. Préférez PerceiveDirectAsync.

ProxyUrl, Geolocation et ActionChain existent sur PerceiveOptions mais ne sont pas encore disponibles côté serveur et reviennent pour l'instant en 422.

Resignez les URL d'artefacts plus tard, traitez jusqu'à 1000 URL par lot, ou récupérez directement les octets sans l'aller-retour par URL signée :

// Les URL d'artefacts sont resignées à chaque récupération de l'opération.
var again = await client.V2.GetPerceiveOperationAsync(op.OperationId);

// Lots : les petits s'exécutent en ligne, les plus gros renvoient "queued" et vous les interrogez.
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}");

// Téléchargement direct : exactement une sortie produisant un artefact est requise.
var direct = await client.V2.PerceiveDirectAsync("https://example.com", new PerceiveOptions { Outputs = new[] { "pdf" } });
await File.WriteAllBytesAsync(direct.Filename ?? "page.pdf", direct.Content);

// Retélécharger un artefact stocké d'une opération antérieure.
var artifact = await client.V2.DownloadPerceiveArtifactAsync(op.OperationId, "markdown");

PerceiveDirectAsync lève localement une ArgumentException sauf si exactement une sortie parmi markdown, html_cleaned, html_raw, screenshot, screenshot_full_page, pdf, links ou images est demandée, puisque structured est du JSON en ligne et non un fichier stocké. Les deux méthodes directes renvoient un PerceiveDirectResult portant Content, ContentType, Filename, OperationId, ObjectKey, CacheHit, RenderQuality, SourceStatusCode, ContentHash et WarningsCount. DownloadPerceiveArtifactAsync ne prend le nom de sortie que lorsque l'opération a produit plus d'un artefact, et renvoie 410 une fois l'artefact sorti de sa fenêtre de rétention.

Discover#

Énumère les URL d'un site sans aucun rendu navigateur, ce qui la rend bien moins coûteuse qu'un 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 Type Par défaut Description
Mode string? "hybrid" "sitemap", "crawl", ou "hybrid" (sitemap plus crawl HTTP).
MaxUrls int? 100 De 1 à 1000.
MaxDepth int? 2 Profondeur de crawl, de 1 à 5.
IncludePatterns, ExcludePatterns IReadOnlyList<string>? -- Liste blanche et liste noire d'expressions régulières, 50 entrées maximum chacune. La liste noire est appliquée en second.
SameDomainOnly, RespectRobots bool? true, -- Rester sur le domaine de départ ; respecter robots.txt pendant la découverte.

DiscoverResult rapporte Total, Urls, PagesCrawled, Truncated, RobotsRespected, une carte de comptage par source dans Sources, et Warnings.

Lookup#

Lance une recherche web catégorisée et, si vous le souhaitez, rend les meilleurs résultats dans le même appel.

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 Type Par défaut Description
Category string? "web" "web", "news", "images", "scholar", "patents", "maps".
Country, Locale string? -- Code pays Google gl ("us", "in") et langue d'interface hl ("en").
TimeFilter string? -- "hour", "day", "week", "month", "year".
NumResults, Page int? 10, 1 De 1 à 100 résultats ; page de 1 à 10.
Location, Autocorrect string?, bool? --, true Localisation en texte libre comme "Austin, Texas" ; laisser le fournisseur corriger les fautes de frappe évidentes.
PerceiveTop int? 0 De 0 à 10. Perçoit automatiquement les N premières URL de résultats avec un rendu navigateur complet.

LookupResult porte également AnswerBox, KnowledgeGraph, PerceiveOperationIds et Warnings.

Distill#

Extraction structurée pilotée par schéma. Une passe CSS gratuite s'exécute en premier lorsque vous en fournissez une, et seuls les champs qu'elle manque escaladent vers le niveau LLM.

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

// Ou découvrez d'abord un site, puis distillez chaque page trouvée.
await client.V2.DistillAsync(new DistillOptions
{
    DiscoverFrom = new DistillDiscoverFrom { Url = "https://example.com", Mode = "sitemap", MaxPages = 10 },
    Schema = new JsonObject { ["title"] = "page title" },
});
Option Type Par défaut Description
Schema JsonObject obligatoire Un objet JSON-Schema ou une carte plate {field: description}. Le Data de la réponse suit cette forme.
Urls IReadOnlyList<string>? -- URL explicites, 50 maximum. Exactement l'un de Urls ou DiscoverFrom.
DiscoverFrom DistillDiscoverFrom? -- Url, Mode ("sitemap", "crawl", "hybrid"), et MaxPages de 1 à 50, 10 par défaut.
CssSchema CssSchema? -- BaseSelector plus Fields, avec Name et TargetField facultatifs.
WaitFor, WaitTimeoutMs string?, int? --, 30000 Condition d'attente avant l'extraction.
Headers, Cookies, RespectRobots -- -- Mêmes formes que les options de rendu ci-dessus.

CssField.Type vaut text, attribute, html, regex, nested, list ou nested_list, avec une imbrication jusqu'à cinq niveaux. Passer à la fois Urls et DiscoverFrom, ou aucun des deux, lève une ArgumentException avant qu'aucune requête ne parte.

Ingest#

Transforme un site entier, ou une pile de documents envoyés, en JSONL découpé qu'un magasin de vecteurs peut lire. Ingest est toujours asynchrone.

// Depuis un site.
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",
});

// Depuis des fichiers envoyés : PDF, DOCX, PPTX, XLSX, CSV, HTML, EPUB, TXT et MD, bureautique hérité et ODF.
var fileJob = await client.V2.IngestFilesAsync(
    new[] { new FileInput(await File.ReadAllBytesAsync("handbook.pdf"), "handbook.pdf") },
    new IngestFilesOptions { Chunk = new IngestChunkOptions { MaxWords = 512 } });

// Interroger jusqu'à ce que le JSONL soit prêt.
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 Type Par défaut Description
Mode string? "urls" "urls", "sitemap", ou "crawl".
Url string? -- URL de départ. Obligatoire pour sitemap et crawl, rejetée pour urls.
Urls IReadOnlyList<string>? -- URL explicites, 1000 maximum. Obligatoire pour urls, rejetée sinon.
MaxPages, MaxDepth, SameDomainOnly int?, int?, bool? 50, 2, true Plafond de découverte de 1 à 1000 pour sitemap et crawl, profondeur de crawl de 1 à 5, rester sur le domaine de départ.
IncludePatterns, ExcludePatterns IReadOnlyList<string>? -- Liste blanche et liste noire d'expressions régulières.
RespectRobots, WaitFor, WaitTimeoutMs -- -- Contrôles de rendu par page.
Chunk IngestChunkOptions? -- MaxWords de 32 à 4000, 512 par défaut. SentenceOverlap de 0 à 10, 1 par défaut.
WebhookUrl string? -- Webhook de fin de traitement, signé en HMAC.

Les règles de mode sont appliquées côté client : un job urls qui définit aussi Url lève donc immédiatement une ArgumentException. Un job traverse les états queued, discovering, processing, puis completed, failed ou canceled. La livraison des webhooks est vérifiable de bout en bout :

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

await client.V2.RotateWebhookSecretAsync();  // les anciennes signatures cessent d'être valides
var retry = await client.V2.RetryIngestWebhookAsync(job.JobId);
Console.WriteLine($"delivered: {retry.Delivered} after {retry.Attempts} attempts");

Watch#

Refait le rendu d'une page selon une planification et vous prévient quand elle change.

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'efface
await client.V2.DeleteWatcherAsync(watcher.WatcherId); // suppression logique, idempotente
Option Type Par défaut Description
FrequencyMinutes int? 60 De 60 à 43200. Le plancher horaire est strict.
DiffMode string? "auto" "auto", "text", "structured", "tables", "metadata".
TrackFields JsonObject? -- Sous-ensemble de champs ou de sélecteurs pour le moteur de diff.
WebhookUrl string? -- Webhook de changement, signé en HMAC. Définissez-le à "" dans une mise à jour pour l'effacer.
NotifyEmail bool? true Prévient le propriétaire du projet par e-mail en cas de changement.

WatcherUpdate accepte aussi Status ("active" ou "paused") et lève une ArgumentException si vous passez une mise à jour sans aucun champ défini.

Les diffs de snapshots contiennent du contenu de page non fiable. WatcherSnapshot.Changes est du JSON brut repris de la page surveillée. Échappez-le avant de l'afficher dans un tableau de bord ou un e-mail.

Options PDF#

PdfOptions est partagé par ConvertUrlToPdfAsync, ConvertDocumentAsync, ConvertToPdfAsync (niveaux de gris uniquement), et PerceiveOptions lorsque pdf figure parmi les sorties.

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",
});
Champ Type Description
PageSize string? "A4", "A3", "Letter", "Legal", et compagnie.
PageWidth, PageHeight double? Dimensions personnalisées. Définies ensemble, elles remplacent PageSize.
Orientation string? "portrait" ou "landscape".
Margins PdfMargins? Top, Bottom, Left, Right, tous facultatifs.
Scale double? Échelle de rendu, par exemple 0.9 pour 90 pour cent.
Grayscale bool? Post-traite le PDF en niveaux de gris.
Header, Footer PdfHeaderFooter? Content jusqu'à 2000 caractères, plus Height.

Gestion des erreurs#

Chaque échec remonte sous forme d'exception typée : les blocs catch se lisent donc du plus spécifique au moins spécifique.

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}"); }
Classe Déclenchée sur Code de statut
AuthenticationException Clé invalide, manquante ou révoquée 401, 403 (rapporté comme 401)
QuotaException Déclenchée sur un HTTP 402 402
RateLimitException Limite de débit dépassée 429
ApiException Toute autre 4xx ou 5xx le code réel
EnconvertException Classe de base de toutes les précédentes --

La hiérarchie va d'EnconvertException à ApiException (qui porte StatusCode), puis aux trois classes spécifiques : un unique catch (EnconvertException) attrape donc tout ce que le SDK lève. Les messages sont extraits du champ detail ou error du corps de la réponse lorsqu'il est présent. La validation que le SDK effectue localement, comme une paire de conversion non prise en charge ou une requête distill malformée, lève plutôt une ArgumentException et n'atteint jamais le réseau. Les codes de réponse sont catalogués dans Codes d'erreur.

Récupération des timeouts#

Les rendus d'URL longs et les conversions de documents volumineux peuvent dépasser un timeout de reverse proxy de 60 à 120 secondes, même quand la conversion elle-même réussit. Le client absorbe cela pour vous :

  1. Avant chaque conversion de fichier unique ou d'URL unique, le SDK génère un identifiant de job et l'envoie avec la requête.
  2. Si cette requête revient en 5xx, le SDK bascule vers le polling de GET /v1/convert/status/{jobId} toutes les 3 secondes au lieu d'échouer.
  3. Un job enregistré comme success renvoie le ConversionResult habituel. Un job enregistré comme failed lève une ApiException avec le statut 500 et le message d'erreur du serveur.
  4. Le délai maximal de polling est de 5 minutes, au-delà duquel le SDK lève ApiException(504, "Conversion timed out").

Cela couvre ConvertUrlToPdfAsync, ConvertUrlToScreenshotAsync, ConvertUrlToMarkdownAsync et les quatre méthodes d'envoi de fichiers. Les soumissions de lots de site entier en sont volontairement exclues, parce qu'une soumission échouée n'a pas de ligne de job à interroger et doit remonter immédiatement. Quand une réponse omet job_id, le SDK y réinjecte l'identifiant qu'il a généré, si bien que ConversionResult.JobId reste toujours utilisable avec GetJobStatusAsync.

Configuration#

using var client = new EnconvertClient(
    apiKey: Environment.GetEnvironmentVariable("ENCONVERT_API_KEY")!,
    baseUrl: null,       // vaut https://api.enconvert.com par défaut
    timeoutMs: 300_000);
Paramètre Type Par défaut Description
apiKey string obligatoire Clé API privée. Une valeur vide lève une ArgumentException.
baseUrl string? https://api.enconvert.com Remplace l'URL de base de l'API. Les barres obliques finales sont supprimées.
timeoutMs int 300_000 Timeout de requête en millisecondes, 5 minutes par défaut.

La clé voyage dans un en-tête X-API-Key sur chaque requête. Les téléchargements d'artefacts vont directement vers des URL de stockage signées, via un second HttpClient qui n'envoie aucune clé et n'applique aucun timeout : un gros ZIP peut donc être diffusé aussi longtemps qu'il le faut. Libérez le client une seule fois à la fin de votre processus, ou enregistrez-le en singleton plutôt que d'en construire un par requête.

Ne codez jamais la clé en dur. Lisez-la depuis une variable d'environnement, les user secrets ou votre gestionnaire de secrets. Quiconque détient votre clé privée peut lancer des conversions sur votre projet.

Structure du résultat#

Chaque méthode de conversion renvoie un ConversionResult :

public sealed record ConversionResult
{
    public required string PresignedUrl { get; init; }  // URL de téléchargement signée
    public required string ObjectKey { get; init; }     // clé de l'objet de stockage
    public required string Filename { get; init; }      // nom de fichier côté serveur
    public int? FileSize { get; init; }                 // octets
    public double? ConversionTimeSeconds { get; init; }
    public string? JobId { get; init; }                 // utilisable avec GetJobStatusAsync
}

Les traitements asynchrones renvoient leurs propres records : JobStatus (Status, PresignedUrl, ObjectKey, Error), BatchSubmission (BatchId, Status, UrlCount, TotalDiscovered, DiscoveryMethod, OutputFormat), et BatchStatus (compteurs agrégés, OutputMode, ZipDownloadUrl, et les Items par URL).

Les artefacts V2 arrivent sous forme de valeurs V2OutputArtifact indexées par nom de sortie, chacune avec Url (signée pour 15 minutes), ObjectKey, SizeBytes, ContentType et ExpiresIn (900 secondes par défaut). Les URL signées expirent : pour un accès permanent, téléchargez donc les octets (passez SaveTo, utilisez PerceiveDirectAsync, ou récupérez l'URL vous-même) et stockez-les dans votre propre bucket. Rappeler GetPerceiveOperationAsync resigne les URL d'artefacts d'une opération encore dans sa fenêtre de rétention.

Source et problèmes#


Questions fréquentes#

Comment convertir des fichiers en C# avec un package NuGet ?#

Exécutez dotnet add package Enconvert, créez un client avec votre clé (new EnconvertClient(apiKey)), et appelez une méthode asynchrone comme ConvertUrlToPdfAsync, ConvertImageAsync ou ConvertDocumentAsync. Passez SaveTo sur le record d'options pour écrire la sortie directement vers un chemin local, ou lisez result.PresignedUrl pour la télécharger vous-même.

Comment convertir une URL en PDF en .NET ?#

Appelez await client.ConvertUrlToPdfAsync("https://example.com", new UrlToPdfOptions { SaveTo = "page.pdf" }). Définissez SinglePage = false pour paginer, et passez un record PdfOptions pour la taille de page, l'orientation, les marges, l'échelle, les niveaux de gris, l'en-tête et le pied de page. La fenêtre d'affichage vaut 1920 x 1080 par défaut.

Comment convertir un DOCX en PDF en C# ?#

Appelez ConvertDocumentAsync("report.docx", new ConvertDocumentOptions { SaveTo = "report.pdf" }). Le PDF est le format de sortie par défaut, OutputFormat est donc facultatif ici. La même méthode gère les entrées XLSX, PPTX, ODF, Pages, Numbers, HTML, Markdown, CSV, JSON, XML, YAML et TOML.

Comment récupérer une page web avec le SDK C# ?#

Utilisez l'espace de noms V2 : await client.V2.PerceiveAsync(url, new PerceiveOptions { Outputs = new[] { "markdown", "structured" } }). Vous obtenez du Markdown, du HTML nettoyé ou brut, des captures d'écran, un PDF, des liens, des images et une extraction structurée, chacun sous forme d'artefact signé, plus un score RenderQuality pour la lecture. Pour énumérer d'abord les URL d'un site sans rien rendre, appelez DiscoverAsync.

Que signifie la qualité de rendu et pourquoi figure-t-elle sur chaque lecture ?#

RenderQuality est un score d'honnêteté de 0.0 à 1.0 attaché à chaque lecture V2. Un score bas signifie que la page ne s'est pas rendue proprement : challenge anti-bot, mur de cookies ou de connexion, erreur HTTP, ou coquille d'application monopage vide. Le contenu revient tout de même, accompagné d'une carte Deductions nommant les contrôles déclenchés et d'une liste Warnings, pour que votre agent puisse rejeter une mauvaise lecture au lieu de la prendre pour argent comptant.

Le SDK gère-t-il les conversions qui dépassent le timeout du proxy ?#

Oui. Il envoie un identifiant de job généré avec chaque conversion de fichier unique ou d'URL unique, et si la requête renvoie un 5xx il interroge GET /v1/convert/status/{jobId} toutes les 3 secondes pendant 5 minutes au maximum. Un succès renvoie le résultat habituel, un échec enregistré lève une ApiException, et dépasser le délai lève ApiException(504, "Conversion timed out"). Les soumissions de lots de site entier sont exclues volontairement.

Puis-je utiliser ce SDK depuis Blazor WebAssembly ou une application mobile ?#

Non. Le client s'authentifie avec une clé API privée qui ne doit jamais être livrée dans du code qu'un utilisateur peut lire. Exécutez-le depuis ASP.NET Core, un worker service, une Azure Function, ou n'importe quel autre hôte .NET 8 côté serveur, et faites appeler par votre front-end votre propre endpoint à la place.

Quelles conversions d'images sont prises en charge ?#

Toutes les paires parmi jpeg, png, svg, heic et webp, soit 20 combinaisons, plus la rastérisation pdf vers jpeg. Le format d'entrée vient de l'extension du nom de fichier, et les paires non prises en charge lèvent une ArgumentException avant tout appel réseau. Consultez la table vous-même avec Formats.ValidOutputsFor("heic").

Comment transformer un site de documentation en fragments prêts pour le RAG ?#

Appelez client.V2.IngestAsync(new IngestOptions { Mode = "sitemap", Url = "https://docs.example.com", MaxPages = 100 }), puis interrogez GetIngestJobAsync jusqu'à ce que Status vaille "completed" et lisez OutputUrl pour récupérer le JSONL. Pour des documents locaux, IngestFilesAsync fait passer les fichiers envoyés par le même découpeur. Ajustez Chunk.MaxWords et Chunk.SentenceOverlap pour correspondre à votre modèle d'embedding.

Combien de temps les URL de téléchargement sont-elles valides ?#

Les URL d'artefacts V2 sont signées pour 15 minutes (ExpiresIn vaut 900 secondes) et sont resignées chaque fois que vous récupérez l'opération avec GetPerceiveOperationAsync. Les résultats de conversion renvoient eux aussi une URL présignée. Dans les deux cas, si vous voulez que le fichier survive à la signature, téléchargez-le et stockez-le dans votre propre bucket.