Screenshot API for Java.
Shotium is a hosted screenshot API you call from Java with the built-in HttpClient (Java 11+): no Selenium, no WebDriver version matrix, no browser binaries in your containers. Send a URL, get back PNG, JPEG or WebP bytes — with full-page capture, template-based OG images, and HMAC-signed URLs you can embed in public HTML.
How do I take a screenshot in Java?
The JDK's own HttpClient — no Maven dependencies. Sign in, create an API key, and this runs as-is:
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.Duration;
var client = HttpClient.newHttpClient();
var req = HttpRequest.newBuilder()
.uri(URI.create("https://api.shotium.com/v1/screenshot?url=https://example.com&format=png"))
.header("Authorization", "Bearer " + System.getenv("SHOTIUM_KEY"))
.timeout(Duration.ofSeconds(60))
.build();
var res = client.send(req, HttpResponse.BodyHandlers.ofByteArray());
if (res.statusCode() != 200) {
throw new RuntimeException("render failed: " + res.statusCode());
}
Files.write(Path.of("shot.png"), res.body());Full-page screenshots and viewport control
Set full_page to capture the entire scroll height (up to 20,000px). Keep the 60-second request timeout — tall pages spend longer in the renderer:
var uri = URI.create(
"https://api.shotium.com/v1/screenshot"
+ "?url=https://news.ycombinator.com"
+ "&full_page=true&format=webp&quality=90&width=1280");Generate OG images from templates
POST typed parameters into one of five built-in templates and get a finished 1200×630 social card back — no HTML to write:
var body = """
{
"template": "blog",
"params": {"title": "Shipping fast without breaking things", "author": "Ada L."},
"format": "png"
}""";
var req = HttpRequest.newBuilder()
.uri(URI.create("https://api.shotium.com/v1/og-image"))
.header("Authorization", "Bearer " + System.getenv("SHOTIUM_KEY"))
.header("Content-Type", "application/json")
.timeout(Duration.ofSeconds(60))
.POST(HttpRequest.BodyPublishers.ofString(body))
.build();
var res = client.send(req, HttpResponse.BodyHandlers.ofByteArray());
Files.write(Path.of("og.png"), res.body());How do I embed OG images without exposing my API key?
Sign the query string with your signing secret instead of sending your key. URLEncoder needs three patches to be RFC 3986 (space, asterisk, tilde), and TreeMap gives byte-order keys:
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.HexFormat;
import java.util.TreeMap;
import java.util.stream.Collectors;
// RFC 3986: URLEncoder is close; fix space (+), asterisk and tilde
static String rfc3986(String s) {
return URLEncoder.encode(s, StandardCharsets.UTF_8)
.replace("+", "%20").replace("*", "%2A").replace("%7E", "~");
}
var params = new TreeMap<String, String>(); // TreeMap sorts keys byte-order
params.put("template", "minimal");
params.put("title", "Less, but better.");
params.put("uid", "YOUR-UID");
var canonical = params.entrySet().stream()
.map(e -> rfc3986(e.getKey()) + "=" + rfc3986(e.getValue()))
.collect(Collectors.joining("&"));
var mac = Mac.getInstance("HmacSHA256");
mac.init(new SecretKeySpec(
System.getenv("SHOTIUM_SIGNING_SECRET").getBytes(StandardCharsets.UTF_8), "HmacSHA256"));
var sig = HexFormat.of().formatHex(mac.doFinal(canonical.getBytes(StandardCharsets.UTF_8)));
var url = "https://api.shotium.com/v1/og-image?" + canonical + "&sig=" + sig;Error handling
Every error is RFC 9457 problem+json with a stable type field you can branch on. Failed renders are never billed:
var res = client.send(req, HttpResponse.BodyHandlers.ofString());
if (res.statusCode() != 200) {
// RFC 9457 problem+json: {"type": "...", "title": "...", "status": ...}
var body = res.body();
if (body.contains("\"rate_limited\"")) {
var wait = res.headers().firstValue("Retry-After").orElse("1");
// back off, then retry
} else if (body.contains("\"render_timeout\"")) {
// site took >30s — retry or skip
}
}Parameters at a glance
| Param | Default | Notes |
|---|---|---|
url | required | http(s) URL to render |
width / height | 1280 × 800 | Viewport, up to 3840 × 2160 |
full_page | false | Full scroll height, ≤20,000px |
format | png | png | jpeg | webp |
quality | 80 | 1–100, lossy formats only |
Full reference — auth, OG templates, rate limits, error table — in the docs. Template parameters live on /og-templates.
Frequently asked questions
Do I need Selenium to take screenshots in Java?
No. Selenium needs a WebDriver binary matched to a browser version on every machine that runs it. Shotium runs the browsers server-side — from Java it's one request with the JDK's built-in HttpClient, no dependencies.
How do I take a full-page screenshot in Java?
Append &full_page=true to the request URI. The renderer scrolls, waits for lazy-loaded content, and captures up to 20,000px. Because tall pages take longer, keep the HttpRequest timeout at 60 seconds and use BodyHandlers.ofByteArray so the image never passes through a String.
Which Java version do the examples require?
The HTTP examples need Java 11+ (java.net.http); the signing example uses HexFormat, which is Java 17+. On older versions, OkHttp or Apache HttpClient work the same way — it's plain HTTPS.
How should Java code react to a failed render?
HttpClient does not throw on HTTP error status, so check statusCode() before writing res.body() to a file. Failures return RFC 9457 problem+json — send those with BodyHandlers.ofString() to read the type field: rate_limited means honor Retry-After, render_timeout means the target took over 30 seconds. Failed renders are never billed.
Try it on your own pages
Sign-up is GitHub OAuth and comes with 100 free render credits — no card. Failed renders never bill. Plans from $15/month on pricing.
Also available for: Python · Node.js · PHP · Ruby · Go · cURL