Screenshot API Java Guide 2026

Integrate SnapAPI screenshot and PDF capture into Java applications with HttpClient, Spring RestTemplate, CompletableFuture, and Spring Boot REST controllers.

Get Free API Key

Calling SnapAPI from Java

Java 11's built-in HttpClient provides a clean async API for making HTTP requests without third-party dependencies. SnapAPI accepts standard Bearer token authentication and returns binary image bytes or JSON, both of which HttpClient handles well. For Spring Boot applications, RestTemplate and WebClient are the idiomatic choices. The examples below cover both the standard library HttpClient for plain Java applications and Spring's HTTP clients for Spring Boot applications.

// Java 11 HttpClient: basic screenshot capture
import java.net.URI;
import java.net.http.*;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.nio.file.*;

public class SnapApiExample {
    private static final String API_KEY = System.getenv("SNAPAPI_KEY");
    private static final String BASE = "https://api.snapapi.pics";
    private static final HttpClient client = HttpClient.newHttpClient();

    public static byte[] screenshot(String url, String format) throws Exception {
        String encoded = URLEncoder.encode(url, StandardCharsets.UTF_8);
        HttpRequest req = HttpRequest.newBuilder()
            .uri(URI.create(BASE + "/screenshot?url=" + encoded + "&format=" + format + "&full_page=true"))
            .header("Authorization", "Bearer " + API_KEY)
            .GET()
            .build();

        HttpResponse<byte[]> resp = client.send(req, HttpResponse.BodyHandlers.ofByteArray());
        if (resp.statusCode() != 200) {
            throw new RuntimeException("API error: " + resp.statusCode());
        }
        return resp.body();
    }

    public static void main(String[] args) throws Exception {
        byte[] png = screenshot("https://example.com", "png");
        Files.write(Path.of("screenshot.png"), png);
        System.out.println("Saved " + png.length + " bytes");
    }
}

Async Screenshot with CompletableFuture

Java's CompletableFuture enables non-blocking concurrent screenshot capture. Use HttpClient's sendAsync method to initiate multiple screenshot requests concurrently and collect results with CompletableFuture.allOf. This is significantly more efficient than sequential captures for batch screenshot jobs: capturing ten pages concurrently takes the time of the slowest single capture rather than the sum of all captures.

import java.net.URI;
import java.net.http.*;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.*;

public class BatchScreenshot {
    private static final String API_KEY = System.getenv("SNAPAPI_KEY");
    private static final HttpClient client = HttpClient.newHttpClient();

    public static CompletableFuture<byte[]> screenshotAsync(String url) {
        try {
            String enc = URLEncoder.encode(url, StandardCharsets.UTF_8);
            HttpRequest req = HttpRequest.newBuilder()
                .uri(URI.create("https://api.snapapi.pics/screenshot?url=" + enc + "&format=png"))
                .header("Authorization", "Bearer " + API_KEY)
                .GET().build();
            return client.sendAsync(req, HttpResponse.BodyHandlers.ofByteArray())
                .thenApply(resp -> {
                    if (resp.statusCode() != 200) throw new RuntimeException("Error: " + resp.statusCode());
                    return resp.body();
                });
        } catch (Exception e) {
            return CompletableFuture.failedFuture(e);
        }
    }

    public static void main(String[] args) throws Exception {
        List<String> urls = List.of(
            "https://example.com",
            "https://github.com",
            "https://stackoverflow.com"
        );

        List<CompletableFuture<byte[]>> futures = urls.stream()
            .map(BatchScreenshot::screenshotAsync)
            .toList();

        CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();

        for (int i = 0; i < futures.size(); i++) {
            byte[] bytes = futures.get(i).get();
            System.out.printf("URL %d: %d bytes%n", i, bytes.length);
        }
    }
}

Spring Boot Integration

In Spring Boot applications, inject the SnapAPI key from application properties and use RestTemplate or WebClient to make screenshot requests. Define a ScreenshotService bean that encapsulates the API calls, and inject it into controllers and service classes as needed. Use RestTemplate for synchronous use cases and WebClient for reactive Spring WebFlux applications.

// Spring Boot: ScreenshotService with RestTemplate
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.*;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;

@Service
public class ScreenshotService {

    @Value("${snapapi.key}")
    private String apiKey;

    private final RestTemplate restTemplate = new RestTemplate();

    public byte[] capture(String url, String format) {
        URI uri = UriComponentsBuilder
            .fromHttpUrl("https://api.snapapi.pics/screenshot")
            .queryParam("url", url)
            .queryParam("format", format)
            .queryParam("full_page", "true")
            .build().toUri();

        HttpHeaders headers = new HttpHeaders();
        headers.setBearerAuth(apiKey);
        HttpEntity<Void> entity = new HttpEntity<>(headers);

        ResponseEntity<byte[]> resp = restTemplate.exchange(uri, HttpMethod.GET, entity, byte[].class);
        if (!resp.getStatusCode().is2xxSuccessful()) {
            throw new RuntimeException("Screenshot failed: " + resp.getStatusCode());
        }
        return resp.getBody();
    }
}

// Spring MVC Controller: screenshot proxy endpoint
@RestController
@RequestMapping("/api")
public class ScreenshotController {

    private final ScreenshotService screenshotService;

    public ScreenshotController(ScreenshotService screenshotService) {
        this.screenshotService = screenshotService;
    }

    @GetMapping("/screenshot")
    public ResponseEntity<byte[]> screenshot(@RequestParam String url) {
        byte[] bytes = screenshotService.capture(url, "png");
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.IMAGE_PNG);
        headers.setCacheControl("public, max-age=3600");
        return new ResponseEntity<>(bytes, headers, HttpStatus.OK);
    }
}

