Screenshot API for Java: HttpClient, Spring Boot & Async Capture
Capture screenshots, generate PDFs, and scrape web pages from Java applications using the built-in HttpClient. Works with Java 11+, Spring Boot, Quarkus, Micronaut, and plain Java — no additional dependencies required.
Screenshot API for Java — HttpClient Quickstart
Java 11 introduced java.net.http.HttpClient — a modern HTTP client that handles HTTPS, async requests, and streaming responses out of the box. No Apache HttpClient or OkHttp required. Here is a complete method that returns screenshot data as a byte[]:
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;
public class SnapApiClient {
private final HttpClient client;
private final String apiKey;
public SnapApiClient(String apiKey) {
this.apiKey = apiKey;
this.client = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(10))
.build();
}
public byte[] screenshot(String url) throws Exception {
String encodedUrl = URLEncoder.encode(url, StandardCharsets.UTF_8);
String endpoint = "https://api.snapapi.pics/v1/screenshot"
+ "?url=" + encodedUrl
+ "&full_page=true&format=jpeg&block_ads=true";
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(endpoint))
.header("X-Api-Key", apiKey)
.timeout(Duration.ofSeconds(30))
.GET()
.build();
HttpResponse<byte[]> response = client.send(
request, HttpResponse.BodyHandlers.ofByteArray());
if (response.statusCode() != 200) {
throw new RuntimeException("SnapAPI error: " + response.statusCode());
}
return response.body();
}
}
// Usage:
// SnapApiClient snap = new SnapApiClient(System.getenv("SNAPAPI_KEY"));
// byte[] jpeg = snap.screenshot("https://example.com");
// Files.write(Path.of("screenshot.jpg"), jpeg);
The HttpResponse.BodyHandlers.ofByteArray() handler accumulates the full binary response in memory. For large captures, use BodyHandlers.ofInputStream() to stream directly to a file without buffering.
Async Screenshot Capture with CompletableFuture
For non-blocking capture in reactive or async Java code, use sendAsync to get a CompletableFuture:
public CompletableFuture<byte[]> screenshotAsync(String url) {
String encodedUrl = URLEncoder.encode(url, StandardCharsets.UTF_8);
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.snapapi.pics/v1/screenshot?url=" + encodedUrl
+ "&full_page=true&format=jpeg"))
.header("X-Api-Key", apiKey)
.timeout(Duration.ofSeconds(30))
.GET()
.build();
return client.sendAsync(request, HttpResponse.BodyHandlers.ofByteArray())
.thenApply(response -> {
if (response.statusCode() != 200) {
throw new RuntimeException("SnapAPI " + response.statusCode());
}
return response.body();
});
}
// Capture multiple URLs in parallel:
List<CompletableFuture<byte[]>> futures = urls.stream()
.map(this::screenshotAsync)
.collect(Collectors.toList());
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
Parallel capture using CompletableFuture.allOf fires all requests concurrently and waits for the slowest one to complete. This is ideal for batch jobs where you need screenshots of many URLs and total wall-clock time matters.
Spring Boot Integration
In a Spring Boot application, register SnapApiClient as a @Service bean and inject it wherever screenshot capture is needed:
@Service
public class ScreenshotService {
private final SnapApiClient snapApi;
public ScreenshotService(@Value("${snapapi.key}") String apiKey) {
this.snapApi = new SnapApiClient(apiKey);
}
@Async
public CompletableFuture<byte[]> captureAsync(String url) {
return snapApi.screenshotAsync(url);
}
}
// In application.properties:
// snapapi.key=${SNAPAPI_KEY}
The @Async annotation runs the capture in Spring's task executor pool, freeing the web thread immediately. Enable async support with @EnableAsync on your configuration class. Return the CompletableFuture from your controller to let Spring MVC handle the async response automatically.
PDF Generation in Java
The PDF endpoint accepts a JSON body. Use HttpRequest.BodyPublishers.ofString with a JSON payload to convert any URL to a PDF:
public byte[] generatePdf(String url) throws Exception {
String body = "{\"url\":\"" + url + "\",\"page_size\":\"A4\",\"print_background\":true}";
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.snapapi.pics/v1/pdf"))
.header("X-Api-Key", apiKey)
.header("Content-Type", "application/json")
.timeout(Duration.ofSeconds(60))
.POST(HttpRequest.BodyPublishers.ofString(body))
.build();
HttpResponse<byte[]> response = client.send(
request, HttpResponse.BodyHandlers.ofByteArray());
if (response.statusCode() != 200) throw new RuntimeException("PDF error: " + response.statusCode());
return response.body();
}
For production use, replace string concatenation with a JSON library like Jackson: new ObjectMapper().writeValueAsString(Map.of("url", url, "page_size", "A4")). This handles escaping edge cases correctly when URLs contain query parameters with special characters.
Error Handling and Retry Logic in Java
Production Java services need exponential backoff for transient API failures. Implement retry logic with a simple loop that respects the Retry-After header on 429 responses:
public byte[] screenshotWithRetry(String url, int maxAttempts) throws Exception {
int attempt = 0;
while (attempt < maxAttempts) {
HttpResponse<byte[]> response = client.send(buildRequest(url),
HttpResponse.BodyHandlers.ofByteArray());
if (response.statusCode() == 200) return response.body();
if (response.statusCode() == 429) {
long retryAfter = response.headers().firstValueAsLong("Retry-After").orElse(5L);
Thread.sleep(retryAfter * 1000L);
} else if (response.statusCode() >= 500) {
Thread.sleep((long) Math.pow(2, attempt) * 1000L);
} else {
throw new RuntimeException("Non-retryable error: " + response.statusCode());
}
attempt++;
}
throw new RuntimeException("Max retry attempts reached");
}
This pattern retries on 429 and 5xx responses, using the Retry-After header value when available and falling back to exponential backoff for server errors. 4xx errors other than 429 are not retried since they indicate a problem with the request itself.
Register at snapapi.pics for 200 free captures per month — no credit card required. The API works with Java 11 or later and requires zero additional dependencies when using the built-in HttpClient.