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))
}