Go Examples
Example code for using PDF API with Go.
Go Examples
Use PDF API with Go's standard net/http package.
Basic Example
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"os"
)
func main() {
payload := map[string]interface{}{
"markdown": "# Hello World\n\nThis is my first PDF!",
"theme": "default",
}
body, err := json.Marshal(payload)
if err != nil {
panic(err)
}
req, err := http.NewRequest("POST", "https://api.pdfapi.dev/v1/pdf/markdown", bytes.NewBuffer(body))
if err != nil {
panic(err)
}
req.Header.Set("Authorization", "Bearer "+os.Getenv("PDFAPI_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 {
body, _ := io.ReadAll(resp.Body)
fmt.Printf("Error: %s\n", string(body))
return
}
pdf, err := io.ReadAll(resp.Body)
if err != nil {
panic(err)
}
err = os.WriteFile("hello.pdf", pdf, 0644)
if err != nil {
panic(err)
}
fmt.Println("PDF saved to hello.pdf")
}
With Options
package main
import (
"bytes"
"encoding/json"
"io"
"net/http"
"os"
)
type PDFRequest struct {
Markdown string `json:"markdown"`
Theme string `json:"theme"`
Options PDFOptions `json:"options"`
}
type PDFOptions struct {
Format string `json:"format,omitempty"`
Landscape bool `json:"landscape,omitempty"`
Margin map[string]string `json:"margin,omitempty"`
Header string `json:"header,omitempty"`
Footer string `json:"footer,omitempty"`
}
func main() {
request := PDFRequest{
Markdown: `# Monthly Report
## Summary
- Revenue: $45,678
- Users: 1,234
## Details
| Metric | Value |
|--------|-------|
| New signups | 156 |
| Churn | 2.3% |`,
Theme: "corporate",
Options: PDFOptions{
Format: "A4",
Margin: map[string]string{
"top": "25mm",
"bottom": "25mm",
"left": "20mm",
"right": "20mm",
},
Footer: `<div style="text-align: center; font-size: 10px;">Page <span class="pageNumber"></span></div>`,
},
}
body, _ := json.Marshal(request)
req, _ := http.NewRequest("POST", "https://api.pdfapi.dev/v1/pdf/markdown", bytes.NewBuffer(body))
req.Header.Set("Authorization", "Bearer "+os.Getenv("PDFAPI_KEY"))
req.Header.Set("Content-Type", "application/json")
client := &http.Client{}
resp, _ := client.Do(req)
defer resp.Body.Close()
pdf, _ := io.ReadAll(resp.Body)
os.WriteFile("report.pdf", pdf, 0644)
}
PDF Client Package
package pdfapi
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"os"
"time"
)
const baseURL = "https://api.pdfapi.dev/v1"
type Client struct {
apiKey string
httpClient *http.Client
}
type PDFOptions struct {
Format string `json:"format,omitempty"`
Landscape bool `json:"landscape,omitempty"`
Margin map[string]string `json:"margin,omitempty"`
PrintBackground bool `json:"print_background,omitempty"`
Scale float64 `json:"scale,omitempty"`
Header string `json:"header,omitempty"`
Footer string `json:"footer,omitempty"`
WaitFor int `json:"wait_for,omitempty"`
}
type MarkdownRequest struct {
Markdown string `json:"markdown"`
Theme string `json:"theme,omitempty"`
Options PDFOptions `json:"options,omitempty"`
}
type HTMLRequest struct {
HTML string `json:"html"`
Options PDFOptions `json:"options,omitempty"`
}
type URLRequest struct {
URL string `json:"url"`
Options PDFOptions `json:"options,omitempty"`
}
type APIError struct {
Error struct {
Code string `json:"code"`
Message string `json:"message"`
} `json:"error"`
}
type UsageResponse struct {
Data struct {
Plan string `json:"plan"`
PDFsGenerated int `json:"pdfs_generated"`
PDFsLimit int `json:"pdfs_limit"`
PDFsRemaining int `json:"pdfs_remaining"`
} `json:"data"`
}
func NewClient(apiKey string) *Client {
if apiKey == "" {
apiKey = os.Getenv("PDFAPI_KEY")
}
return &Client{
apiKey: apiKey,
httpClient: &http.Client{
Timeout: 60 * time.Second,
},
}
}
func (c *Client) doRequest(method, endpoint string, body interface{}) ([]byte, error) {
var reqBody io.Reader
if body != nil {
jsonBody, err := json.Marshal(body)
if err != nil {
return nil, err
}
reqBody = bytes.NewBuffer(jsonBody)
}
req, err := http.NewRequest(method, baseURL+endpoint, reqBody)
if err != nil {
return nil, err
}
req.Header.Set("Authorization", "Bearer "+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()
respBody, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
}
if resp.StatusCode >= 400 {
var apiErr APIError
json.Unmarshal(respBody, &apiErr)
return nil, fmt.Errorf("API error: %s - %s", apiErr.Error.Code, apiErr.Error.Message)
}
return respBody, nil
}
func (c *Client) MarkdownToPDF(markdown, theme string, options PDFOptions) ([]byte, error) {
return c.doRequest("POST", "/pdf/markdown", MarkdownRequest{
Markdown: markdown,
Theme: theme,
Options: options,
})
}
func (c *Client) HTMLToPDF(html string, options PDFOptions) ([]byte, error) {
return c.doRequest("POST", "/pdf/html", HTMLRequest{
HTML: html,
Options: options,
})
}
func (c *Client) URLToPDF(url string, options PDFOptions) ([]byte, error) {
return c.doRequest("POST", "/pdf/url", URLRequest{
URL: url,
Options: options,
})
}
func (c *Client) GetUsage() (*UsageResponse, error) {
body, err := c.doRequest("GET", "/usage", nil)
if err != nil {
return nil, err
}
var usage UsageResponse
err = json.Unmarshal(body, &usage)
return &usage, err
}
Using the Client
package main
import (
"fmt"
"os"
"yourmodule/pdfapi"
)
func main() {
client := pdfapi.NewClient("")
// Generate PDF from markdown
pdf, err := client.MarkdownToPDF(
"# Hello World",
"corporate",
pdfapi.PDFOptions{
Format: "A4",
Margin: map[string]string{"top": "25mm", "bottom": "25mm"},
},
)
if err != nil {
panic(err)
}
os.WriteFile("output.pdf", pdf, 0644)
// Check usage
usage, _ := client.GetUsage()
fmt.Printf("Used %d of %d PDFs\n", usage.Data.PDFsGenerated, usage.Data.PDFsLimit)
}
HTTP Handler
package main
import (
"encoding/json"
"io"
"net/http"
"os"
)
func generatePDFHandler(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
var request struct {
Markdown string `json:"markdown"`
Theme string `json:"theme"`
}
if err := json.NewDecoder(r.Body).Decode(&request); err != nil {
http.Error(w, "Invalid request body", http.StatusBadRequest)
return
}
payload := map[string]string{
"markdown": request.Markdown,
"theme": request.Theme,
}
body, _ := json.Marshal(payload)
req, _ := http.NewRequest("POST", "https://api.pdfapi.dev/v1/pdf/markdown",
bytes.NewBuffer(body))
req.Header.Set("Authorization", "Bearer "+os.Getenv("PDFAPI_KEY"))
req.Header.Set("Content-Type", "application/json")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
http.Error(w, "Failed to generate PDF", http.StatusInternalServerError)
return
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(resp.Body)
http.Error(w, string(body), resp.StatusCode)
return
}
w.Header().Set("Content-Type", "application/pdf")
w.Header().Set("Content-Disposition", "attachment; filename=document.pdf")
io.Copy(w, resp.Body)
}
func main() {
http.HandleFunc("/api/pdf", generatePDFHandler)
http.ListenAndServe(":8080", nil)
}
Gin Framework
package main
import (
"bytes"
"encoding/json"
"io"
"net/http"
"os"
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
r.POST("/pdf", func(c *gin.Context) {
var request struct {
Markdown string `json:"markdown"`
Theme string `json:"theme"`
}
if err := c.BindJSON(&request); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request"})
return
}
payload, _ := json.Marshal(map[string]string{
"markdown": request.Markdown,
"theme": request.Theme,
})
req, _ := http.NewRequest("POST", "https://api.pdfapi.dev/v1/pdf/markdown",
bytes.NewBuffer(payload))
req.Header.Set("Authorization", "Bearer "+os.Getenv("PDFAPI_KEY"))
req.Header.Set("Content-Type", "application/json")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to generate PDF"})
return
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(resp.Body)
c.Data(resp.StatusCode, "application/json", body)
return
}
c.Header("Content-Type", "application/pdf")
c.Header("Content-Disposition", "attachment; filename=document.pdf")
io.Copy(c.Writer, resp.Body)
})
r.Run(":8080")
}
Batch Processing with Goroutines
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"os"
"path/filepath"
"sync"
"time"
)
type Result struct {
InputFile string
OutputFile string
Success bool
Error string
}
func generatePDF(markdown, outputPath string) error {
payload := map[string]string{"markdown": markdown, "theme": "default"}
body, _ := json.Marshal(payload)
req, _ := http.NewRequest("POST", "https://api.pdfapi.dev/v1/pdf/markdown",
bytes.NewBuffer(body))
req.Header.Set("Authorization", "Bearer "+os.Getenv("PDFAPI_KEY"))
req.Header.Set("Content-Type", "application/json")
client := &http.Client{Timeout: 60 * time.Second}
resp, err := client.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode == 429 {
// Rate limited, wait and retry
time.Sleep(60 * time.Second)
return generatePDF(markdown, outputPath)
}
if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(resp.Body)
return fmt.Errorf("API error: %s", string(body))
}
pdf, _ := io.ReadAll(resp.Body)
return os.WriteFile(outputPath, pdf, 0644)
}
func batchConvert(inputDir, outputDir string, workers int) []Result {
files, _ := filepath.Glob(filepath.Join(inputDir, "*.md"))
os.MkdirAll(outputDir, 0755)
results := make([]Result, len(files))
jobs := make(chan int, len(files))
var wg sync.WaitGroup
// Start workers
for w := 0; w < workers; w++ {
wg.Add(1)
go func() {
defer wg.Done()
for i := range jobs {
file := files[i]
content, _ := os.ReadFile(file)
outputFile := filepath.Join(outputDir,
filepath.Base(file[:len(file)-3])+".pdf")
err := generatePDF(string(content), outputFile)
results[i] = Result{
InputFile: file,
OutputFile: outputFile,
Success: err == nil,
}
if err != nil {
results[i].Error = err.Error()
}
// Small delay to respect rate limits
time.Sleep(500 * time.Millisecond)
}
}()
}
// Send jobs
for i := range files {
jobs <- i
}
close(jobs)
wg.Wait()
return results
}
func main() {
results := batchConvert("documents/", "output/", 3)
successful := 0
for _, r := range results {
if r.Success {
successful++
fmt.Printf("✓ %s -> %s\n", r.InputFile, r.OutputFile)
} else {
fmt.Printf("✗ %s: %s\n", r.InputFile, r.Error)
}
}
fmt.Printf("\nCompleted: %d/%d files\n", successful, len(results))
}