Need to capture website screenshots or generate PDFs in a .NET application? Installing Playwright or Puppeteer in a C# project adds significant complexity — browser binaries, async lifecycle management, Linux dependency headaches in Docker. This guide shows a clean alternative: the URLSnap REST API, called with HttpClient from any .NET 6/8 project.
using System.Net.Http.Json;
using var http = new HttpClient();
var result = await http.PostAsJsonAsync(
"https://urlsnap.dev/api/register",
new { email = "you@example.com" }
);
var data = await result.Content.ReadFromJsonAsync<Dictionary<string, string>>();
Console.WriteLine(data!["key"]); // us_abc123...
using System.Net.Http.Headers;
const string ApiKey = "us_your_key_here";
async Task TakeScreenshot(string url, string outputPath, int width = 1280, int height = 800)
{
using var http = new HttpClient();
http.DefaultRequestHeaders.Add("x-api-key", ApiKey);
var query = $"?url={Uri.EscapeDataString(url)}&width={width}&height={height}";
var response = await http.GetAsync($"https://urlsnap.dev/api/screenshot{query}");
response.EnsureSuccessStatusCode();
var bytes = await response.Content.ReadAsByteArrayAsync();
await File.WriteAllBytesAsync(outputPath, bytes);
Console.WriteLine($"Saved {outputPath} ({bytes.Length / 1024} KB)");
}
// Full-page screenshot
await TakeScreenshot("https://example.com", "screenshot.png");
// Mobile viewport
await TakeScreenshot("https://urlsnap.dev", "mobile.png", width: 390, height: 844);
const string ApiKey = "us_your_key_here";
async Task UrlToPdf(string url, string outputPath, string format = "A4", bool landscape = false)
{
using var http = new HttpClient();
http.DefaultRequestHeaders.Add("x-api-key", ApiKey);
var query = $"?url={Uri.EscapeDataString(url)}&format={format}&landscape={landscape.ToString().ToLower()}";
var response = await http.GetAsync($"https://urlsnap.dev/api/pdf{query}");
response.EnsureSuccessStatusCode();
var bytes = await response.Content.ReadAsByteArrayAsync();
await File.WriteAllBytesAsync(outputPath, bytes);
Console.WriteLine($"PDF saved to {outputPath} ({bytes.Length / 1024} KB)");
}
// A4 PDF
await UrlToPdf("https://example.com", "page.pdf");
// Letter landscape
await UrlToPdf("https://en.wikipedia.org/wiki/C_Sharp_(programming_language)", "csharp-wiki.pdf", "Letter", landscape: true);
using System.Net.Http.Json;
const string ApiKey = "us_your_key_here";
async Task HtmlToPdf(string html, string outputPath, string format = "A4")
{
using var http = new HttpClient();
http.DefaultRequestHeaders.Add("x-api-key", ApiKey);
var response = await http.PostAsJsonAsync(
"https://urlsnap.dev/api/pdf",
new { html, format }
);
response.EnsureSuccessStatusCode();
var bytes = await response.Content.ReadAsByteArrayAsync();
await File.WriteAllBytesAsync(outputPath, bytes);
}
var invoiceHtml = """
<!DOCTYPE html>
<html>
<head>
<style>
body { font-family: sans-serif; padding: 40px; color: #111; }
h1 { color: #6366f1; }
table { width: 100%; border-collapse: collapse; margin-top: 20px; }
th, td { padding: 10px; border-bottom: 1px solid #eee; text-align: left; }
.total { font-weight: bold; }
</style>
</head>
<body>
<h1>Invoice #INV-2026-001</h1>
<p>Bill to: Acme Corp</p>
<table>
<tr><th>Item</th><th>Amount</th></tr>
<tr><td>API Starter Plan</td><td>$9.00</td></tr>
<tr class="total"><td>Total</td><td>$9.00</td></tr>
</table>
</body>
</html>
""";
await HtmlToPdf(invoiceHtml, "invoice.pdf");
Console.WriteLine("Invoice saved!");
// Program.cs (minimal API style)
using Microsoft.AspNetCore.Mvc;
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
const string ApiKey = "us_your_key_here";
app.MapGet("/download-pdf", async ([FromQuery] string url, HttpContext ctx) =>
{
if (string.IsNullOrWhiteSpace(url))
return Results.BadRequest(new { error = "url required" });
using var http = new HttpClient { Timeout = TimeSpan.FromSeconds(60) };
http.DefaultRequestHeaders.Add("x-api-key", ApiKey);
var query = $"?url={Uri.EscapeDataString(url)}&format=A4";
var upstream = await http.GetAsync($"https://urlsnap.dev/api/pdf{query}");
if (!upstream.IsSuccessStatusCode)
{
var errBody = await upstream.Content.ReadAsStringAsync();
return Results.Content(errBody, "application/json", statusCode: (int)upstream.StatusCode);
}
var bytes = await upstream.Content.ReadAsByteArrayAsync();
return Results.File(bytes, "application/pdf", "page.pdf");
});
app.Run();
using var http = new HttpClient();
http.DefaultRequestHeaders.Add("x-api-key", "us_your_key_here");
var info = await http.GetFromJsonAsync<Dictionary<string, JsonElement>>("https://urlsnap.dev/api/me");
Console.WriteLine($"Plan: {info!["plan"]}");
Console.WriteLine($"Used today: {info["requests_today"]}/{info["daily_limit"]}");
Console.WriteLine($"Total: {info["requests_total"]}");
async Task<bool> SafeScreenshot(string url, string outputPath, string apiKey)
{
try
{
using var http = new HttpClient { Timeout = TimeSpan.FromSeconds(60) };
http.DefaultRequestHeaders.Add("x-api-key", apiKey);
var query = $"?url={Uri.EscapeDataString(url)}&width=1280&height=800";
var response = await http.GetAsync($"https://urlsnap.dev/api/screenshot{query}");
if (response.StatusCode == System.Net.HttpStatusCode.TooManyRequests)
{
Console.WriteLine("Daily limit reached. Upgrade at urlsnap.dev/#pricing");
return false;
}
if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized)
{
Console.WriteLine("Invalid API key.");
return false;
}
response.EnsureSuccessStatusCode();
var bytes = await response.Content.ReadAsByteArrayAsync();
await File.WriteAllBytesAsync(outputPath, bytes);
return true;
}
catch (TaskCanceledException)
{
Console.WriteLine("Request timed out.");
return false;
}
catch (HttpRequestException ex)
{
Console.WriteLine($"HTTP error: {ex.Message}");
return false;
}
}
Free tier: 20 requests/day. No NuGet packages, no browser binaries.
Get your free API key →