Capture any website as PNG, JPEG, WebP, or PDF with one HttpClient request. No Selenium, no WebDriver, no Chrome on the JVM host. Works in Spring Boot and plain Java 11+.
200 free screenshots/month. No credit card required.
The whole API is a GET request. Your key goes in the X-API-Key header, options go in query parameters, the image comes back as the response body.
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;
import java.time.Duration;
public class Screenshot {
public static void main(String[] args) throws Exception {
String target = URLEncoder.encode("https://example.com", StandardCharsets.UTF_8);
URI uri = URI.create("https://app.snap-render.com/v1/screenshot?url=" + target
+ "&format=png&width=1280&height=800");
HttpClient client = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(10))
.build();
HttpRequest request = HttpRequest.newBuilder(uri)
.header("X-API-Key", System.getenv("SNAPRENDER_API_KEY"))
.timeout(Duration.ofSeconds(60))
.GET()
.build();
HttpResponse<byte[]> response = client.send(request, HttpResponse.BodyHandlers.ofByteArray());
if (response.statusCode() != 200) {
// Body is JSON: {"error":{"code":"...","message":"...","status":429}}
throw new RuntimeException("SnapRender " + response.statusCode() + ": "
+ new String(response.body(), StandardCharsets.UTF_8));
}
Files.write(Path.of("screenshot.png"), response.body());
}
}
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.time.Duration;
import java.util.Map;
import java.util.stream.Collectors;
public final class SnapRenderClient {
private static final String ENDPOINT = "https://app.snap-render.com/v1/screenshot";
private final HttpClient http = HttpClient.newHttpClient();
private final String apiKey;
public SnapRenderClient(String apiKey) { this.apiKey = apiKey; }
public byte[] capture(String url, Map<String, String> options) throws Exception {
String query = options.entrySet().stream()
.map(e -> e.getKey() + "=" + URLEncoder.encode(e.getValue(), StandardCharsets.UTF_8))
.collect(Collectors.joining("&"));
HttpRequest request = HttpRequest.newBuilder(URI.create(ENDPOINT + "?url="
+ URLEncoder.encode(url, StandardCharsets.UTF_8) + "&" + query))
.header("X-API-Key", apiKey)
.timeout(Duration.ofSeconds(60))
.GET()
.build();
HttpResponse<byte[]> response = http.send(request, HttpResponse.BodyHandlers.ofByteArray());
int status = response.statusCode();
if (status == 200) {
response.headers().firstValue("X-Cache").ifPresent(v -> System.out.println("cache: " + v));
return response.body();
}
String body = new String(response.body(), StandardCharsets.UTF_8);
if (status == 429) {
// RATE_LIMITED: per-minute burst, retry after a minute. QUOTA_EXCEEDED: monthly limit, do not retry.
throw new IllegalStateException("Rate limit or quota: " + body);
}
if (status == 400 || status == 401) {
// INVALID_URL, BLOCKED_URL, or a bad key: fix the input, do not retry.
throw new IllegalArgumentException(body);
}
throw new RuntimeException("SnapRender " + status + ": " + body);
}
}
// Usage
byte[] jpeg = new SnapRenderClient(System.getenv("SNAPRENDER_API_KEY")).capture(
"https://example.com",
Map.of("format", "jpeg", "quality", "85", "full_page", "true",
"device", "iphone_15_pro", "dark_mode", "true",
"cache", "true", "cache_ttl", "3600"));
@GetMapping(value = "/reports/{id}/pdf", produces = MediaType.APPLICATION_PDF_VALUE)
public ResponseEntity<byte[]> reportPdf(@PathVariable String id) throws Exception {
byte[] pdf = snapRender.capture(
"https://example.com/reports/" + id,
Map.of("format", "pdf", "full_page", "true", "width", "1024"));
return ResponseEntity.ok()
.header(HttpHeaders.CONTENT_DISPOSITION, "inline; filename=\"report-" + id + ".pdf\"")
.body(pdf);
}
Ads and cookie banners are blocked by default. The PDF is a rendered page image, not a print-stylesheet layout. Full parameter reference in the docs.
Selenium WebDriver is built for driving a browser through test scenarios. Using it to produce an image of a URL in a production service means shipping Chrome and chromedriver alongside the JVM, keeping their versions aligned, and accepting a browser process that takes hundreds of megabytes next to your heap. On a container platform that is a bigger image, slower scaling, and a class of OOM kills that have nothing to do with your code.
With a screenshot API the browser runs on our side. Java sends one request with the HttpClient that has been in the JDK since version 11 and receives the finished bytes. Cookie banners and ads are removed, full-page capture handles lazy loading, and nothing in your build or Dockerfile changes.
The client above is synchronous. For high volume, use sendAsync and a bounded executor, or run captures from a scheduled job and store the bytes in S3 or your database. Repeat captures of the same URL within cache_ttl are served from the cache and are not billed.
Because the integration is a GET request, it runs anywhere the JVM can open an outbound HTTPS connection, including AWS Lambda Java runtimes, GraalVM native images, and distroless containers where a browser could never be installed.
| SnapRender API | Selenium in production | |
|---|---|---|
| Dependencies | java.net.http (JDK 11+) | selenium-java plus Chrome plus chromedriver |
| Container image | Unchanged, distroless OK | Hundreds of MB larger |
| Memory | Heap only | Browser process beside the JVM |
| Cookie banners and ads | Blocked by default | Custom code per site |
| Caching | cache=true, hits are free | Build it yourself |
| Browser updates | Handled for you | Yours to test and ship |
Want to see the output first? Try the free website screenshot tool. It uses the same rendering engine as the API.
Build a URI for the /v1/screenshot endpoint with the target in the url parameter, send a GET with java.net.http.HttpClient and your key in the X-API-Key header, and read the response body as a byte array. The page renders in real Chrome on SnapRender's servers, so the JVM never launches a browser.
No. HttpClient ships with Java 11 and later, so there is no Maven dependency, no chromedriver binary to match to a Chrome version, and no browser on the host. The same code works with OkHttp or Spring's RestClient if you prefer them.
Yes. Wrap the call in a @Service, read the key from application properties or an environment variable, and return the bytes from a controller with the right Content-Type, or run it from an @Async or @Scheduled method for background captures.
Yes. Pass format=pdf and the response body is a PDF of the rendered page. It is a page render rather than a print stylesheet layout, so use full_page=true for long pages. Combine it with width and dark_mode as needed.
Yes. The free plan includes 200 screenshots per month with every feature: all formats including PDF, full-page capture, device emulation, dark mode, and caching. No credit card is required to sign up.
The same API has quickstarts for other languages.
Related: HTML to PDF API and serverless runtimes.
Or skip the setup entirely
Everything in this guide is one GET request with SnapRender. No browser to babysit, no timeouts to tune.
200 screenshots a month free. First render in under a minute.
Grab a key, paste the class, done. 200 free screenshots per month, no credit card.