Enconvert 0.1.1

dotnet add package Enconvert --version 0.1.1
                    
NuGet\Install-Package Enconvert -Version 0.1.1
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Enconvert" Version="0.1.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Enconvert" Version="0.1.1" />
                    
Directory.Packages.props
<PackageReference Include="Enconvert" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Enconvert --version 0.1.1
                    
#r "nuget: Enconvert, 0.1.1"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Enconvert@0.1.1
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Enconvert&version=0.1.1
                    
Install as a Cake Addin
#tool nuget:?package=Enconvert&version=0.1.1
                    
Install as a Cake Tool

Enconvert C# / .NET SDK

Honest eyes for your AI agent — the C# / .NET SDK for Enconvert. Targets .NET 8+.

Read any web page or file into clean Markdown, JSON, or screenshots, and get a render_quality score (0.0–1.0) on every read — so a blocked, challenge, or empty-SPA page comes back flagged with a low score and warnings, never mistaken for real content. Perceive, discover, look up, distill, ingest, and watch the web; convert 40+ file and document formats through the same key.

Wiring an agent (Claude, Cursor, Windsurf, n8n, …)? The MCP server is the native path — npx @enconvert/mcp setup. This SDK is the programmatic REST path for everything else.

Install

dotnet add package Enconvert

Quick Start

using Enconvert;

var client = new EnconvertClient("sk_...");

// Read a page the way your agent should — with a quality score attached.
var op = await client.V2.PerceiveAsync("https://example.com", new PerceiveOptions
{
    Outputs = new[] { "markdown", "structured" },
});
Console.WriteLine($"{op.Outputs["markdown"].Url} {op.RenderQuality}"); // e.g. 0.93

V2 — agent-ready data (client.V2)

The V2 namespace turns web pages into agent-ready data: render, search, extract, ingest, and monitor. All V2 endpoints require a private API key and are plan-gated — a disabled feature or exhausted monthly quota throws QuotaException (HTTP 402).

Every render carries RenderQuality (0.0–1.0). A low score means the page didn't render cleanly (challenge page, cookie wall, empty shell); the content is still returned, flagged, so a bad read never quietly enters your agent's context.

Perceive — render a URL into artifacts

var op = await client.V2.PerceiveAsync("https://example.com", new PerceiveOptions
{
    Outputs = new[] { "markdown", "screenshot", "structured" },
    Extract = new[] { "tables", "metadata" },
});
Console.WriteLine(op.RenderQuality);          // honesty score, 0.0–1.0
Console.WriteLine(op.Outputs["markdown"].Url); // 15-min signed URL
Console.WriteLine(op.Structured);

// Re-sign artifact URLs later:
var again = await client.V2.GetPerceiveOperationAsync(op.OperationId);

// Batch (<=1000 URLs; small batches run inline, larger return "queued" — poll):
var batch = await client.V2.PerceiveBatchAsync(new[] { "https://a.com", "https://b.com" }, new PerceiveBatchOptions
{
    Outputs = new[] { "markdown" },
    OutputMode = "zip",
});
var done = await client.V2.GetPerceiveBatchAsync(batch.JobId);

// Direct download — stream the artifact bytes, no signed-URL round trip.
// Requires exactly one artifact-producing output:
var direct = await client.V2.PerceiveDirectAsync("https://example.com", new PerceiveOptions
{
    Outputs = new[] { "pdf" },
});
await File.WriteAllBytesAsync(direct.Filename ?? "page.pdf", direct.Content);

// Re-download a stored artifact of an earlier operation (410 past retention):
var bytes = await client.V2.DownloadPerceiveArtifactAsync(op.OperationId, "markdown");

Discover — enumerate a site's URLs (no rendering)

var found = await client.V2.DiscoverAsync("https://example.com", new DiscoverOptions
{
    Mode = "hybrid",                          // "sitemap" | "crawl" | "hybrid"
    MaxUrls = 200,
    ExcludePatterns = new[] { "/tag/" },
});
Console.WriteLine($"{found.Total} {string.Join(", ", found.Urls)}");

Lookup — web search with optional auto-perceive

var search = await client.V2.LookupAsync("best static site generators", new LookupOptions
{
    Category = "web",            // web | news | images | scholar | patents | maps
    NumResults = 10,
    PerceiveTop = 3,             // auto-render top 3 results (uses perceive quota)
});
foreach (var hit in search.Results)
{
    Console.WriteLine($"{hit.Title} {hit.Url} {hit.Perceive?.RenderQuality}");
}

Distill — schema-driven structured extraction

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 // optional free CSS pass before the LLM tier
    {
        BaseSelector = ".plan-card",
        Fields = new[]
        {
            new CssField { Name = "name", Type = "text", Selector = "h3" },
            new CssField { Name = "price", Type = "text", Selector = ".price" },
        },
    },
});
Console.WriteLine($"{extraction.Results[0].Data} {extraction.Results[0].ExtractionTier}");

