PDF to EPUB Conversion in C# / .NET — toolkit.bot API Integration
This guide shows how to call the toolkit.bot REST API from C# to convert PDF files to EPUB. The implementation uses HttpClient with MultipartFormDataContent — no third-party packages required.
HttpClient example (.NET 6+)
using System;
using System.IO;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
class Pdf2EpubClient
{
private static readonly HttpClient _http = new();
private const string ApiBase = "https://toolkit.bot/api/v1";
static async Task Main(string[] args)
{
var apiKey = Environment.GetEnvironmentVariable("TOOLKIT_API_KEY")
?? throw new Exception("TOOLKIT_API_KEY not set");
var pdfPath = args.Length > 0 ? args[0] : "document.pdf";
var epubPath = Path.ChangeExtension(pdfPath, ".epub");
await ConvertAsync(apiKey, pdfPath, epubPath);
Console.WriteLine($"Saved: {epubPath}");
}
static async Task ConvertAsync(string apiKey, string pdfPath, string epubPath)
{
_http.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", apiKey);
// --- Upload ---
await using var fs = File.OpenRead(pdfPath);
using var form = new MultipartFormDataContent();
var fileContent = new StreamContent(fs);
fileContent.Headers.ContentType =
new MediaTypeHeaderValue("application/pdf");
form.Add(fileContent, "file", Path.GetFileName(pdfPath));
var uploadResp = await _http.PostAsync($"{ApiBase}/jobs", form);
uploadResp.EnsureSuccessStatusCode();
using var uploadDoc = JsonDocument.Parse(
await uploadResp.Content.ReadAsStringAsync());
var jobId = uploadDoc.RootElement.GetProperty("job_id").GetString()!;
Console.WriteLine($"Job ID: {jobId}");
// --- Poll ---
string? downloadUrl = null;
for (int i = 0; i < 60; i++)
{
await Task.Delay(4000);
var statusResp = await _http.GetAsync($"{ApiBase}/jobs/{jobId}");
using var statusDoc = JsonDocument.Parse(
await statusResp.Content.ReadAsStringAsync());
var status = statusDoc.RootElement.GetProperty("status").GetString();
Console.WriteLine($"[{i+1}] {status}");
if (status == "done")
{
downloadUrl = statusDoc.RootElement
.GetProperty("download_url").GetString();
break;
}
if (status == "failed")
throw new Exception("Conversion failed on server");
}
if (downloadUrl is null)
throw new TimeoutException("Job did not complete in time");
// --- Download ---
var epubBytes = await _http.GetByteArrayAsync(downloadUrl);
await File.WriteAllBytesAsync(epubPath, epubBytes);
}
}
Run from CLI:
dotnet run -- document.pdf
Or build a standalone executable:
dotnet publish -c Release -r win-x64 --self-contained true
ASP.NET Core controller
For web applications, inject IHttpClientFactory and wrap the conversion in a background service or controller action:
// Program.cs
builder.Services.AddHttpClient("toolkit", c =>
{
c.BaseAddress = new Uri("https://toolkit.bot/api/v1/");
c.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue(
"Bearer", builder.Configuration["Toolkit:ApiKey"]);
});
builder.Services.AddScoped<IEpubConversionService, EpubConversionService>();
// EpubConversionService.cs
public class EpubConversionService : IEpubConversionService
{
private readonly HttpClient _http;
public EpubConversionService(IHttpClientFactory factory)
=> _http = factory.CreateClient("toolkit");
public async Task<byte[]> ConvertAsync(
Stream pdfStream, string filename,
CancellationToken ct = default)
{
using var form = new MultipartFormDataContent();
var content = new StreamContent(pdfStream);
content.Headers.ContentType = new MediaTypeHeaderValue("application/pdf");
form.Add(content, "file", filename);
var upload = await _http.PostAsync("jobs", form, ct);
upload.EnsureSuccessStatusCode();
var uploadJson = await upload.Content.ReadFromJsonAsync<JsonElement>(
cancellationToken: ct);
var jobId = uploadJson.GetProperty("job_id").GetString()!;
for (int i = 0; i < 60; i++)
{
await Task.Delay(4000, ct);
var status = await _http.GetFromJsonAsync<JsonElement>(
$"jobs/{jobId}", ct);
var s = status.GetProperty("status").GetString();
if (s == "done")
{
var url = status.GetProperty("download_url").GetString()!;
return await _http.GetByteArrayAsync(url, ct);
}
if (s == "failed")
throw new InvalidOperationException("Conversion failed");
}
throw new TimeoutException("Job timed out");
}
}
// Controller
[HttpPost("convert")]
public async Task<IActionResult> Convert(IFormFile pdf)
{
await using var stream = pdf.OpenReadStream();
var epubBytes = await _epubService.ConvertAsync(stream, pdf.FileName);
return File(epubBytes, "application/epub+zip",
Path.ChangeExtension(pdf.FileName, ".epub"));
}
NuGet packages
The examples above use only BCL types. If you prefer a higher-level HTTP library, Refit generates a typed client from an interface:
// Install: dotnet add package Refit
public interface IToolkitApi
{
[Multipart]
[Post("/jobs")]
Task<JobResponse> UploadAsync([AliasAs("file")] StreamPart file);
[Get("/jobs/{jobId}")]
Task<JobResponse> GetStatusAsync(string jobId);
}
public record JobResponse(
[property: JsonPropertyName("job_id")] string JobId,
[property: JsonPropertyName("status")] string Status,
[property: JsonPropertyName("download_url")] string? DownloadUrl);
FAQ
Does this work with .NET Framework?
Yes. The HttpClient and MultipartFormDataContent APIs exist in .NET Framework 4.5+. Replace async/await Task.Delay with Thread.Sleep if targeting older frameworks without async support.
How do I handle cancellation tokens properly?
Pass a CancellationToken through to every await call. Use CancellationTokenSource.CancelAfter(TimeSpan.FromMinutes(5)) for an overall timeout on the conversion.
Is there a NuGet package for toolkit.bot?
Not yet. The HttpClient approach above is production-ready and requires no external dependencies.
Sign up at toolkit.bot/api — free tier, no credit card required.