Scheduled Monitoring with @Scheduled

Spring's @Scheduled annotation runs methods on a configured schedule without external job infrastructure. Combine it with ScreenshotService to build a website monitoring job that captures screenshots of monitored URLs on a regular interval, stores them in S3 using the AWS SDK for Java, and compares them against previous captures using a pixel comparison library. Define the monitored URL list in application properties or a database table, inject it into the monitoring service, and schedule the capture job to run every 30 minutes or on a custom cron expression.

@Service
public class MonitoringService {

    private final ScreenshotService screenshotService;
    private final MonitoredUrlRepository urlRepo;
    private final S3Client s3;

    public MonitoringService(ScreenshotService ss, MonitoredUrlRepository repo, S3Client s3) {
        this.screenshotService = ss;
        this.urlRepo = repo;
        this.s3 = s3;
    }

    @Scheduled(fixedDelay = 1800000) // every 30 minutes
    public void captureAll() {
        urlRepo.findAll().forEach(u -> {
            try {
                byte[] bytes = screenshotService.capture(u.getUrl(), "png");
                String key = "screenshots/" + u.getId() + "/" + System.currentTimeMillis() + ".png";
                s3.putObject(r -> r.bucket("my-bucket").key(key),
                    software.amazon.awssdk.core.sync.RequestBody.fromBytes(bytes));
                System.out.println("Captured: " + u.getUrl());
            } catch (Exception e) {
                System.err.println("Failed: " + u.getUrl() + " — " + e.getMessage());
            }
        });
    }
}

Error Handling and Retry in Java

Java applications calling external APIs need explicit retry logic for transient failures. Use Spring Retry's @Retryable annotation on the ScreenshotService.capture method to automatically retry on RuntimeException with exponential backoff: annotate with @Retryable(value = RuntimeException.class, maxAttempts = 3, backoff = @Backoff(delay = 1000, multiplier = 2)) and add @EnableRetry to your application configuration class. For HttpClient-based (non-Spring) applications, implement retry in a loop: attempt the request up to three times, catch IOException and http error status codes that indicate transient failures (429, 503), sleep with Thread.sleep using exponential delay, and re-throw the exception after the maximum number of attempts is exhausted. Log each retry attempt with the attempt number and delay to make retry behavior visible in application logs. Distinguish between retryable errors and non-retryable errors: a 400 Bad Request indicates a problem with the request parameters that will not be resolved by retrying, while a 503 Service Unavailable is a candidate for retry with backoff.

Testing Java Screenshot Code

Testing Java code that calls SnapAPI requires mocking the HTTP client to avoid making real API calls during unit tests. For Spring Boot applications, use MockRestServiceServer to intercept RestTemplate calls and return configured responses without making actual HTTP requests. Create a MockRestServiceServer from your RestTemplate bean in the test class, set up expectations for the screenshot endpoint URL and return a byte array response, and verify that the response was handled correctly by your service. For non-Spring Java applications, use WireMock, which provides a standalone mock HTTP server that runs in the test JVM and responds to configured request patterns with configurable responses. WireMock's API allows precise configuration of response status codes, headers, and bodies, enabling you to test success paths, error handling, and timeout behavior without any real network calls. For integration tests that verify real API behavior, annotate them with a custom JUnit condition that checks for the SNAPAPI_KEY environment variable and skips the test when the key is not available, so that integration tests run in CI with the real key but are skipped in local development without it.

PDF Generation in Java

Generating PDFs from web content in Java traditionally requires heavyweight libraries like iText, Apache PDFBox, or a local wkhtmltopdf binary. SnapAPI eliminates these dependencies by providing PDF generation as an API endpoint: pass a URL or HTML content, receive a PDF byte array in response. For invoice generation in Spring Boot, define a PDF service that calls the screenshot endpoint with format=pdf, receives the PDF bytes, and either stores them in S3 or returns them directly in an HTTP response as an application/pdf content type. For Spring MVC endpoints that serve PDF downloads, use ResponseEntity with byte[] body and MediaType.APPLICATION_PDF content type, setting Content-Disposition to attachment with a filename to trigger a browser download. This approach requires no local PDF library dependencies, produces accurate visual PDFs from any web content including those with complex CSS layouts, and works identically in all deployment environments without the binary dependency issues that wkhtmltopdf introduces in containerized environments.

Java Best Practices for Screenshot APIs

Several Java-specific patterns improve the reliability and maintainability of SnapAPI integrations. Always configure explicit connection and read timeouts on your HTTP client: Java's HttpClient.Builder allows setting a timeout with .connectTimeout(Duration.ofSeconds(10)) for the connect phase and .timeout(Duration.ofSeconds(60)) on individual requests to prevent indefinitely hanging threads. Store the API key in environment variables and retrieve it with System.getenv rather than hardcoding it in source files or application properties committed to version control. Use a dedicated thread pool executor for concurrent screenshot operations rather than the fork-join common pool to avoid starving other application threads. Define a SnapApiException class that wraps HTTP error responses with the status code and error message, enabling callers to handle API errors with precise catch clauses rather than catching generic RuntimeException. Annotate the ScreenshotService class with Spring's @Cacheable annotation for endpoint methods that accept a URL parameter, enabling automatic result caching through Spring's caching abstraction backed by Redis or Caffeine. These patterns follow standard Java and Spring idioms, making the SnapAPI integration readable and maintainable for any Java developer joining the project.