java.net.http only, no Maven dependency

Screenshot API
for Java

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.

Java quickstart: screenshot with HttpClient

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.

Screenshot.java (Java 11+)
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());
    }
}
Reusable client: full page, mobile device, dark mode, cached, error codes handled
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"));
Webpage to PDF from a Spring Boot controller
@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.

Take a screenshot in Java without Selenium

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.

Works in Spring Boot, Quarkus, Lambda, and distroless images

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.

Java screenshot API FAQ

How do I take a screenshot of a website in Java?+

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.

Do I need Selenium or WebDriver for this?+

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.

Does it work inside Spring Boot?+

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.

Can I generate a PDF of a webpage in Java?+

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.

Is there a free tier for the Java screenshot API?+

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.

Not writing Java today?

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.

One HttpClient call between you and the screenshot

Grab a key, paste the class, done. 200 free screenshots per month, no credit card.