Skip to main content

Screenshot URL

Converts a target URL into an image using Headless Chromium.

This route works by simulating a standard browser navigation: it connects to the URL, executes the necessary JavaScript, loads assets (CSS, images, fonts), and captures the final state of the DOM as a PDF. It supports Single Page Applications (SPAs) and dynamic content rendering.

Configuration

You can configure the Chromium module behavior. See the Chromium module configuration for details.

Basics

POST/forms/chromium/screenshot/url
Gotenberg-Output-Filenamestring
The filename of the resulting file - Gotenberg automatically appends the file extension. Defaults to a random UUID filename.
Gotenberg-Tracestring
A custom request ID to identify the request in the logs; overrides the default UUID.
urlstringrequired
URL of the page to convert into PDF.
cURL
curl \
--request POST http://localhost:3000/forms/chromium/screenshot/url \
--form url=https://my.url \
-o my.jpeg
The screenshot has been successfully created.
Content-Disposition: attachment; filename={output-filename.ext}
Content-Type: {content-type}
Content-Length: {content-length}
Gotenberg-Trace: {trace}
Body: {output-file}

Rendering Behavior

widthnumber
The device screen width in pixels.
Default:800
heightnumber
The device screen height in pixels.
Default:600
clipboolean
Define whether to clip the screenshot according to the device dimensions.
Default:false
formatenum
The image compression format. Options: 'png', 'jpeg', 'webp'.
Default:png
qualitynumber
The compression quality (0-100). Only used if format is 'jpeg'.
Default:100
omitBackgroundboolean
Hides the default white background to allow transparency.
Default:false
optimizeForSpeedboolean
Optimizes image encoding for speed rather than resulting file size.
Default:false
emulatedMediaTypeenum
Media type to emulate ('screen' or 'print').
Default:print
waitDelaystring
Duration to wait before converting (e.g. '5s').
Default:None
waitForExpressionstring
Waits until this JavaScript expression returns true.
Default:None
waitForSelectorstring
Waits until the given selector (e.g., '#id') is visible on the page.
Default:None
cURL
curl \
--request POST http://localhost:3000/forms/chromium/screenshot/url \
--form url=https://my.url \
--form width=1280 \
--form height=720 \
--form format=jpeg \
--form quality=85 \
--form optimizeForSpeed=true \
-o my.jpeg

Viewport & Layout

The viewport defines the image. Unlike PDFs which paginate content, screenshots capture the browser's viewport exactly as rendered.

  • Resolution: Ensure you set the width and height form fields to match your desired target device (e.g., 1920x1080 for desktop, 375x812 for mobile).
  • If your screenshot's content is repeated and clipped, consider setting the skipNetworkIdleEvent form field to false (see issue #1065).
tip

If you are simulating a mobile device, remember to also set the userAgent to match a mobile browser, or the site might serve the desktop version.

Image Format

Choosing the right output format impacts quality and file size:

  • PNG: Best for screenshots containing text, UI elements, or flat colors. It is lossless, ensuring text remains sharp.
  • JPEG: Best for screenshots containing photographs or complex gradients. Use the quality form field to balance file size against visual fidelity (default is 100).
  • WebP: Offers a modern balance of compression and quality, usually superior to JPEG.

JavaScript & Dynamic Content

Chromium captures what is currently visible. If the page relies on JavaScript to render data, charts, or external content, the conversion might trigger before the rendering is complete, resulting in blank or incomplete sections.

Cheatsheets

If the content is generated dynamically:

  1. Use the waitDelay form field to add a fixed pause before conversion.
  2. For more precision, use the waitForExpression form field to trigger the conversion only when a specific JavaScript condition (e.g., window.status === 'ready') is met.

Wait Delay

Use this as a fallback when you cannot modify the target page's code. It forces Gotenberg to wait for a fixed duration before rendering, giving JavaScript time to finish execution.

Reliability Note

This method is "brute force". If the page loads faster, time is wasted. If it loads slower, the image will be incomplete. Use explicit waits (Expression or Selector) whenever possible.

