ScreenshotNeo

BlogHow-to

Save Selenium Webpage Screenshots with Timestamps and URL Names in Java

Save Selenium screenshots with filenames built from the page host, path, and a UTC timestamp. Includes runnable Java code, safe naming, and troubleshooting.

By the ScreenshotNeo team4 October 20268 min read

Use Selenium’s TakesScreenshot interface to capture the current browsing context, then build the output filename yourself from a sanitized URL label and an explicit UTC timestamp. Selenium captures the image; your Java code chooses the name and where to save it.

The example below uses Java 11 or later, Selenium 4, and ChromeDriver. It creates a name such as example-com-products-widget_20261003T203416Z.png, creates the output directory if needed, and avoids overwriting an existing file by adding a numeric suffix.

1. Add Selenium and prepare Chrome

Add Selenium Java to your project. With Maven, declare the Selenium Java dependency using the version already approved for your project. Selenium Manager can manage a compatible browser driver in supported Selenium releases; alternatively, configure ChromeDriver through your environment. See Selenium’s official WebDriver documentation for the capture pattern and browser setup guidance.

This standalone class assumes Selenium is on the classpath and that Chrome is available:

import java.io.IOException;
import java.net.URI;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.time.Instant;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.Locale;
import java.util.regex.Pattern;

import org.openqa.selenium.OutputType;
import org.openqa.selenium.TakesScreenshot;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;

public class NamedScreenshot {
    private static final DateTimeFormatter UTC_STAMP =
            DateTimeFormatter.ofPattern("uuuuMMdd'T'HHmmssSSS'Z'", Locale.ROOT)
                    .withZone(ZoneOffset.UTC);
    private static final Pattern UNSAFE = Pattern.compile("[^A-Za-z0-9._-]+");

    public static void main(String[] args) throws IOException {
        String target = args.length > 0 ? args[0] : "https://example.com/products/widget";
        Path outputDirectory = Path.of("screenshots");
        Files.createDirectories(outputDirectory);

        WebDriver driver = new ChromeDriver();
        try {
            driver.get(target);
            // Replace this with a wait for the page state your test actually needs.
            Path destination = uniqueDestination(outputDirectory, target, Instant.now());
            Path temporaryScreenshot = ((TakesScreenshot) driver)
                    .getScreenshotAs(OutputType.FILE).toPath();
            Files.copy(temporaryScreenshot, destination, StandardCopyOption.COPY_ATTRIBUTES);
            System.out.println("Saved screenshot: " + destination.toAbsolutePath());
        } finally {
            driver.quit();
        }
    }

    private static Path uniqueDestination(Path directory, String url, Instant capturedAt)
            throws IOException {
        String label = urlLabel(url);
        String stamp = UTC_STAMP.format(capturedAt);
        String base = label + "_" + stamp;
        Path candidate = directory.resolve(base + ".png");
        int suffix = 1;
        while (Files.exists(candidate)) {
            candidate = directory.resolve(base + "_" + suffix++ + ".png");
        }
        return candidate;
    }

    private static String urlLabel(String value) {
        URI uri = URI.create(value);
        String host = uri.getHost();
        if (host == null || host.isBlank()) {
            throw new IllegalArgumentException("URL must be absolute and include a host: " + value);
        }
        String path = uri.getRawPath();
        String combined = host + (path == null ? "" : path);
        String safe = UNSAFE.matcher(combined).replaceAll("-")
                .replaceAll("^[._-]+|[._-]+$", "");
        return safe.isBlank() ? "page" : safe;
    }
}

Run it with a URL argument, for example java NamedScreenshot https://example.com/products/widget. The timestamp is generated after navigation returns. If the page loads important content asynchronously, wait for that content before taking the screenshot.

2. How the URL-and-time filename is built

  1. Parse the URL. URI extracts the host and raw path. The code deliberately leaves out query parameters and fragments.
  2. Sanitize the label. Characters outside letters, numbers, dot, underscore, and hyphen become hyphens. This avoids treating URL separators or reserved punctuation as filesystem path components.
  3. Format UTC explicitly. The suffix includes milliseconds and a literal Z, so names are comparable across machines and unambiguously indicate UTC.
  4. Choose a collision policy. The loop adds _1, _2, and so on if a name already exists. This is suitable for a single writer; concurrent writers should use a unique run identifier or an atomic file-creation strategy.
  5. Keep extension and format aligned. Selenium’s OutputType.FILE screenshot is saved as PNG here, so the destination ends in .png.
Decision Recommended default When to change it
URL parts in the name Host and path only Include a query value only when it is necessary and known not to contain secrets or personal data.
Timestamp zone UTC, marked with Z Use local time only for a workflow that requires it, and label the zone to avoid ambiguity.
Timestamp precision Milliseconds Add a unique test or job ID when multiple captures can happen within the same millisecond.
Collision behavior Suffix the filename Fail instead when overwriting or silently suffixing would hide a test failure.
Screenshot scope Driver’s current browsing context Use element capture for one component; verify browser and driver support before depending on full-page output.

3. Wait for the page state you need

driver.get() returning does not guarantee that every application-specific asynchronous element is ready. Wait for a meaningful condition before capture. For example, to wait for a results panel:

import java.time.Duration;
import org.openqa.selenium.By;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;

new WebDriverWait(driver, Duration.ofSeconds(15))
        .until(ExpectedConditions.visibilityOfElementLocated(By.cssSelector("main .results")));

Path screenshot = ((TakesScreenshot) driver)
        .getScreenshotAs(OutputType.FILE).toPath();

