Go Integration
Learn how to generate PDFs from your Go application using the standard library's net/http package.
Why use PixelToPdf with Go?
Go's simplicity and performance make it ideal for high-throughput PDF generation. With no external dependencies needed beyond the standard library, PixelToPdf integrates cleanly into any Go project. Perfect for microservices, CLI tools, and web applications that need reliable document generation at scale.
Prerequisites
- Go 1.18+ (no external dependencies required)
- A PixelToPdf API key from your dashboard (free tier available)
Basic Example
This example uses Go's standard library to convert a webpage to PDF. We use os.Getenv() to securely read the API key from environment variables.
package main
import (
"bytes"
"encoding/json"
"io"
"net/http"
"os"
)
func main() {
payload := map[string]interface{}{
"source": "https://example.com",
}
jsonData, _ := json.Marshal(payload)
req, _ := http.NewRequest("POST", "https://api.pixeltopdf.com/convert/pdf", bytes.NewBuffer(jsonData))
req.Header.Set("X-API-Key", os.Getenv("PIXELTOPDF_API_KEY"))
req.Header.Set("Content-Type", "application/json")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
if resp.StatusCode == http.StatusOK {
pdfData, _ := io.ReadAll(resp.Body)
os.WriteFile("output.pdf", pdfData, 0644)
println("PDF saved to output.pdf")
} else {
println("Error:", resp.StatusCode)
}
}How it works: We create an HTTP client and build a POST request with http.NewRequest. The payload is serialized to JSON using json.Marshal. The response body is read with io.ReadAll and written to disk with os.WriteFile.
Convert HTML String
For dynamic documents, pass your HTML directly as a string. Go's raw string literals (backticks) make it easy to write multi-line HTML without escaping.
package main
import (
"bytes"
"encoding/json"
"io"
"net/http"
"os"
)
func main() {
html := `
<!DOCTYPE html>
<html>
<head>
<style>
body { font-family: Arial, sans-serif; padding: 40px; }
h1 { color: #333; }
</style>
</head>
<body>
<h1>Hello, PixelToPdf!</h1>
<p>This PDF was generated from HTML.</p>
</body>
</html>`
payload := map[string]interface{}{
"source": html,
"format": "A4",
"margin": map[string]string{
"top": "20mm",
"bottom": "20mm",
"left": "20mm",
"right": "20mm",
},
}
jsonData, _ := json.Marshal(payload)
req, _ := http.NewRequest("POST", "https://api.pixeltopdf.com/convert/pdf", bytes.NewBuffer(jsonData))
req.Header.Set("X-API-Key", os.Getenv("PIXELTOPDF_API_KEY"))
req.Header.Set("Content-Type", "application/json")
client := &http.Client{}
resp, _ := client.Do(req)
defer resp.Body.Close()
pdfData, _ := io.ReadAll(resp.Body)
os.WriteFile("output.pdf", pdfData, 0644)
}Tip: You can use Go's html/template package to generate your HTML dynamically. The margin option accepts values in mm, cm, in, or px.
With Gin
This Gin handler creates an endpoint that generates PDFs on-demand. When users visit /invoice/123/pdf, they'll download the invoice as a PDF file.
package main
import (
"bytes"
"encoding/json"
"io"
"net/http"
"os"
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
r.GET("/invoice/:id/pdf", func(c *gin.Context) {
id := c.Param("id")
payload := map[string]interface{}{
"source": "https://yourapp.com/invoice/" + id + "/html",
}
jsonData, _ := json.Marshal(payload)
req, _ := http.NewRequest("POST", "https://api.pixeltopdf.com/convert/pdf", bytes.NewBuffer(jsonData))
req.Header.Set("X-API-Key", os.Getenv("PIXELTOPDF_API_KEY"))
req.Header.Set("Content-Type", "application/json")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
c.String(http.StatusInternalServerError, "PDF generation failed")
return
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
c.String(http.StatusInternalServerError, "PDF generation failed")
return
}
pdfData, _ := io.ReadAll(resp.Body)
c.Header("Content-Disposition", "attachment; filename=invoice-"+id+".pdf")
c.Data(http.StatusOK, "application/pdf", pdfData)
})
r.Run(":8080")
}Best practice: Reuse the http.Client instead of creating a new one for each request. Consider using a middleware to inject the client. For production, add timeouts with context.WithTimeout.
Reusable Client
This reusable client package wraps the API call with proper error handling. Sentinel errors make it easy to handle specific error cases using Go's idiomatic errors.Is() pattern.
package pixeltopdf
import (
"bytes"
"encoding/json"
"errors"
"io"
"net/http"
)
var (
ErrInvalidAPIKey = errors.New("invalid API key")
ErrNoCredits = errors.New("no credits remaining")
ErrRateLimitExceeded = errors.New("rate limit exceeded")
ErrGenerationFailed = errors.New("PDF generation failed")
)
type Client struct {
apiKey string
httpClient *http.Client
}
func NewClient(apiKey string) *Client {
return &Client{
apiKey: apiKey,
httpClient: &http.Client{},
}
}
type ConvertOptions struct {
Source string `json:"source"`
Format string `json:"format,omitempty"`
Landscape bool `json:"landscape,omitempty"`
Margin map[string]string `json:"margin,omitempty"`
}
func (c *Client) ConvertToPDF(opts ConvertOptions) ([]byte, error) {
jsonData, err := json.Marshal(opts)
if err != nil {
return nil, err
}
req, err := http.NewRequest("POST", "https://api.pixeltopdf.com/convert/pdf", bytes.NewBuffer(jsonData))
if err != nil {
return nil, err
}
req.Header.Set("X-API-Key", c.apiKey)
req.Header.Set("Content-Type", "application/json")
resp, err := c.httpClient.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
switch resp.StatusCode {
case http.StatusOK:
return io.ReadAll(resp.Body)
case http.StatusUnauthorized:
return nil, ErrInvalidAPIKey
case http.StatusPaymentRequired:
return nil, ErrNoCredits
case http.StatusTooManyRequests:
return nil, ErrRateLimitExceeded
default:
return nil, ErrGenerationFailed
}
}How it works: The ConvertOptions struct uses JSON tags to control serialization. The switch statement maps HTTP status codes to specific error types, making error handling predictable and type-safe.
Using the Client
Here's how to use the reusable client in your application. The error handling demonstrates Go's idiomatic approach to checking specific error types.
package main
import (
"os"
"yourmodule/pixeltopdf"
)
func main() {
client := pixeltopdf.NewClient(os.Getenv("PIXELTOPDF_API_KEY"))
pdf, err := client.ConvertToPDF(pixeltopdf.ConvertOptions{
Source: "https://example.com",
Format: "A4",
Margin: map[string]string{
"top": "20mm",
"bottom": "20mm",
"left": "20mm",
"right": "20mm",
},
})
if err != nil {
switch err {
case pixeltopdf.ErrInvalidAPIKey:
println("Invalid API key")
case pixeltopdf.ErrNoCredits:
println("No credits remaining")
case pixeltopdf.ErrRateLimitExceeded:
println("Rate limit exceeded, try again later")
default:
println("Error:", err.Error())
}
return
}
os.WriteFile("output.pdf", pdf, 0644)
println("PDF saved to output.pdf")
}Important: The API returns different status codes for different error types: 401 for authentication issues, 402 when you need more credits, and 429 when you've hit the rate limit. Handle each appropriately in production.
Next steps
You've mastered the basics! Learn about headers, footers, page breaks, and advanced styling in our API documentation.