Tools: How to take screenshots and generate PDFs in Go

Tools: How to take screenshots and generate PDFs in Go

How to Take Screenshots and Generate PDFs in Go ## Screenshot from a URL ## PDF from a URL ## PDF from HTML ## Use in an HTTP handler ## Record a narrated video 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. If you're generating documents from templates (invoices, reports), pass html instead of url: The same pattern extends to video recording — useful for Go services that generate docs, demos, or changelogs: No CGo, no subprocess, no browser binary. Pure Go, standard library HTTP client. Try it free — 100 requests/month, no credit card. → pagebolt.dev Templates let you quickly answer FAQs or store snippets for re-use. Are you sure you want to ? It will become hidden in your post, but will still be visible via the comment's permalink. as well , this person and/or CODE_BLOCK: 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) } CODE_BLOCK: 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) } CODE_BLOCK: 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) } CODE_BLOCK: 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) } CODE_BLOCK: 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) } CODE_BLOCK: 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) } CODE_BLOCK: 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) } CODE_BLOCK: 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) } CODE_BLOCK: 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) } CODE_BLOCK: 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) } CODE_BLOCK: 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) } CODE_BLOCK: 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) } CODE_BLOCK: 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) } CODE_BLOCK: 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) } CODE_BLOCK: 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) }