// Or discover-then-distill:
await client.V2.DistillAsync(new DistillOptions
{
    DiscoverFrom = new DistillDiscoverFrom { Url = "https://example.com", Mode = "sitemap", MaxPages = 10 },
    Schema = new JsonObject { ["title"] = "page title", ["summary"] = "one-line summary" },
});

Ingest — site or files to RAG-ready JSONL (always async)

Turn a whole site — or a set of uploaded documents — into chunked, RAG-ready JSONL through one pipeline.

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

// Or from uploaded files (PDF, DOCX, PPTX, XLSX, CSV, HTML, EPUB, TXT/MD, legacy/ODF office):
var fileJob = await client.V2.IngestFilesAsync(new[]
{
    new FileInput(await File.ReadAllBytesAsync("handbook.pdf"), "handbook.pdf"),
    new FileInput(await File.ReadAllBytesAsync("notes.docx"), "notes.docx"),
}, new IngestFilesOptions
{
    Chunk = new IngestChunkOptions { MaxWords = 512, SentenceOverlap = 1 },
});

var status = await client.V2.GetIngestJobAsync(job.JobId);            // poll
if (status.Status == "completed") Console.WriteLine(status.OutputUrl); // JSONL

await client.V2.ListIngestJobsAsync(new V2ListOptions { Limit = 20 });
await client.V2.CancelIngestJobAsync(job.JobId); // idempotent

// Webhook signing (HMAC):
var secret = await client.V2.GetWebhookSecretAsync();
await client.V2.RotateWebhookSecretAsync();         // invalidates old secret
await client.V2.RetryIngestWebhookAsync(job.JobId); // re-deliver

Watch — recurring change monitoring

var watcher = await client.V2.CreateWatcherAsync("https://example.com/pricing", new WatchCreateOptions
{
    FrequencyMinutes = 60,       // hourly floor
    DiffMode = "auto",           // auto | text | structured | tables | metadata
    WebhookUrl = "https://my.app/hooks/changes",
    NotifyEmail = true,
});

await client.V2.ListWatchersAsync();
await client.V2.GetWatcherAsync(watcher.WatcherId);
await client.V2.GetWatcherSnapshotsAsync(watcher.WatcherId, new SnapshotListOptions { Limit = 10 });
await client.V2.UpdateWatcherAsync(watcher.WatcherId, new WatcherUpdate { Status = "paused" });
await client.V2.UpdateWatcherAsync(watcher.WatcherId, new WatcherUpdate { WebhookUrl = "" }); // clears webhook
await client.V2.DeleteWatcherAsync(watcher.WatcherId); // soft-delete, idempotent

V2 error handling

using Enconvert;

try
{
    await client.V2.IngestAsync(new IngestOptions { Urls = new[] { "https://example.com" } });
}
catch (QuotaException)
{
    Console.Error.WriteLine("Upgrade plan or wait for quota reset");
}

File conversion

The same key also converts 40+ formats. Two "anything → X" endpoints auto-detect the input; the format-specific endpoints below give you a validated, typed path.

Anything to Markdown / PDF

// Any document → clean Markdown (a RAG-ingestion building block):
await client.ConvertToMarkdownAsync("report.docx", new ConvertToMarkdownOptions { SaveTo = "report.md" });
// PDF, DOCX, PPTX, XLSX, CSV, HTML, EPUB, TXT/MD, and legacy/ODF office. (Images not supported.)

// Almost anything → PDF:
await client.ConvertToPdfAsync("slides.pptx", new ConvertToPdfOptions { SaveTo = "slides.pdf" });
// office/ODF/Pages/Numbers/RTF/CSV, HTML, Markdown, text, images, SVG, EPUB, or a PDF passthrough.
// Only PdfOptions.Grayscale is honored on this endpoint:
await client.ConvertToPdfAsync("scan.pdf", new ConvertToPdfOptions
{
    PdfOptions = new PdfOptions { Grayscale = true },
    SaveTo = "gray.pdf",
});

Both accept a path string, a byte[], or a FileInput record (new FileInput(bytes, "scan.pdf")) when bytes need an explicit filename.

Image Conversion

var result = await client.ConvertImageAsync("photo.heic", new ConvertImageOptions
{
    OutputFormat = "webp",
    SaveTo = "photo.webp",
});

Any pair among jpeg, png, svg, heic, webp — plus PDF rasterization:

await client.ConvertImageAsync("scan.pdf", new ConvertImageOptions { OutputFormat = "jpeg", SaveTo = "scan.jpeg" });

