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.

terminal
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.png

Sign 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).

Illustrative PNG capture used in homepage marketing

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.

signed-url-example.txt
<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.

Plain-English summaryYou send us a URL → we open it in a browser → we send back a screenshot (or PDF, or HTML). One API call. No browser software to install or maintain on your end.

How it works

When you make a request, here is what happens in order:

  1. Your code sends a request to our API with your API key and the URL of the page you want to capture.
  2. We validate your key and quota. If you're authenticated and have credits remaining, we accept the job.
  3. 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.).
  4. 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.

Keep your key privateYour API key is like a password. Store it in an environment variable (not hard-coded in your source code) and never commit it to a public repository like GitHub.

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, or html.

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.

terminal
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.png

When 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.json.example
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.

capture.mjs
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

route.ts
// 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:

terminal
pip install requests
capture.py
import 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

views.py
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.

capture.php
<?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

screenshot.php
<?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.

capture.rb
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']}"
end

Using HTTParty (cleaner syntax)

terminal
gem install httparty
capture_httparty.rb
require "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.

capture.go
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:

response.json
{
  "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-response.json
{
  "error":  "Monthly request limit reached. Upgrade your plan at /billing",
  "code":   "RATE_LIMIT_EXCEEDED",
  "status": 429
}
401

Your API key is missing, wrong, or has been deleted. Double-check the value in your environment variable.

429

You've used all the captures in your monthly plan. Upgrade on the Billing page to get more.

422

The URL you sent is invalid, private (like localhost), or we couldn't reach it. Only public https:// addresses work.

500

Something 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.