How to Save Google Maps as an Image from a C# Browser Component
Capture a rendered Google Map with CefSharp or download a Static API image in C#, with readiness checks, policy guidance, troubleshooting, and alternatives.
To save Google Maps as an image in a C# application, choose between two approaches:
- Capture the rendered browser page with CefSharp’s off-screen Chromium browser. This preserves the interactive viewport, overlays, and state visible in the page.
- Request a map image directly from the Google Maps Static API. This avoids browser rendering and gives deterministic dimensions, styling, markers, and paths.
The first approach is usually right when your application already displays a map. The Static API is better when you need a predictable image generated from coordinates and parameters.
1. Capture a rendered map with CefSharp
CefSharp’s CaptureScreenshotAsync method captures the browser page. The example below opens a map in an off-screen Chromium browser, waits for the initial navigation, captures a 1200×800 PNG, and writes it to disk.
using CefSharp;
using CefSharp.OffScreen;
using var browser = new ChromiumWebBrowser(
"https://www.google.com/maps/@40.7128,-74.0060,12z");
await browser.WaitForInitialLoadAsync();
// Add an application-specific readiness check here. For example,
// wait until the map container exists and visible tiles have loaded.
byte[] png = await browser.CaptureScreenshotAsync(
CefSharp.DevTools.Page.CaptureScreenshotFormat.Png,
quality: 100,
viewport: new CefSharp.DevTools.Page.Viewport
{
X = 0,
Y = 0,
Width = 1200,
Height = 800,
Scale = 1
});
await File.WriteAllBytesAsync("map.png", png);
The CefSharp API describes this operation as “Capture page screenshot.” The exact readiness event differs by CefSharp version, so treat WaitForInitialLoadAsync as navigation completion rather than proof that every map tile is visible. Check the CefSharp project documentation for the version you use.
Install and initialize CefSharp
Add the package that matches your application type and target runtime, then initialize CefSharp once before creating browsers. A minimal setup is application-specific because WinForms, WPF, .NET Framework, and modern .NET use different startup code. Keep initialization on the process startup path and dispose each off-screen browser after capture.
Wait until the map is actually ready
A map can finish its first navigation while tiles, labels, controls, or custom overlays are still loading. A robust readiness strategy combines:
- Navigation completion (
WaitForInitialLoadAsync). - A short, bounded delay for the first tile batch when necessary.
- A DOM or JavaScript condition that confirms the map container exists.
- An application-specific signal for your own overlays or markers.
Use a timeout so a missing tile or blocked request cannot hold a worker forever. If you control the page, expose a JavaScript flag such as window.mapReady = true after your overlay and data work completes, then poll that flag through CefSharp’s JavaScript evaluation API.
Control the output
| Setting | Effect |
|---|---|
| Format | PNG is lossless and suitable for labels; JPEG is smaller for photographic content; WebP support depends on the CefSharp version and downstream use. |
| Viewport | Sets the captured rectangle and dimensions. Match it to the browser’s device scale and your output requirements. |
| Scale | Increases pixel density for retina-style output, at the cost of memory and file size. |
| Quality | Applies to lossy formats; it does not improve a PNG. |
A browser screenshot captures what is rendered in the viewport. It does not automatically create an unlimited, scroll-stitched map. For a larger area, set a larger viewport or capture multiple tiles and compose them only when your use complies with Google’s terms.
2. Download a map image with the Google Maps Static API
The Google Maps Static API returns a GIF, PNG, or JPEG image in response to an HTTP request. Build a URL with a center, zoom, size, map type, markers, paths, and optional styling, then save the HTTP response bytes.
using System.Net;
using System.Net.Http;
var query = new Dictionary<string, string>
{
["center"] = "40.7128,-74.0060",
["zoom"] = "12",
["size"] = "800x600",
["maptype"] = "roadmap",
["markers"] = "color:red|40.7128,-74.0060",
["format"] = "png",
["key"] = "YOUR_API_KEY"
};
var builder = new UriBuilder("https://maps.googleapis.com/maps/api/staticmap");
var encoded = string.Join("&", query.Select(pair =>
$"{Uri.EscapeDataString(pair.Key)}={Uri.EscapeDataString(pair.Value)}"));
builder.Query = encoded;
using var http = new HttpClient { Timeout = TimeSpan.FromSeconds(30) };
using var response = await http.GetAsync(builder.Uri);
response.EnsureSuccessStatusCode();
await using var input = await response.Content.ReadAsStreamAsync();
await using var output = File.Create("map.png");
await input.CopyToAsync(output);
Enable the Maps Static API in your Google Cloud project, configure billing, and authenticate the request as described in Google’s documentation. Encode every parameter. Google documents a maximum URL length of 16,384 characters, so long style, path, or marker definitions may need to be reduced.
Static API parameters you commonly need
| Parameter | Use |
|---|---|
center |
Latitude/longitude or an address around which the map is drawn. |
zoom |
Controls geographic detail. Larger values show a smaller area. |
size |
Image dimensions in pixels, such as 800x600. |
scale |
Requests higher-density output where supported. |
maptype |
Chooses a supported map presentation such as roadmap, satellite, terrain, or hybrid. |
markers |
Adds one or more markers with color, label, and coordinates. |
path |
Draws lines or routes using encoded points. |
style |
Applies feature and element styling rules. |
format |
Selects PNG, JPEG, or another documented image format. |
key and signature |
Authenticate and, where required, sign the request. |
When Static API output is the better fit
- You need identical dimensions for every generated image.
- You do not need JavaScript state, user-selected layers, or custom DOM overlays.
- You want a simple HTTP worker instead of a Chromium process.
- You can express the map using documented parameters such as markers, paths, and styles.
3. Browser screenshot or Static API?
| Requirement | Browser screenshot | Static API |
|---|---|---|
| Match the current interactive viewport | Strong fit | Weak fit |
| Preserve overlays or user state rendered in the page | Strong fit | Only what API parameters support |
| Deterministic dimensions and styling | Possible with viewport control | Strong fit through size, scale, style, and related parameters |
| Avoid JavaScript/browser rendering | No | Yes |
| Credential and billing setup | Depends on the loaded page and usage | Required for the Google service |
4. Google Maps attribution and usage rules
Keep the Google logo and all supplied attribution visible, legible, unmodified, and correctly positioned. Google’s Maps JavaScript policies require attribution when displaying results. The Google Maps Platform Terms restrict exporting, extracting, scraping, pre-fetching, indexing, storing, resharing, or rehosting Google Maps content outside the services.
- Do not remove logos, copyright notices, or other proprietary notices.
- Do not bulk-download map tiles or turn them into an external map database.
- Keep a saved image within the use permitted by the applicable Maps Platform terms.
- Review the current Google terms and API-specific policies before publishing or redistributing images.
5. Or skip the browser setup
ScreenshotNeo provides a one-call website screenshot API. It can capture the rendered Google Maps page without installing or operating CefSharp.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://www.google.com/maps/@40.7128,-74.0060,12z -o map.webp
import requests
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={
"access_key": "YOUR_API_KEY",
"url": "https://www.google.com/maps/@40.7128,-74.0060,12z"
},
timeout=90,
)
r.raise_for_status()
open("map.webp", "wb").write(r.content)
const q = new URLSearchParams({
access_key: 'YOUR_API_KEY',
url: 'https://www.google.com/maps/@40.7128,-74.0060,12z'
});
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot failed: ${res.status}`);
const fs = await import('node:fs/promises');
await fs.writeFile('map.webp', Buffer.from(await res.arrayBuffer()));
See the ScreenshotNeo API documentation for parameters and response headers. Cookie banners, newsletter popups, and chat widgets are removed before the shot. Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed, and the response identifies the page verdict and billing result in X-Page-Verdict and X-Billed headers. An MCP server lets Claude, Cursor, and other MCP clients call take_screenshot, get_page_info, and capture_pdf. The Free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000 screenshots. Create a free ScreenshotNeo account.
6. Troubleshooting
The screenshot is blank or shows a loading map
Cause: capture occurred before tiles or JavaScript finished. Fix: wait for navigation, then poll a map-ready condition or add a bounded delay. Confirm the browser process has network access and that the page did not show a consent or bot-check screen.
Markers or overlays are missing
Cause: the overlay is added asynchronously or lies outside the viewport. Fix: wait for your overlay’s DOM condition, set the viewport to include it, and capture only after your application signals readiness.
The Static API returns an error image or HTTP error
Cause: the API is not enabled, billing is not configured, the key is restricted incorrectly, or a parameter is malformed. Fix: verify project configuration, key restrictions, URL encoding, required authentication, and the documented parameter spelling.
The request exceeds the URL limit
Cause: marker, path, or style parameters exceed Google’s documented 16,384-character limit. Fix: shorten encoded paths, reduce style rules, split the request, or use a simpler representation.
CefSharp fails during startup
Cause: CefSharp was initialized too late, incompatible binaries were deployed, or the process architecture does not match the package. Fix: initialize once at application startup, deploy the required Chromium resources, and use the package/runtime combination documented for your target.
The output is too large
Cause: a large viewport, high scale, or lossless PNG contains many pixels. Fix: capture only the required rectangle, lower scale, or use JPEG when text and line art do not require lossless output.
7. Performance, reliability, and cost notes
- A browser capture carries Chromium startup and rendering overhead. Reuse a browser process for batches, but isolate pages and dispose them when finished.
- Static API calls are simpler for workers and queues because the response is already an image and no browser process is needed.
- Bound every navigation, readiness wait, and HTTP request with a timeout. Record the URL, dimensions, format, and failure reason for retries.
- Retry transient network failures with a small capped backoff. Do not retry authentication or invalid-parameter errors unchanged.
- Cache identical Static API requests when your terms and application requirements permit it. Keep credentials server-side.
- Google Maps Static API usage is a billed Google service and requires billing configuration. CefSharp itself is a library, but the pages and map services it loads can have their own usage and policy requirements.
8. FAQ
Can I save a Google Map without rendering a browser?
Yes. Use the Google Maps Static API and save its image response. It cannot reproduce arbitrary interactive page state or overlays that are not represented by API parameters.
Which format should I choose?
Use PNG for crisp labels and line work. Use JPEG when a smaller lossy file is acceptable. Follow the format support documented by the API or your CefSharp version.
Can I capture the entire world map in one image?
A browser screenshot is limited to the rendered viewport, and Static API requests have documented size and URL constraints. Design a bounded image or a compliant tiling workflow instead of extracting map content.
Do saved images need Google attribution?
Keep the attribution supplied by Google and follow the current Maps Platform policies and terms for your use case.