Choose a selector that represents the state the test cares about. A fixed sleep is simpler but slower when the page is fast and still unreliable when it is slow. Avoid capturing immediately after navigation if fonts, images, client-rendered content, or animations affect the expected result.

4. Capture a single element or a full page

The driver-level call captures the current browsing context. Selenium’s documentation also shows taking a screenshot from a particular element. If the goal is a card, chart, or component, capture that element rather than relying on viewport dimensions to frame it:

import org.openqa.selenium.WebElement;

WebElement card = driver.findElement(By.cssSelector(".product-card"));
Path elementFile = card.getScreenshotAs(OutputType.FILE).toPath();
Files.copy(elementFile, outputDirectory.resolve("product-card.png"));

Full-page screenshots are not a universal promise of the driver-level API. The Selenium API describes a screenshot of the current browsing context; browser and driver capabilities determine whether output extends beyond the visible viewport. Confirm the behavior for the browser/version in your environment if the complete document is required. See the TakesScreenshot API reference.

5. cURL, Python, and Node.js alternatives

These alternatives are useful when the task is to request an image from a screenshot service rather than drive a browser test. Selenium remains appropriate when you need browser automation and assertions in the same Java workflow.

cURL

curl -G "https://api.screenshotneo.com/v1/shot" \
  -d access_key=YOUR_API_KEY \
  --data-urlencode url=https://example.com/products/widget \
  -o shot.webp

Python

import requests

r = requests.get(
    "https://api.screenshotneo.com/v1/shot",
    params={"access_key": "YOUR_API_KEY", "url": "https://example.com/products/widget"},
    timeout=90,
)
r.raise_for_status()
open("shot.webp", "wb").write(r.content)

Node.js

const q = new URLSearchParams({
  access_key: 'YOUR_API_KEY',
  url: 'https://example.com/products/widget'
});
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
const bytes = Buffer.from(await res.arrayBuffer());
await import('node:fs/promises').then(fs => fs.writeFile('shot.webp', bytes));

Keep API keys out of source control; load them from an environment variable or secret manager in deployed code. The examples use the documented ScreenshotNeo endpoint and request pattern. See the ScreenshotNeo API documentation for options and response details.

6. Or skip the browser setup

ScreenshotNeo can return a screenshot with one GET request, so a Java browser and driver are not needed for a standalone capture:

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://example.com/products/widget -o shot.webp

ScreenshotNeo accepts cookie and consent banners like a visitor, then removes more than 60 known consent platforms, newsletter popups, and chat widgets; each cleanup step can be turned off. Bot checks, blank pages, failed loads, timeouts, and cache hits cost nothing, and response headers identify the page verdict and billing status. Its MCP server provides screenshot tools for AI agents, and the free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000 screenshots. Every feature is on every plan. Read the API docs, then sign up for 1,000 free screenshots a month, no card required.

7. Troubleshooting

Symptom Likely cause Fix
ClassCastException at TakesScreenshot The selected driver does not implement screenshot capture. Use a browser driver that supports the Selenium screenshot interface and check the driver/browser setup.
Screenshot is blank or missing page content Capture happened before the relevant asynchronous state rendered, or navigation failed. Wait for a page-specific element, inspect navigation and browser logs, and capture only after the expected state appears.
Filename contains an unexpected label URL parsing can differ for relative URLs, internationalized hosts, or unusual path encodings. Require an absolute URL with a host, normalize it deliberately, and add tests for the URL forms your application accepts.
Names collide or files overwrite Timestamp precision is insufficient for concurrent or rapid captures, or the save operation replaces a file. Include a run/job ID and create files atomically with a defined collision policy.
Directory or copy error The process cannot write to the destination, or the directory does not exist. Create the directory, verify permissions and available disk space, and log the absolute destination.
Browser process remains after an error Driver cleanup did not run. Keep browser use in a try/finally and call quit() in the finally block.
Output is only the visible area Driver-level capture is not guaranteed to mean full-document capture. Use element capture for a component or verify a browser-specific full-page method for the required browser and version.

8. Performance, reliability, and storage

  • Wait on conditions, not long fixed delays. A targeted wait avoids capturing early while not forcing every fast page to wait the same arbitrary interval.
  • Limit parallel browser sessions. Each WebDriver has browser-process and memory costs. For parallel test runs, assign separate output paths and unique run identifiers.
  • Keep filenames useful but non-sensitive. Query strings can contain tokens, email addresses, or search terms. Omitting query and fragment data reduces leakage and keeps names manageable.
  • Choose retention intentionally. Screenshots can grow into a large artifact collection. Store only what tests need, and apply the retention policy of your CI or artifact store.
  • Record context alongside artifacts. When debugging, preserve the test/run ID, browser version, target URL (in a suitably protected log), and capture time as structured metadata.
  • Do not infer full-page coverage from a successful file save. Check image dimensions or a known lower-page element when full-document coverage matters.

9. FAQ

Does Selenium add the timestamp or URL to the filename?

No. Selenium returns screenshot data or a temporary file; your application constructs the destination name.

Why use UTC in the filename?

UTC avoids ambiguity when artifacts from machines in different time zones are compared. The Z suffix marks the chosen convention.

Should the query string be included?

Usually not. It can make names unwieldy and may expose sensitive values. Include a selected, sanitized value only when it is essential.

Can I save JPEG instead of PNG?

The Selenium example shown returns a PNG screenshot. Keep the extension consistent with the actual bytes; renaming a PNG file to .jpg does not convert its format.

Can I use the name as a complete audit record?

No. A filename is useful for sorting and discovery, but store additional test metadata separately when you need reproducible audit context.