> Source: https://txtfetch.com/for/csharp > Plain-text twin — every page on txtfetch.com has one. https://txtfetch.com/text --- # Three NuGet packages, one HttpClient call. PdfPig or iText, the Open XML SDK, and a Tesseract wrapper each do one job well. None of the three talk to each other, and none of them do OCR without native binaries. the-parser-zoo .NET's document story means picking a library per format, and living with each one's specific trade-off. On PDF, PdfPig is MIT-licensed and capable for text extraction, but it has no OCR of its own. iText is more full-featured, but it is dual-licensed. It is free under AGPLv3, which requires open-sourcing anything that links it. In most commercial contexts, that is a non-starter. The alternative is a paid commercial license. The Open XML SDK reads .docx, .xlsx, and .pptx directly against the OOXML schema. But its object model is low-level by design. Pulling plain text out of a paragraph means walking Run and Text elements manually. It also has nothing at all for the legacy .doc, .xls, and .ppt binary formats, which need a separate library entirely. OCR means a native Tesseract wrapper, commonly the Tesseract NuGet package, itself a wrapper over the C++ library. It ships platform-specific native binaries (win-x64, linux-x64, osx-arm64, and more) as part of your deployment. Picking the wrong runtime identifier for a target container is a common source of a DllNotFoundException. That error only shows up once deployed, not at build time. | library | covers | stops at | | --- | --- | --- | | `PdfPig` | PDF text extraction, MIT-licensed | No OCR of its own — scanned pages with no text layer return nothing | | `iText` | Full-featured PDF reading and writing | Free under AGPLv3 (viral copyleft) or a paid commercial license — a real decision for closed-source products | | `Open XML SDK` | Direct, schema-accurate .docx/.xlsx/.pptx access | Low-level object model (manual Run/Text traversal); no legacy .doc/.xls/.ppt support at all | | `Tesseract (NuGet wrapper)` | OCR via a native Tesseract binary per platform | Ships platform-specific native binaries; a wrong runtime identifier fails only after deployment | one-request txtfetch replaces the three-library split with one call. POST a file, or pass ?url=, to https://api.txtfetch.com/v1/extract using System.Net.Http and System.Text.Json. Both are already in the BCL, so this needs zero NuGet packages. There is no AGPL-vs-commercial licensing decision for PDF, and no manual Run/Text traversal for Office files. There is also no native Tesseract runtime identifier to get right for OCR. The response is the same { "status": "success", "extracted\_text": "..." } across every format. C# ```csharp using System.Net.Http.Headers; using System.Text.Json; using var client = new HttpClient(); client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", Environment.GetEnvironmentVariable("TXTFETCH_KEY")); using var content = new MultipartFormDataContent(); var fileBytes = await File.ReadAllBytesAsync("report.pdf"); content.Add(new ByteArrayContent(fileBytes), "file", "report.pdf"); var response = await client.PostAsync("https://api.txtfetch.com/v1/extract", content); var json = JsonDocument.Parse(await response.Content.ReadAsStringAsync()).RootElement; Console.WriteLine(json.GetProperty("extracted_text").GetString()); ``` ``` { "status": "success", "extracted_text": "..." } ``` from-a-url Skip the download entirely. Pass a `url` parameter and txtfetch fetches the document server-side: C# ```csharp using System.Net.Http.Headers; using System.Text.Json; using var client = new HttpClient(); client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", Environment.GetEnvironmentVariable("TXTFETCH_KEY")); var url = "https://api.txtfetch.com/v1/extract?url=" + Uri.EscapeDataString("https://example.com/report.pdf"); var response = await client.PostAsync(url, null); var json = JsonDocument.Parse(await response.Content.ReadAsStringAsync()).RootElement; Console.WriteLine(json.GetProperty("extracted_text").GetString()); ``` errors Every non-success response carries a stable `error.code`. Match on that, not on `error.message`. See the full [error reference](https://txtfetch.com/docs/errors) for every code and HTTP status txtfetch can return. C# ```csharp using System.Net; using System.Net.Http.Headers; using System.Text.Json; using var client = new HttpClient(); client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", Environment.GetEnvironmentVariable("TXTFETCH_KEY")); using var content = new MultipartFormDataContent(); var fileBytes = await File.ReadAllBytesAsync("report.pdf"); content.Add(new ByteArrayContent(fileBytes), "file", "report.pdf"); var response = await client.PostAsync("https://api.txtfetch.com/v1/extract", content); var body = await response.Content.ReadAsStringAsync(); var json = JsonDocument.Parse(body).RootElement; if (response.IsSuccessStatusCode) { Console.WriteLine(json.GetProperty("extracted_text").GetString()); } else if (response.StatusCode == (HttpStatusCode)429) { var code = json.GetProperty("error").GetProperty("code").GetString(); var retryAfter = response.Headers.RetryAfter?.Delta?.TotalSeconds; Console.WriteLine($"back off: {code}, retry after {retryAfter}s"); } else { var error = json.GetProperty("error"); Console.WriteLine($"extraction failed: {error.GetProperty("code").GetString()} — {error.GetProperty("message").GetString()}"); } ``` big-files-and-batches Large uploads or slow documents are routed to an async job automatically. That returns a `202` plus a `job_id` to poll, and `?async=true` forces that path for any request. Direct upload size ceilings by plan: Hobby 10 MB, Developer 50 MB, Scale 200 MB. Use `?url=` for anything larger. Server-side fetches aren't held to the upload ceiling. See [async jobs & webhooks](https://txtfetch.com/docs/async) for the full lifecycle, including webhook delivery instead of polling. C# ```csharp using System.Net.Http.Headers; using System.Text.Json; using var client = new HttpClient(); client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", Environment.GetEnvironmentVariable("TXTFETCH_KEY")); var submitUrl = "https://api.txtfetch.com/v1/extract?url=" + Uri.EscapeDataString("https://example.com/report.pdf") + "&async=true"; var submit = await client.PostAsync(submitUrl, null); var submitJson = JsonDocument.Parse(await submit.Content.ReadAsStringAsync()).RootElement; var jobId = submitJson.GetProperty("job_id").GetString(); JsonElement result; while (true) { await Task.Delay(2000); var poll = await client.GetAsync($"https://api.txtfetch.com/v1/extract/{jobId}"); result = JsonDocument.Parse(await poll.Content.ReadAsStringAsync()).RootElement; if (result.GetProperty("status").GetString() != "processing") break; } Console.WriteLine(result.GetProperty("extracted_text").GetString()); ``` gotchas - iText's AGPLv3 free tier requires open-sourcing software that links it, in most distribution models. Many commercial .NET teams end up on the paid commercial license specifically to avoid that. That license cost is more than most PDF-reading tasks need to carry. - The Open XML SDK's object model mirrors the OOXML schema closely. That is powerful for editing, but it means even "give me the plain text" requires walking Paragraph → Run → Text nodes yourself. There's no ExtractText() convenience method. - Open XML SDK is OOXML-only. A legacy .doc or .xls, still common in older enterprise archives, needs an entirely separate library. The SDK doesn't parse the OLE2 binary format at all. - The Tesseract NuGet wrapper's native binaries are runtime-identifier-specific (win-x64, linux-x64, linux-arm64, and more). A container built for the wrong RID compiles fine, then throws DllNotFoundException the first time OCR actually runs. formats - [PDF](https://txtfetch.com/extract/pdf) - [Word / PowerPoint / Excel](https://txtfetch.com/extract/docx) - [Legacy .doc / .xls / .ppt](https://txtfetch.com/extract/legacy-office) - [Scans & images (OCR)](https://txtfetch.com/extract/image) faq **How do I extract text from a PDF in C# without iText's AGPL license?**: POST the PDF (or pass ?url=) to https://api.txtfetch.com/v1/extract with System.Net.Http. There is no PDF library to choose, and so no AGPLv3-vs-commercial-license decision to make at all. The response is { "status": "success", "extracted_text": "..." }. **Does the Open XML SDK have a simple way to get plain text from a .docx?**: Not directly. Its object model mirrors the OOXML schema, so plain text means walking Paragraph, Run, and Text elements yourself. txtfetch returns extracted_text directly for .docx, .pptx, and .xlsx. It also reads the legacy .doc, .xls, and .ppt formats, which the Open XML SDK doesn't read at all. **How do I OCR a scanned document in .NET without shipping native Tesseract binaries?**: POST the scan to txtfetch. OCR runs server-side, so there's no Tesseract NuGet package, no per-runtime-identifier native binary, and no DllNotFoundException risk in your deployment. **Is there an official .NET SDK for txtfetch?**: Not yet. But there's nothing to install in the meantime. It's a single HttpClient POST using System.Net.Http and System.Text.Json, both already part of the .NET base class library, shown above. go-further - [How to extract text from a PDF for RAG →](https://txtfetch.com/blog/extract-text-from-pdf-for-rag) - [Parsing DOCX, PPTX, and XLSX for LLM ingestion →](https://txtfetch.com/blog/parse-office-docs-docx-pptx-xlsx-for-llms) - [Document workflows & automation →](https://txtfetch.com/solutions/document-workflows) - [API quickstart →](https://txtfetch.com/docs) - [How the pipeline works →](https://txtfetch.com/how-it-works) - [Get an API key →](https://app.txtfetch.com/signup) other-languages - [`Python`](https://txtfetch.com/for/python) - [`JavaScript`](https://txtfetch.com/for/javascript) - [`Go`](https://txtfetch.com/for/go) - [`Java`](https://txtfetch.com/for/java) - [All languages →](https://txtfetch.com/for) ## Paste it into your project. The C# / .NET snippet above runs as written. Add your key and it works. [Get an API key →](https://app.txtfetch.com/signup) [More SDK quickstarts →](https://txtfetch.com/docs/quickstarts)