Screenshot API for Go

Capture a web page as an image from Go. Standard library only - net/http and net/url.

Quickstart

Written carefully but not executed on our side — we had no Go runtime on the machine that wrote it. If it misbehaves, tell us and we will fix it the same day.
package main

import (
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"os"
)

func main() {
	q := url.Values{}
	q.Set("url", "https://example.com")
	q.Set("access_key", os.Getenv("SCREENSHOTLINE_KEY"))
	q.Set("format", "png")
	q.Set("viewport_width", "1280")
	q.Set("block_ads", "true")
	q.Set("block_cookie_banners", "true")

	res, err := http.Get("https://api.screenshotline.com/take?" + q.Encode())
	if err != nil {
		panic(err)
	}
	defer res.Body.Close()

	body, _ := io.ReadAll(res.Body)
	if res.StatusCode != http.StatusOK {
		var e struct {
			Error struct{ Message string } `json:"error"`
		}
		json.Unmarshal(body, &e)
		panic(e.Error.Message)
	}

	os.WriteFile("screenshot.png", body, 0o644)
	fmt.Println(res.Header.Get("X-Quota-Remaining"), "renders remaining")
}
Need a key? Create a free account — 100 renders a month, no card, no confirmation email.

Options

ParameterDefaultWhat it does
urlrequiredThe page to capture. http/https only; private and reserved addresses are refused.
formatpngpng, jpeg, webp or pdf.
full_pagefalseCapture the whole scrollable page, scrolling first so lazy images load.
viewport_width1280Viewport width in pixels.
viewport_height800Viewport height in pixels.
selectorCapture only the element matching this CSS selector.
block_adsfalseBlock ad networks and collapse the empty slot they leave behind.
block_cookie_bannersfalseRemove consent banners, including ones injected after load.
color_schemelightRender the page as light or dark.
delay0Extra wait before capture, in ms.
cachefalseServe a cached image when one exists. Cache hits are never billed.
fail_on_blankfalseReturn 502 instead of an image when the capture looks blank.

Errors

Failures return JSON with a stable code. Successful captures return the image bytes directly, with no envelope.

{
  "error": {
    "code": "invalid_access_key",
    "message": "Unknown or revoked API key."
  }
}

Common codes: missing_access_key (401), invalid_access_key (403), quota_exceeded (402), render_timeout (504), blank_capture (502).

Failed renders are not billed, and neither are cache hits. We detect a blank capture rather than assuming a 200 means success, so a bot wall that returns an empty page does not come out of your quota.

Signed URLs

The headline use case is putting a capture straight into an <img src>. A raw key in public HTML is a billing incident waiting to happen, so sign the URL instead: the signature covers every parameter, and a tampered URL is refused.

node tools/sign.js "https://example.com" full_page=true

Set REQUIRE_SIGNATURE=true to refuse unsigned requests entirely.

Quota headers

Every response carries your position, so you never need a second request to check:

X-Quota-Limit: 2000
X-Quota-Used: 417
X-Quota-Remaining: 1583
X-Quota-Period: 2026-09