Tools: How to take screenshots and generate PDFs in Go

Tools: How to take screenshots and generate PDFs in Go

Source: Dev.to

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 hide this comment? It will become hidden in your post, but will still be visible via the comment's permalink. Hide child comments as well For further actions, you may consider blocking this person and/or reporting abuse 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) } Enter fullscreen mode Exit fullscreen mode 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) } Enter fullscreen mode Exit fullscreen mode 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) } Enter fullscreen mode Exit fullscreen mode 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) } Enter fullscreen mode Exit fullscreen mode 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) } Enter fullscreen mode Exit fullscreen mode 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) }