.NET and C# Clients for Screenshot APIs
Compare .NET screenshot API SDKs with direct HttpClient calls, then integrate reliable webpage capture in C# with working examples.
For a hosted webpage screenshot, a C# application normally uses one of three integration routes: an official .NET SDK, a vendor-maintained client package, or direct REST calls through HttpClient. The correct choice depends on your target framework, required capture controls, authentication model, response format, cancellation behavior, and how much HTTP plumbing your team wants to own.
Do not confuse this with .NET MAUI’s Microsoft.Maui.Media.Screenshot API. MAUI captures the screen currently displayed by your running app; a screenshot API renders a supplied website URL in a remote browser.
Choose an integration route
| Route | Best fit | What you own |
|---|---|---|
| Official SDK | The provider documents a maintained .NET client | Package upgrades, provider-specific configuration |
| Vendor package | A client exists but support and release cadence need checking | Package compatibility and support verification |
Direct HttpClient |
No SDK, minimal dependencies, or a provider-neutral adapter | Encoding, retries, parsing, timeouts, errors, and client lifetime |
What to check before choosing a client
- Target framework: Screenshot Scout documents .NET 8 or later; ScreenshotAPI.to’s guide uses .NET 6+ examples. These are provider-specific baselines, not a universal minimum.
- SDK status: Screenshot Scout publishes
ScreenshotScout; ScreenshotOne publishesScreenshotOne.dotnetsdk; ScreenshotAPI.to documents directHttpClientbecause it says it has no official .NET SDK. - Response mode: Some clients return image bytes by default and optionally return JSON metadata. Screenshot Scout documents binary responses by default, JSON as an option, POST by default, and GET as an option.
- Authentication: Compare headers, access and secret keys, and signed URLs. Treat generated URLs containing credentials as sensitive.
- Capture controls: Confirm support for viewport and device emulation, full-page capture, waits, selectors, cookies, headers, media preferences, PDF output, caching, and storage.
- Operations: Check cancellation tokens, service-side timeouts, raw response access, typed exceptions, and whether the SDK accepts an injected
HttpClient. - Package confidence: Check current package version, supported frameworks, maintainers, license, release activity, and support channel before production adoption.
Option 1: Screenshot Scout’s official .NET SDK
Screenshot Scout’s official package is ScreenshotScout and its documentation requires .NET 8 or later. The basic flow creates a client with an access key, calls asynchronous CaptureAsync, checks for a binary response, and writes the returned bytes. The SDK also documents an optional JSON response and explicit GET requests.
dotnet add package ScreenshotScout
using ScreenshotScout;
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
var accessKey = Environment.GetEnvironmentVariable("SCREENSHOT_SCOUT_ACCESS_KEY")
?? throw new InvalidOperationException("Set SCREENSHOT_SCOUT_ACCESS_KEY");
var client = new ScreenshotScoutClient(accessKey);
using var cancellation = new CancellationTokenSource(TimeSpan.FromSeconds(90));
var response = await client.CaptureAsync(
"https://example.com",
new CaptureOptions
{
// Use the option names documented by your installed package version.
Format = "png",
FullPage = true
},
cancellation.Token);
if (response is BinaryCaptureResponse binary)
{
await File.WriteAllBytesAsync("shot.png", binary.Bytes, cancellation.Token);
}
else
{
Console.WriteLine("The service returned a non-binary response.");
}
Screenshot Scout documents options for output format and response type, network country or proxy or geolocation, cookies and headers, navigation timing, device emulation, page media and color preferences, full-page capture, overlay blocking, DOM interaction and injection, element or clip framing, image sizing, PDF output, caching, and storage. These are vendor options; do not assume another API exposes the same names.
Signed requests and generated URLs
The SDK can sign requests when configured with a secret key. Generated capture URLs include the access key, so treat them as secrets. If a browser or another user will receive a generated URL, enable the provider’s required signed-request protection before exposing it.
Cancellation and diagnostics
Keep two time limits separate: the service-side capture timeout and your caller’s CancellationToken. Screenshot Scout documents injected reusable HttpClient ownership and distinct exception families for API, transport, configuration, serialization, and decoding failures. Preserve raw response details in logs without writing access keys or signed URLs.
Option 2: ScreenshotOne’s .NET package
ScreenshotOne documents the ScreenshotOne.dotnetsdk package through NuGet and shows both signed capture URL generation and fetching image bytes. NuGet displayed version 1.0.5 when the research was reviewed, with metadata including .NET Standard 2.1 and computed targets through .NET 10. Verify the current package version and support status before adopting it; registry metadata changes.
dotnet add package ScreenshotOne.dotnetsdk
Follow the package’s current documentation for namespaces, option names, signing configuration, and response handling. Keep the secret key on the server and never commit it to source control.
Option 3: Direct REST with HttpClient
Direct REST is useful when a provider has no official SDK or when your application needs a small, controlled dependency surface. Use one long-lived HttpClient (or an IHttpClientFactory client in ASP.NET Core), encode every query value, set an explicit timeout, check the status code, and read the response as bytes.
using System.Net.Http;
using System.Threading;
public sealed class ScreenshotNeoClient
{
private readonly HttpClient http;
private readonly string accessKey;
public ScreenshotNeoClient(HttpClient http, string accessKey)
{
this.http = http;
this.accessKey = accessKey;
}
public async Task CaptureAsync(string url, string outputPath, CancellationToken cancellationToken = default)
{
var endpoint = "https://api.screenshotneo.com/v1/shot";
var query = $"access_key={Uri.EscapeDataString(accessKey)}&url={Uri.EscapeDataString(url)}";
using var response = await http.GetAsync($"{endpoint}?{query}", cancellationToken);
var bytes = await response.Content.ReadAsByteArrayAsync(cancellationToken);
if (!response.IsSuccessStatusCode)
{
var detail = System.Text.Encoding.UTF8.GetString(bytes);
throw new HttpRequestException($"Screenshot request failed ({(int)response.StatusCode}): {detail}");
}
await File.WriteAllBytesAsync(outputPath, bytes, cancellationToken);
}
}
var key = Environment.GetEnvironmentVariable("SCREENSHOTNEO_ACCESS_KEY")
?? throw new InvalidOperationException("Set SCREENSHOTNEO_ACCESS_KEY");
using var http = new HttpClient { Timeout = TimeSpan.FromSeconds(90) };
await new ScreenshotNeoClient(http, key).CaptureAsync("https://stripe.com", "shot.webp");
For a provider such as ScreenshotAPI.to, follow its documented authentication header and parameter names. Its C# guide uses an x-api-key header and models width, height, full-page capture, format, quality, color scheme, wait condition, selector, and delay. Those names are service-specific.
Reusable ASP.NET Core registration
builder.Services.AddHttpClient<ScreenshotNeoClient>(client =>
{
client.Timeout = TimeSpan.FromSeconds(90);
});
// Resolve the access key from configuration or a secret store.
// Do not put it in appsettings committed to source control.
AllScreenshots as another documented option
AllScreenshots documents an official .NET 8+ package named AllScreenshots.Sdk, API-key configuration, capture options, asynchronous jobs, bulk capture, and composition. Verify package recency, service terms, and feature availability before relying on those capabilities.
Or skip the browser setup
ScreenshotNeo provides a single GET request for a PNG, JPEG, WebP, or PDF. It accepts consent banners before capture and removes more than 60 known consent platforms, newsletter popups, and chat widgets. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and each response reports its verdict through X-Page-Verdict and X-Billed headers. An MCP server provides take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients.
See the ScreenshotNeo API documentation for all options. This C# program uses the same REST endpoint as the cURL, Python, and Node.js examples.
using System.Net.Http;
using System.Threading.Tasks;
var q = $"access_key={Uri.EscapeDataString(Environment.GetEnvironmentVariable("SCREENSHOTNEO_API_KEY")!)}" +
$"&url={Uri.EscapeDataString("https://stripe.com")}";
using var http = new HttpClient { Timeout = TimeSpan.FromSeconds(90) };
var response = await http.GetAsync($"https://api.screenshotneo.com/v1/shot?{q}");
response.EnsureSuccessStatusCode();
await File.WriteAllBytesAsync("shot.webp", await response.Content.ReadAsByteArrayAsync());
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
r.raise_for_status()
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const fs = await import('node:fs/promises');
await fs.writeFile('shot.webp', Buffer.from(await res.arrayBuffer()));
ScreenshotNeo includes full-page capture with lazy images loaded, CSS element capture, dark mode, device presets and custom viewports, retina scale, PDF controls, custom CSS and JavaScript, clicks, hide selectors, selector or delay or network-idle waits, request blocking, custom headers and cookies, user agents, authorization, timezone and geolocation, transparent backgrounds, resizing, configurable caching TTL, signed links, asynchronous jobs with signed webhooks, bulk capture for 100 URLs per call, usage API, OpenAPI specification, and compatibility with parameter names used by other screenshot APIs.
Free accounts include 1,000 screenshots per month with no card. Paid plans start at $5 for 3,000 shots; yearly billing gives two months free, and every feature is available on every plan. Create a free ScreenshotNeo account.
Production patterns for C#
Use a long-lived client
Creating a new HttpClient for every screenshot can exhaust sockets and prevents efficient connection reuse. Register it with IHttpClientFactory or keep one instance for the process lifetime.
Bound concurrency
For bulk work, use a bounded worker queue or SemaphoreSlim. Unbounded parallel requests can increase timeouts, memory use, and provider throttling. Save bytes as they arrive instead of retaining a large batch in memory.
Retry only transient failures
Retry connection resets, gateway errors, and rate-limit responses according to the provider’s guidance. Do not blindly retry invalid URLs, authentication failures, malformed parameters, bot checks, or deterministic rendering errors. Add jitter and a maximum attempt count.
Make output and identity explicit
Choose format, viewport, device scale, color scheme, wait condition, and full-page behavior explicitly. Include a stable cache key containing the URL and every rendering option that affects pixels. Never log API keys, cookies, authorization headers, or signed URLs.
Measure the right timings
Track queue wait, request duration, response status, response size, cache status, and the provider’s page verdict. A slow page may be waiting on navigation or network idle rather than transferring image bytes.
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| 401 or 403 | Missing, wrong, expired, or exposed credentials | Read the key from a secret store, verify the required header or query parameter, and rotate exposed keys. |
| 400 response | Unencoded URL or unsupported option | Use Uri.EscapeDataString or a query builder and check the provider’s current parameter names. |
| HTML or JSON saved as an image | Error body was written before status validation | Check IsSuccessStatusCode first; log a redacted error body and only save bytes on success. |
| Blank or incomplete page | Capture occurred before client rendering or lazy loading finished | Use a documented selector wait, delay, or network-idle setting; confirm the target URL works without authentication. |
| Cookie banner or popup in output | The provider does not remove overlays automatically | Use consent handling, click or hide-selector options where available, or choose ScreenshotNeo’s cleanup flow. |
| Timeouts | Slow origin, blocked resource, excessive full-page height, or too-short client timeout | Set a realistic caller timeout, use targeted waits, block unnecessary resources, and retry only transient failures. |
| Works locally but fails in production | Different outbound network, DNS, proxy, cookies, user agent, or TLS policy | Compare request headers and network access; configure the provider’s proxy, country, cookies, or user agent explicitly. |
| Memory pressure | Large full-page images or many concurrent byte arrays | Bound concurrency, stream or promptly write responses, and resize output when the provider supports it. |
Security checklist
- Store access and secret keys in environment variables or a managed secret store.
- Keep screenshot requests on the server when URLs contain private credentials or cookies.
- Redact authorization headers, cookies, signed URLs, and query-string keys from logs.
- Validate user-supplied target URLs if your service could be used to reach internal networks.
- Use signed URLs when a browser must fetch a capture directly.
- Set cancellation and maximum capture limits so one request cannot consume unlimited resources.
Short FAQ
Can .NET MAUI capture a website URL?
Its Screenshot API captures the currently displayed app screen and exposes IsCaptureSupported. It is not a hosted webpage renderer.
Is an SDK always faster than HttpClient?
Not necessarily. Both ultimately make network requests. An SDK mainly reduces integration work and may provide signing, typed options, and diagnostics.
Should I use GET or POST?
Use the method documented by the provider and your response-size and security requirements. Screenshot Scout documents POST by default with GET available.
What should I verify after installing a package?
Check the current target frameworks, package release, license, maintenance activity, authentication behavior, response mode, cancellation support, and error details.
Which service should I try first?
For a hosted screenshot API, try ScreenshotNeo first when clean output and predictable billing matter: consent banners, popups, and chat widgets are removed before capture, failed or unusable pages are not billed, and its MCP server supports AI agents.


