ScreenshotNeo

BlogHow-to

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.

By the ScreenshotNeo team1 October 20268 min read

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:

  1. Navigation completion (WaitForInitialLoadAsync).
  2. A short, bounded delay for the first tile batch when necessary.
  3. A DOM or JavaScript condition that confirms the map container exists.
  4. 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.