Documentation / Getting started
Getting started
This guide walks you from zero to a working screenshot in about five minutes. You don't need any prior experience with screenshot APIs — just a ScreenNabster account and a way to send an HTTP request (examples for every popular language are below).
First touch
The API is a single HTTPS endpoint. With your API key in the X-API-Key header, the smallest useful request is a GET with just url and output in the query string.
curl "https://screennabster.com/api/v1/capture?url=https%3A%2F%2Fexample.com&output=png" \
-H "X-API-Key: pk_live_your_key_here" \
--output screenshot.pngSign up free — you get 50 captures/month with no credit card, create a key under API Keys, then run the command with your real key.
What you get back
A PNG screenshot of the page (same outcome as all code examples further down this page).

Embedding in HTML (without JavaScript)
Browsers cannot send custom headers on <img> requests. For OG images or static HTML embeds, use a signed GET URL with access_key, expires (Unix seconds), and signature (HMAC-SHA256). See API reference · Capture endpoint and the Open Graph images guide.
<meta property="og:image" content="https://screennabster.com/api/v1/capture?url=...&output=png&access_key=...&expires=...&signature=..." />Overview
ScreenNabster is a service that takes a website address (URL) and turns it into an image, a PDF, or a copy of the page's HTML code — on demand, from your own code. Think of it as a headless browser you rent by the request: you send a URL, we open it in a real browser behind the scenes, take the screenshot, and send the file back to you.
Common use-cases include generating social media preview images, archiving pages for compliance, creating PDF reports, and monitoring visual changes on websites. Everything is handled over a simple HTTP request — the same kind your browser makes every time you visit a website.
How it works
When you make a request, here is what happens in order:
- Your code sends a request to our API with your API key and the URL of the page you want to capture.
- We validate your key and quota. If you're authenticated and have credits remaining, we accept the job.
- A real browser opens the URL on our render servers, waits for the page to fully load, and applies any options you sent (full-page, dark mode, custom viewport size, etc.).
- The file is returned to you — either directly in the HTTP response (fast, synchronous) or uploaded to storage with a link sent to your webhook URL (for long-running or batch jobs).
Most captures complete in 1–3 seconds. New accounts include 50 free captures per month; paid plans add higher quotas. Unused monthly credits do not roll over between billing periods.
What you need
- A ScreenNabster account. Create one free with Google or email — no credit card required to sign up.
- An active quota. Every account includes 50 free captures per month. When you need more volume or paid-only capture options, subscribe on Billing.
- An API key. Create one from the API Keys page in your dashboard. Copy it — you'll use it in every request.
- A way to make HTTP requests. Any programming language works. Examples for curl, JavaScript, Python, PHP, Ruby, and Go are below — pick the one that matches your project.
Your API key
Every request must include your API key so we know who is making the call and which plan's quota to use. You pass it in a request header called X-API-Key.
Think of a request header as a label attached to your message — it travels alongside the URL and body data but is separate from both. Most HTTP libraries have a dedicated place to set headers; the examples below show exactly where.
Your first capture
The simplest possible request has just two fields:
url— the web address you want to capture.output— the file format:png,jpeg,webp,pdf, orhtml.
We start with GET + query parameters — the easiest pattern to try in a browser or terminal. The same options work as a Post JSON body if you prefer — see the POST JSON section further down and the API reference. Every example captures example.com as screenshot.png.
curl (command line)
curl is a command-line tool available on every operating system. It's the fastest way to test your API key without writing any code.
curl "https://screennabster.com/api/v1/capture?url=https%3A%2F%2Fexample.com&output=png" \
-H "X-API-Key: pk_live_your_key_here" \
--output screenshot.pngWhen it finishes, screenshot.png appears in the folder you ran the command from. That's it — your first capture.
POST JSON instead of GET (optional)
Every field in the query string can instead be sent as JSON in the body of a POST. The examples below default to GET for clarity; this pattern is identical across languages — set Content-Type: application/json, pass { url, output }, and keep X-API-Key.
POST https://screennabster.com/api/v1/capture
Content-Type: application/json
X-API-Key: pk_live_your_key_here
{
"url": "https://example.com",
"output": "png"
}JavaScript / Node.js
Works in Node.js 18 and newer using the built-in fetch function — no extra packages needed. If you're using an older Node version, install node-fetch and import fetch from "node-fetch" at the top.
import { writeFile } from "node:fs/promises";
const query = new URLSearchParams({
url: "https://example.com",
output: "png",
}).toString();
const response = await fetch("https://screennabster.com/api/v1/capture?" + query, {
headers: {
// Store your key in an environment variable — never hard-code it
"X-API-Key": process.env.SCREENNABSTER_API_KEY,
},
});
if (!response.ok) {
const error = await response.json();
throw new Error(`Capture failed: ${error.error}`);
}
const buffer = Buffer.from(await response.arrayBuffer());
await writeFile("screenshot.png", buffer);
console.log("Saved screenshot.png");Run with SCREENNABSTER_API_KEY=pk_live_... node capture.mjs. The file screenshot.png will appear in your current directory.
Using inside a Next.js or Express route
// Next.js App Router example — app/api/capture/route.ts
export async function GET() {
const query = new URLSearchParams({
url: "https://example.com",
output: "png",
}).toString();
const upstream = await fetch("https://screennabster.com/api/v1/capture?" + query, {
headers: {
"X-API-Key": process.env.SCREENNABSTER_API_KEY!,
},
});
if (!upstream.ok) {
return new Response("Capture failed", { status: 500 });
}
return new Response(upstream.body, {
headers: { "Content-Type": "image/png" },
});
}Python
Uses the popular requests library. If you don't have it yet, install it once with pip:
pip install requestsimport os
import requests
response = requests.get(
"https://screennabster.com/api/v1/capture",
params={
"url": "https://example.com",
"output": "png",
},
headers={
# Set your key as an environment variable:
# export SCREENNABSTER_API_KEY=pk_live_...
"X-API-Key": os.environ["SCREENNABSTER_API_KEY"],
},
)
response.raise_for_status()
with open("screenshot.png", "wb") as f:
f.write(response.content)
print("Saved screenshot.png")Run with SCREENNABSTER_API_KEY=pk_live_... python capture.py.
Using inside a Django or Flask view
import os
import requests
from django.http import HttpResponse # or Flask's send_file
def screenshot_view(request):
resp = requests.get(
"https://screennabster.com/api/v1/capture",
params={
"url": request.GET.get("url", "https://example.com"),
"output": "png",
},
headers={
"X-API-Key": os.environ["SCREENNABSTER_API_KEY"],
},
)
resp.raise_for_status()
return HttpResponse(resp.content, content_type="image/png")PHP
No external packages needed — PHP's built-in cURL extension is available on virtually every hosting environment.
<?php
// Your API key — store it in an environment variable, not in this file
$apiKey = getenv("SCREENNABSTER_API_KEY");
$params = http_build_query([
"url" => "https://example.com",
"output" => "png",
]);
$ctx = stream_context_create([
"http" => [
"method" => "GET",
"header" => "X-API-Key: {$apiKey}\r\n",
],
]);
$imageData = file_get_contents("https://screennabster.com/api/v1/capture?{$params}", false, $ctx);
if ($imageData === false) {
die("Capture request failed\n");
}
file_put_contents("screenshot.png", $imageData);
echo "Saved screenshot.png\n";Serving the image directly to the browser
<?php
$params = http_build_query([
"url" => $_GET["url"] ?? "https://example.com",
"output" => "png",
]);
$ctx = stream_context_create([
"http" => [
"method" => "GET",
"header" => "X-API-Key: " . getenv("SCREENNABSTER_API_KEY") . "\r\n",
],
]);
$image = file_get_contents("https://screennabster.com/api/v1/capture?{$params}", false, $ctx);
header("Content-Type: image/png");
echo $image;Ruby
Uses Ruby's built-in net/http library — no gems required. If you prefer a cleaner syntax, gem install httparty and use the HTTParty example at the bottom.
require "net/http"
require "uri"
require "json"
uri = URI("https://screennabster.com/api/v1/capture")
uri.query = URI.encode_www_form(url: "https://example.com", output: "png")
resp = Net::HTTP.start(uri.host, uri.port, use_ssl: true) do |h|
h.get(uri.request_uri, "X-API-Key" => ENV["SCREENNABSTER_API_KEY"])
end
if resp.is_a?(Net::HTTPSuccess)
File.write("screenshot.png", resp.body, mode: "wb")
puts "Saved screenshot.png"
else
error = JSON.parse(resp.body)
raise "Capture failed: #{error['error']}"
endUsing HTTParty (cleaner syntax)
gem install httpartyrequire "httparty"
response = HTTParty.get(
"https://screennabster.com/api/v1/capture",
query: {
url: "https://example.com",
output: "png",
},
headers: {
"X-API-Key" => ENV["SCREENNABSTER_API_KEY"],
}
)
raise "Capture failed: #{response['error']}" unless response.success?
File.write("screenshot.png", response.body, mode: "wb")
puts "Saved screenshot.png"Go
Uses only the Go standard library — no third-party packages. Works with Go 1.18 and newer.
package main
import (
"fmt"
"io"
"net/http"
"os"
)
func main() {
req, err := http.NewRequest(http.MethodGet, "https://screennabster.com/api/v1/capture", nil)
if err != nil {
panic(err)
}
q := req.URL.Query()
q.Set("url", "https://example.com")
q.Set("output", "png")
req.URL.RawQuery = q.Encode()
req.Header.Set("X-API-Key", os.Getenv("SCREENNABSTER_API_KEY"))
resp, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
data, _ := io.ReadAll(resp.Body)
fmt.Fprintf(os.Stderr, "capture failed: %s\n", data)
os.Exit(1)
}
img, err := io.ReadAll(resp.Body)
if err != nil {
panic(err)
}
if err := os.WriteFile("screenshot.png", img, 0644); err != nil {
panic(err)
}
fmt.Println("Saved screenshot.png")
}Run with SCREENNABSTER_API_KEY=pk_live_... go run capture.go.
Understanding the response
When a capture succeeds, the API returns the file directly in the HTTP response body. The Content-Type header tells you what format it is (image/png, application/pdf, etc.). Your code can save it to disk, upload it to cloud storage, or stream it straight to a user's browser.
If you'd rather receive JSON instead of raw bytes — for example, to get a base64-encoded image you can embed in a JSON API response — add response_type: "json" to your request. The JSON shape is:
{
"format": "png",
"data": "<base64-encoded image>",
"duration": 1423
}The duration field is the server-side render time in milliseconds — useful for monitoring and debugging slow captures.
When things go wrong
If a request fails, the API always responds with JSON — never an HTML error page. The JSON has three fields:
error— a plain-English description of what went wrong.code— a stable machine-readable identifier (useful for handling errors in code).status— the HTTP status number (e.g. 401, 429, 500).
{
"error": "Monthly request limit reached. Upgrade your plan at /billing",
"code": "RATE_LIMIT_EXCEEDED",
"status": 429
}401Your API key is missing, wrong, or has been deleted. Double-check the value in your environment variable.
429You've used all the captures in your monthly plan. Upgrade on the Billing page to get more.
422The URL you sent is invalid, private (like localhost), or we couldn't reach it. Only public https:// addresses work.
500Something went wrong on our end. These are rare — if it persists, contact support.
A full list of error codes is in the API reference.
Usage & billing
Each plan includes a fixed number of captures per month. Every successful capture uses one credit from that pool. Failed requests (network errors, invalid URLs) do not consume credits.
You can check how many credits you've used and how many remain on the Usage page. If you need more credits mid-month, upgrade your plan on the Billing page — the new quota activates right away.
Ready to go deeper? Check out the guides for real-world examples, or the API reference for every available option.