File input accepts a path string, a byte[], or a FileInput record (new FileInput(bytes, "scan.pdf")) when bytes need an explicit filename — pick whichever ConvertImageAsync/ConvertDocumentAsync overload matches what you have.

Unsupported pairs throw before any request is made. Introspect programmatically:

using Enconvert;

Formats.ValidOutputsFor("json"); // ["csv", "toml", "xml", "yaml"]
Formats.ValidOutputsFor("pdf");  // ["jpeg"]

Document Conversion

await client.ConvertDocumentAsync("report.docx", new ConvertDocumentOptions { SaveTo = "report.pdf" });
await client.ConvertDocumentAsync("data.json", new ConvertDocumentOptions { OutputFormat = "yaml", SaveTo = "data.yaml" });
await client.ConvertDocumentAsync("notes.md", new ConvertDocumentOptions { OutputFormat = "html", SaveTo = "notes.html" });

Supported inputs: doc/docx, xls/xlsx, ppt/pptx, odt, ods, odp, ots, pages, numbers, html, markdown, csv, json, xml, yaml, toml. (EPUB → use ConvertToPdfAsync / ConvertToMarkdownAsync.)

Supported conversions

Input Outputs
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 each other (all 20 pairs)
pdf jpeg

URL to PDF / Screenshot / Markdown

var result = await client.ConvertUrlToPdfAsync("https://example.com", new UrlToPdfOptions
{
    SaveTo = "page.pdf",
});

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

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

Website to PDF / Screenshot (whole-site batch)

Discover every page of a website (via sitemap, or full crawl on Pro/Business plans), convert each one in the background, and receive a single ZIP. Requires a private API key with crawl access.

var batch = await client.ConvertWebsiteToPdfAsync("https://example.com", new WebsiteToPdfOptions
{
    CrawlMode = "sitemap",                    // "auto" (default) | "sitemap" | "full"
    ExcludePatterns = new[] { "/blog/tag/" }, // full crawl mode only
});
Console.WriteLine($"{batch.BatchId} {batch.UrlCount} {batch.DiscoveryMethod}");

// Block until done and save the ZIP:
var status = await client.WaitForBatchAsync(batch.BatchId, new WaitForBatchOptions { SaveTo = "site.zip" });
Console.WriteLine($"{status.Completed} of {status.Total} pages converted");

// Or poll yourself:
var s = await client.GetBatchStatusAsync(batch.BatchId);
if (s.Status != "processing") Console.WriteLine(s.ZipDownloadUrl);

ConvertWebsiteToScreenshotAsync works the same way and produces a ZIP of PNGs.

PDF options & authenticated pages

var result = await client.ConvertUrlToPdfAsync("https://internal.example.com/report", new UrlToPdfOptions
{
    PdfOptions = new PdfOptions
    {
        PageSize = "A4",             // or custom dimensions via PageWidth + PageHeight
        Orientation = "landscape",
        Margins = new PdfMargins { Top = 10, Bottom = 10, Left = 15, Right = 15 },
        Header = new PdfHeaderFooter { Content = "Quarterly Report", Height = 15 },
        Footer = new PdfHeaderFooter { Content = "Confidential", Height = 12 },
    },
    Auth = new HttpBasicAuth { Username = "user", Password = "pass" }, // or Cookies / Headers, plan-gated
    SaveTo = "report.pdf",
});

Do not combine Auth with an Authorization header — the API rejects the conflict.

Job status (async polling)

var status = await client.GetJobStatusAsync("job_abc123");
if (status.Status == "success") Console.WriteLine(status.PresignedUrl);

Error Handling

using Enconvert;

try
{
    await client.V2.PerceiveAsync("https://example.com");
}
catch (AuthenticationException)
{
    Console.Error.WriteLine("Invalid API key");
}
catch (QuotaException)
{
    Console.Error.WriteLine("Plan feature off or quota exhausted");
}
catch (RateLimitException)
{
    Console.Error.WriteLine("Too many requests — slow down");
}
catch (ApiException e)
{
    Console.Error.WriteLine($"API error [{e.StatusCode}]: {e.Message}");
}

The exception hierarchy is EnconvertException → ApiException (carries StatusCode) → AuthenticationException / QuotaException / RateLimitException.

Configuration

var client = new EnconvertClient(
    apiKey: "sk_...",
    baseUrl: null,      // override the API base URL; defaults to https://api.enconvert.com
    timeoutMs: 300_000  // default
);

Every method accepts an optional trailing CancellationToken.

Get an API Key

Sign up at enconvert.com. Free tier: 100 ops/month, no credit card.

License

MIT

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • net8.0

    • No dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.1.1 100 9/17/2026
0.1.0 119 8/6/2026
0.0.1 118 7/16/2026