How to Take Screenshots and Generate PDFs in Go
Go has no headless browser library. The usual workarounds — shell out to Chrome, run a Python sidecar, use a cgo binding — all add operational weight. Here's the clean alternative: one HTTP POST, binary response, standard library.
Go has no headless browser library. When Go services need to capture screenshots or generate PDFs, the usual approaches are: shell out to Chrome, run a sidecar Python process, or reach for a cgo-wrapped browser binding. All of these add operational complexity to what should be a simple output.
Here's the clean approach: one HTTP POST, binary response, standard library.
Screenshot from a URL
package main
import (
"bytes"
"encoding/json"
"io"
"net/http"
"os"
)
func Screenshot(url string) ([]byte, error) {
payload, _ := json.Marshal(map[string]interface{}{
"url": url,
"fullPage": true,
"blockBanners": true,
})
req, _ := http.NewRequest("POST", "https://pagebolt.dev/api/v1/screenshot", bytes.NewBuffer(payload))
req.Header.Set("x-api-key", os.Getenv("PAGEBOLT_API_KEY"))
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
return io.ReadAll(resp.Body)
}
func main() {
img, err := Screenshot("https://example.com")
if err != nil {
panic(err)
}
os.WriteFile("screenshot.png", img, 0644)
}
PDF from a URL
func PDF(url string) ([]byte, error) {
payload, _ := json.Marshal(map[string]interface{}{
"url": url,
"blockBanners": true,
})
req, _ := http.NewRequest("POST", "https://pagebolt.dev/api/v1/pdf", bytes.NewBuffer(payload))
req.Header.Set("x-api-key", os.Getenv("PAGEBOLT_API_KEY"))
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
return io.ReadAll(resp.Body)
}
PDF from HTML
If you're generating documents from templates (invoices, reports), pass html instead of url:
import "html/template"
type Invoice struct {
ID string
Amount string
Due string
}
const invoiceTmpl = `<!DOCTYPE html>
<html><head><style>
body { font-family: system-ui; padding: 40px; }
.amount { font-size: 32px; font-weight: bold; }
</style></head>
<body>
<h1>Invoice #{{.ID}}</h1>
<p>Due: {{.Due}}</p>
<div class="amount">{{.Amount}}</div>
</body></html>`
func InvoicePDF(inv Invoice) ([]byte, error) {
var buf bytes.Buffer
t := template.Must(template.New("invoice").Parse(invoiceTmpl))
t.Execute(&buf, inv)
payload, _ := json.Marshal(map[string]interface{}{
"html": buf.String(),
})
req, _ := http.NewRequest("POST", "https://pagebolt.dev/api/v1/pdf", bytes.NewBuffer(payload))
req.Header.Set("x-api-key", os.Getenv("PAGEBOLT_API_KEY"))
req.Header.Set("Content-Type", "application/json")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
return io.ReadAll(resp.Body)
}
Use in an HTTP handler
func screenshotHandler(w http.ResponseWriter, r *http.Request) {
targetURL := r.URL.Query().Get("url")
if targetURL == "" {
http.Error(w, "url required", http.StatusBadRequest)
return
}
img, err := Screenshot(targetURL)
if err != nil {
http.Error(w, "capture failed", http.StatusBadGateway)
return
}
w.Header().Set("Content-Type", "image/png")
w.Write(img)
}
func main() {
http.HandleFunc("/screenshot", screenshotHandler)
http.ListenAndServe(":8080", nil)
}
Record a narrated video
The same pattern extends to video recording — useful for Go services that generate docs, demos, or changelogs:
type Step struct {
Action string `json:"action"`
URL string `json:"url,omitempty"`
Selector string `json:"selector,omitempty"`
Note string `json:"note,omitempty"`
}
type AudioGuide struct {
Enabled bool `json:"enabled"`
Voice string `json:"voice"`
Script string `json:"script"`
}
type VideoRequest struct {
Steps []Step `json:"steps"`
AudioGuide AudioGuide `json:"audioGuide"`
Pace string `json:"pace"`
}
func RecordVideo() ([]byte, error) {
body := VideoRequest{
Steps: []Step{
{Action: "navigate", URL: "https://yourapp.com", Note: "Open the app"},
{Action: "click", Selector: "#get-started", Note: "Get started"},
},
AudioGuide: AudioGuide{
Enabled: true,
Voice: "nova",
Script: "Here's the app. {{1}} {{2}} One click to begin.",
},
Pace: "slow",
}
payload, _ := json.Marshal(body)
req, _ := http.NewRequest("POST", "https://pagebolt.dev/api/v1/video", bytes.NewBuffer(payload))
req.Header.Set("x-api-key", os.Getenv("PAGEBOLT_API_KEY"))
req.Header.Set("Content-Type", "application/json")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
return io.ReadAll(resp.Body)
}
No CGo, no subprocess, no browser binary. Pure Go, standard library HTTP client.
Get Started Free
100 requests/month, no credit card
Screenshots, PDFs, and video recordings from your Go service — no Chrome binary, no CGo, no sidecar process.
Get Your Free API Key →