How to take website screenshots in Go

How to take website screenshots in Go

chromedp, rod, and the API approach — with working code.

March 28, 2026 · 8 min read

Go doesn't have a Playwright equivalent with the same polish as the Node.js ecosystem, but there are solid options for headless browser automation. Here are three approaches to taking screenshots in Go, each with different trade-offs.

1. chromedp

chromedp is the most popular Go library for driving Chrome via the DevTools Protocol. It's low-level — closer to Puppeteer's API than Playwright's.

Install
go get github.com/chromedp/chromedp
main.go
package main

import (
    "context"
    "os"
    "time"

    "github.com/chromedp/chromedp"
)

func main() {
    // Create context with timeout
    ctx, cancel := chromedp.NewContext(context.Background())
    defer cancel()
    ctx, cancel = context.WithTimeout(ctx, 30*time.Second)
    defer cancel()

    var buf []byte
    err := chromedp.Run(ctx,
        chromedp.EmulateViewport(1440, 900),
        chromedp.Navigate("https://example.com"),
        chromedp.WaitReady("body"),
        chromedp.CaptureScreenshot(&buf),
    )
    if err != nil {
        panic(err)
    }

    os.WriteFile("screenshot.png", buf, 0644)
}

chromedp requires Chrome/Chromium installed on the system. It connects via the DevTools Protocol — it doesn't bundle a browser like Playwright does.

Full-page capture with chromedp

import "github.com/chromedp/cdproto/page"

// Full page screenshot requires using the CDP directly
var buf []byte
err := chromedp.Run(ctx,
    chromedp.Navigate("https://example.com"),
    chromedp.WaitReady("body"),
    chromedp.ActionFunc(func(ctx context.Context) error {
        // Get full page metrics
        _, _, _, _, _, contentSize, err := page.GetLayoutMetrics().Do(ctx)
        if err != nil { return err }

        // Set viewport to full page height
        chromedp.EmulateViewport(
            int64(contentSize.Width),
            int64(contentSize.Height),
        ).Do(ctx)

        // Capture
        buf, err = page.CaptureScreenshot().
            WithFormat(page.CaptureScreenshotFormatPng).
            WithCaptureBeyondViewport(true).
            Do(ctx)
        return err
    }),
)

2. rod

rod is a higher-level alternative to chromedp. It manages the browser lifecycle automatically and has a more ergonomic API.

go get github.com/go-rod/rod
package main

import "github.com/go-rod/rod"

func main() {
    page := rod.New().MustConnect().MustPage("https://example.com")
    page.MustWindowFullscreen()
    page.MustWaitStable()
    page.MustScreenshot("screenshot.png")
}

rod auto-downloads a compatible browser binary (like Playwright). The Must* methods panic on error — use the non-Must variants for production code.

3. Screenshot API

The simplest approach — no browser to manage, just HTTP:

package main

import (
    "bytes"
    "encoding/json"
    "io"
    "net/http"
    "os"
)

func main() {
    body, _ := json.Marshal(map[string]any{
        "url":    "https://example.com",
        "width":  1440,
        "height": 900,
    })

    req, _ := http.NewRequest("POST",
        "https://api.nightglass.xyz/api/v1/screenshot",
        bytes.NewReader(body))
    req.Header.Set("Content-Type", "application/json")
    req.Header.Set("Authorization", "Bearer YOUR_API_KEY")

    resp, err := http.DefaultClient.Do(req)
    if err != nil { panic(err) }
    defer resp.Body.Close()

    f, _ := os.Create("screenshot.png")
    defer f.Close()
    io.Copy(f, resp.Body)
}

No Chrome to install, no binary management, works in any environment including serverless. At $0.005 per screenshot, it's the cheapest approach when you factor in the cost of managing browser infrastructure.

Which approach for Go?

ApproachBest forDownsides
chromedpFull browser control, complex interactionsLow-level API, requires Chrome installed
rodSimpler API, auto-downloads browserLess community support than chromedp
Screenshot APIURL-to-image without infraExternal dependency, costs per call

For most Go applications that just need URL-to-image, the API approach is the path of least resistance. Note: if deploying to serverless, running Chrome isn't possible — see why. For a broader overview, the complete headless browser guide covers architecture and when self-hosting makes sense. Comparing APIs? 2026 API comparison.