cURL
curl \
--request POST http://localhost:3000/forms/chromium/screenshot/url \
--form url=https://my.url \
--form waitDelay=5s \
-o my.jpeg

Wait For Expression

This is the most robust method for synchronization. It pauses the conversion process until a specific JavaScript expression evaluates to true within the page context. This ensures the image is generated exactly when your data is ready.

Example: Client-side logic

// Inside your HTML page.
window.status = "loading";

fetchData().then(() => {
renderCharts();
// Signal to Gotenberg that the page is ready.
window.status = "ready";
});
cURL
curl \
--request POST http://localhost:3000/forms/chromium/screenshot/url \
--form url=https://my.url \
-- form 'waitForExpression=window.status === '\''ready'\''' \
-o my.jpeg

Wait For Selector

Ideally suited for Single Page Applications (SPAs) or frameworks like React/Vue. This method delays the conversion until a specific HTML element - identified by a CSS selector - appears in the DOM.

Example: Dynamic Element Injection

// Inside your HTML page.
await heavyCalculation();

const completionMarker = document.createElement("div");
completionMarker.id = "app-ready"; // The selector we wait for.
document.body.appendChild(completionMarker);
cURL
curl \
--request POST http://localhost:3000/forms/chromium/screenshot/url \
--form url=https://my.url \
--form 'waitForSelector=#app-ready' \
-o my.jpeg

HTTP & Networking

cookiesjson
Array of cookies to add to the request.
Default:None
extraHttpHeadersjson
Custom HTTP headers for the request.
Default:None
userAgentstring
Overrides the default User-Agent header.
Default:None
skipNetworkIdleEventboolean
Does not wait for Chromium network to be idle.
Default:true
failOnHttpStatusCodesjson
Returns a 409 Conflict if Gotenberg receives these codes for the main URL.
Default:[499,599]
failOnResourceHttpStatusCodesjson
Returns a 409 Conflict if Gotenberg receives these codes for at least one resource (images/css/etc.).
Default:None
ignoreResourceHttpStatusDomainsjson
Excludes resources from failOnResourceHttpStatusCodes checks based on their hostname.
Default:None
failOnResourceLoadingFailedboolean
Returns a 400 Bad Request if assets (images/css/etc.) fail to load due to network errors.
Default:false
cURL
curl \
--request POST http://localhost:3000/forms/chromium/screenshot/url \
--form url=https://my.url \
--form 'userAgent="Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko)"' \
--form skipNetworkIdleEvent=false \
--form failOnResourceLoadingFailed=true \
-o my.jpeg

Cookies

The cookies form field accepts a JSON-formatted array of cookie objects. It allows you to authenticate requests or maintain session state during the conversion process.

Cookie Object Schema

KeyDescriptionDefault
nameThe name of the cookie.Required
valueThe value of the cookie.Required
domainThe domain the cookie applies to (e.g., example.com).Required
pathThe URL path the cookie applies to.None
secureIf true, the cookie is only sent over HTTPS.None
httpOnlyIf true, the cookie is inaccessible to JavaScript (document.cookie).None
sameSiteControls cross-site behavior. Values: "Strict", "Lax", "None".None

Session Lifecycle

Cookies are transient and automatically expire when the request reaches its time limit.

To ensure strict isolation between conversions (preventing data leakage), you can configure the API to explicitly clear the cookie jar after every request. See API Configuration for details.

cURL
curl \
--request POST http://localhost:3000/forms/chromium/screenshot/url \
--form url=https://my.url \
--form 'cookies=[{"name":"yummy_cookie","value":"choco","domain":"theyummycookie.com"}]' \
-o my.jpeg

HTTP Headers

The extraHttpHeaders form field accepts a JSON-formatted object representing the HTTP headers to send with every request.

Schema

This is a key-value map where:

  • Key: The header name (e.g., Authorization, X-Custom-Header).
  • Value: The header value (string).
Scoped Headers

By default, headers are sent with every request made by the browser (including images, stylesheets, and scripts).

To restrict a header to specific URLs, append a ;scope= token containing a Regular Expression. Gotenberg will process this token and only send the header if the target URL matches the regex.

