Capture a web page as an image from Go. Standard library only - net/http and net/url.
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")
}
| Parameter | Default | What it does |
|---|---|---|
| url | required | The page to capture. http/https only; private and reserved addresses are refused. |
| format | png | png, jpeg, webp or pdf. |
| full_page | false | Capture the whole scrollable page, scrolling first so lazy images load. |
| viewport_width | 1280 | Viewport width in pixels. |
| viewport_height | 800 | Viewport height in pixels. |
| selector | — | Capture only the element matching this CSS selector. |
| block_ads | false | Block ad networks and collapse the empty slot they leave behind. |
| block_cookie_banners | false | Remove consent banners, including ones injected after load. |
| color_scheme | light | Render the page as light or dark. |
| delay | 0 | Extra wait before capture, in ms. |
| cache | false | Serve a cached image when one exists. Cache hits are never billed. |
| fail_on_blank | false | Return 502 instead of an image when the capture looks blank. |
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).
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.
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