Taking a screenshot of a URL in Java is harder than it should be. The standard answer — Selenium WebDriver with ChromeDriver — introduces a chain of fragile dependencies: a matching Chrome binary, a matching ChromeDriver binary, platform-specific paths, and a headless server that leaks memory under load.
This guide covers both approaches. You will see what running Selenium actually looks like at scale, where it breaks, and how to replace 80+ lines of driver boilerplate with a single HTTP call to the SnapSharp screenshot API.
The Selenium WebDriver Approach
Selenium WebDriver with ChromeDriver is the most common Java solution for browser automation and screenshots:
import org.openqa.selenium.By;
import org.openqa.selenium.OutputType;
import org.openqa.selenium.TakesScreenshot;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
public class SeleniumScreenshot {
public static byte[] screenshotWithSelenium(String url) throws IOException {
System.setProperty("webdriver.chrome.driver", "/usr/local/bin/chromedriver");
ChromeOptions options = new ChromeOptions();
options.addArguments("--headless=new");
options.addArguments("--no-sandbox");
options.addArguments("--disable-dev-shm-usage");
options.addArguments("--disable-gpu");
options.addArguments("--window-size=1280,720");
WebDriver driver = new ChromeDriver(options);
try {
driver.get(url);
Thread.sleep(2000); // Wait for page load — unreliable
File tempFile = ((TakesScreenshot) driver).getScreenshotAs(OutputType.FILE);
return Files.readAllBytes(tempFile.toPath());
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new RuntimeException("Screenshot interrupted", e);
} finally {
driver.quit(); // Must close or you leak browser processes
}
}
}This works locally. In production it breaks in predictable ways.
What breaks in production
Driver version mismatch. Chrome updates automatically; ChromeDriver does not. After any Chrome auto-update your tests throw SessionNotCreatedException: Chrome version must be between 114 and 119. You need WebDriverManager or manual versioning to stay in sync.
Docker image size. A headless Chrome Docker image adds 300–500 MB. That is per service. Multiply by your replica count and cloud storage costs add up fast.
Concurrency. Each new ChromeDriver() spawns a full browser process. Under concurrent load you hit memory pressure quickly — each Chrome instance uses 300–600 MB RAM. You need a pool, queuing logic, and health checks to keep it stable.
Thread.sleep for page load. The 2-second sleep above is wrong for slow pages and wasteful for fast ones. The correct approach is explicit waits (WebDriverWait), but that requires knowing which element signals readiness — different for every site.
CI/CD friction. GitHub Actions, CircleCI, and most CI environments require extra setup steps to install Chrome + ChromeDriver at matching versions. The --no-sandbox flag required in containers is a security trade-off.
The Screenshot API Approach
The SnapSharp screenshot API runs a managed Chromium pool on the server side. Your Java code makes an HTTP request and receives a PNG or JPEG — no browser binaries required.
Using Java's built-in HttpClient (Java 11+)
import java.io.IOException;
import java.net.URI;
import java.net.URLEncoder;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
public class SnapSharpScreenshot {
private static final String API_KEY = System.getenv("SNAPSHARP_API_KEY");
private static final String API_BASE = "https://api.snapsharp.dev/v1";
private static final HttpClient CLIENT = HttpClient.newHttpClient();
public static byte[] screenshot(String url) throws IOException, InterruptedException {
String encodedUrl = URLEncoder.encode(url, StandardCharsets.UTF_8);
URI uri = URI.create(API_BASE + "/screenshot?url=" + encodedUrl + "&width=1280&height=720&format=png");
HttpRequest request = HttpRequest.newBuilder()
.uri(uri)
.header("Authorization", "Bearer " + API_KEY)
.GET()
.build();
HttpResponse<byte[]> response = CLIENT.send(request, HttpResponse.BodyHandlers.ofByteArray());
if (response.statusCode() != 200) {
throw new RuntimeException("Screenshot API error: HTTP " + response.statusCode());
}
return response.body();
}
public static void main(String[] args) throws Exception {
byte[] png = screenshot("https://github.com");
Files.write(Path.of("screenshot.png"), png);
System.out.println("Saved screenshot.png (" + png.length + " bytes)");
}
}No Chrome binary. No ChromeDriver. No driver version matrix. One HTTP call.
Using OkHttp
If your project already uses OkHttp (common in Android and Spring Boot projects):
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
public class SnapSharpOkHttp {
private static final String API_KEY = System.getenv("SNAPSHARP_API_KEY");
private static final OkHttpClient CLIENT = new OkHttpClient();
public static byte[] screenshot(String url) throws IOException {
String apiUrl = "https://api.snapsharp.dev/v1/screenshot"
+ "?url=" + java.net.URLEncoder.encode(url, "UTF-8")
+ "&width=1280&height=720&format=png";
Request request = new Request.Builder()
.url(apiUrl)
.header("Authorization", "Bearer " + API_KEY)
.build();
try (Response response = CLIENT.newCall(request).execute()) {
if (!response.isSuccessful()) {
throw new IOException("Unexpected response code: " + response.code());
}
return response.body().bytes();
}
}
}Full-page screenshots
String apiUrl = "https://api.snapsharp.dev/v1/screenshot"
+ "?url=" + URLEncoder.encode(url, StandardCharsets.UTF_8)
+ "&full_page=true"
+ "&width=1280"
+ "&format=png";Full-page capture requires the Starter plan or higher.
Mobile device emulation
String apiUrl = "https://api.snapsharp.dev/v1/screenshot"
+ "?url=" + URLEncoder.encode(url, StandardCharsets.UTF_8)
+ "&device=iPhone+14"
+ "&format=png";Available devices: iPhone 14, Pixel 7, iPad Pro, Galaxy S21, MacBook Pro 13.
Async screenshot for batch jobs
For batch processing where you do not need an immediate response:
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
public class AsyncScreenshotJob {
public static String submitJob(String url) throws Exception {
String jsonBody = "{\"url\":\"" + url + "\",\"width\":1280,\"height\":720}";
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.snapsharp.dev/v1/async/screenshot"))
.header("Authorization", "Bearer " + System.getenv("SNAPSHARP_API_KEY"))
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(jsonBody))
.build();
HttpResponse<String> response = CLIENT.send(request, HttpResponse.BodyHandlers.ofString());
// Returns: {"job_id": "abc123", "status": "pending"}
return response.body();
}
}Async screenshots require the Growth plan or higher.
Comparison
| Selenium WebDriver | SnapSharp API | |
|---|---|---|
| Setup | Chrome + ChromeDriver binary | HTTP client only |
| Docker image overhead | +300–500 MB | None |
| Concurrent requests | ~5–10 before OOM | Unlimited (rate-limited by plan) |
| Version maintenance | Chrome/driver sync required | None |
| Full-page capture | Complex JS scroll + stitch | full_page=true |
| Mobile emulation | ChromeOptions per device | device=iPhone+14 |
| Caching | DIY | Built-in Redis, cache hits free |
| Cold start | 3–8s (Chrome launch) | 0ms (warm pool) |
Gradle dependency
If you use OkHttp, add to your build.gradle:
dependencies {
implementation 'com.squareup.okhttp3:okhttp:4.12.0'
}The java.net.http.HttpClient example requires no additional dependencies — it is built into Java 11+.
Getting your API key
- Create a free account — no credit card required
- Go to Dashboard → API Keys → Create key
- Set the environment variable:
export SNAPSHARP_API_KEY=sk_...
The free tier includes 100 screenshots/month. The Starter plan provides 5,000/month with full-page, retina, and device emulation support.