Example: "X-Internal-Token": "secret-123;scope=.*\\.internal\\.api"

  • Matches: https://data.internal.api/v1 (Header sent)
  • Ignores: https://google.com/fonts (Header NOT sent)

Note: The scope token is stripped before the header is sent to the server.

cURL
curl \
--request POST http://localhost:3000/forms/chromium/screenshot/url \
--form url=https://my.url \
--form-string 'extraHttpHeaders={"X-Header":"value","X-Scoped-Header":"value;scope=https?:\/\/([a-zA-Z0-9-]+\.)*domain\.com\/.*"}' \
-o my.jpeg

Invalid HTTP Status Codes

You can configure Gotenberg to return a 409 Conflict error if the main page or its resources return specific HTTP status codes.

These fields accept a JSON-formatted array of integers.

FieldDescription
failOnHttpStatusCodesFails if the main page URL returns a matching code.
failOnResourceHttpStatusCodesFails if any asset (image, CSS, script) returns a matching code.

Status Code Ranges

You can define ranges using the X99 notation:

  • 499 matches every code from 400 to 499.
  • 599 matches every code from 500 to 599.

Domain Exclusions

Use ignoreResourceHttpStatusDomains to prevent failures for specific third-party assets (e.g., analytics or tracking scripts that might fail without affecting the PDF).

Matching Rules: Gotenberg matches if the asset's hostname equals or is a subdomain of the entry.

Input values are automatically normalized (trimmed, lowercased, port/scheme removed):

  • example.com
  • *.example.com or .example.com
  • example.com:443 (port is ignored)
  • https://example.com/path (scheme/path are ignored)
cURL
curl \
--request POST http://localhost:3000/forms/chromium/screenshot/url \
--form url=https://my.url \
--form 'failOnHttpStatusCodes=[499,599]' \
--form 'failOnResourceHttpStatusCodes=[499,599]' \
--form 'ignoreResourceHttpStatusDomains=["sentry-cdn.com","analytics.example.com"]' \
-o my.jpeg
The main page or one of its resources returned an invalid status code.
Content-Type: text/plain; charset=UTF-8
Gotenberg-Trace: {trace}
Body: Invalid HTTP status code from the main page: 400: Bad Request

Network Errors

Gotenberg automatically validates the connection to the main page URL. If the browser encounters any of the following critical network errors, the API immediately returns 400 Bad Request to indicate the page could not be reached.

Critical Error List

  • net::ERR_CONNECTION_CLOSED
  • net::ERR_CONNECTION_RESET
  • net::ERR_CONNECTION_REFUSED
  • net::ERR_CONNECTION_ABORTED
  • net::ERR_CONNECTION_FAILED
  • net::ERR_NAME_NOT_RESOLVED
  • net::ERR_INTERNET_DISCONNECTED
  • net::ERR_ADDRESS_UNREACHABLE
  • net::ERR_BLOCKED_BY_CLIENT
  • net::ERR_BLOCKED_BY_RESPONSE
  • net::ERR_FILE_NOT_FOUND
  • net::ERR_HTTP2_PROTOCOL_ERROR
Strict Resource Validation

By default, if an image or stylesheet fails to load (e.g., a broken 404 image link), the PDF is still generated with a missing asset icon.

To force the conversion to fail completely when any resource encounters a network error, set the failOnResourceLoadingFailed form field to true.

Console

failOnConsoleExceptionsboolean
Returns a 409 Conflict response if there are exceptions in the Chromium console.
Default:false
cURL
curl \
--request POST http://localhost:3000/forms/chromium/screenshot/url \
--form url=https://my.url \
--form failOnConsoleExceptions=true \
-o my.jpeg
Exceptions in the Chromium console.
Content-Type: text/plain; charset=UTF-8
Gotenberg-Trace: {trace}
Body:

Chromium console exceptions:

exception "Uncaught" (17:10): Error: Exception 1
at file:///tmp/db09d2c8-31e3-4058-9923-c2705350f2b3/index.html:18:11;
exception "Uncaught" (20:10): Error: Exception 2
at file:///tmp/db09d2c8-31e3-4058-9923-c2705350f2b3/index.html:21:11: