Skip to main content

Go SDK

The official Pictify SDK for Go provides an idiomatic Go interface for the Pictify API.

Installation

go get github.com/pictify/pictify-go

Quick Start

package main

import (
    "fmt"
    "log"

    "github.com/pictify/pictify-go"
)

func main() {
    client := pictify.NewClient("pk_live_your_api_key")

    image, err := client.Images.Create(&pictify.ImageCreateParams{
        HTML:   "<h1 style=\"color: #667eea;\">Hello World</h1>",
        Width:  1200,
        Height: 630,
    })
    if err != nil {
        log.Fatal(err)
    }

    fmt.Println(image.URL)
}

Configuration

Basic Configuration

client := pictify.NewClient("pk_live_your_api_key")

Advanced Configuration

client := pictify.NewClient(
    "pk_live_your_api_key",
    pictify.WithBaseURL("https://api.pictify.io/v1"),
    pictify.WithTimeout(30 * time.Second),
    pictify.WithRetries(3),
)

Environment Variables

import "os"

client := pictify.NewClient(os.Getenv("PICTIFY_API_KEY"))

Images

Generate from HTML

image, err := client.Images.Create(&pictify.ImageCreateParams{
    HTML: `
        <div style="width: 1200px; height: 630px; background: #667eea;
                    display: flex; align-items: center; justify-content: center;">
            <h1 style="color: white; font-size: 48px;">Hello World</h1>
        </div>
    `,
    Width:  1200,
    Height: 630,
    Format: pictify.FormatPNG,
})
if err != nil {
    log.Fatal(err)
}

fmt.Println(image.URL)    // https://cdn.pictify.io/renders/...
fmt.Println(image.ID)     // img_abc123
fmt.Println(image.Width)  // 1200
fmt.Println(image.Height) // 630

Generate from URL

image, err := client.Images.Create(&pictify.ImageCreateParams{
    URL:      "https://example.com",
    Width:    1200,
    Height:   630,
    FullPage: false,
})

Canvas Rendering

image, err := client.Images.Canvas(&pictify.CanvasParams{
    FabricJSData: map[string]interface{}{
        "version": "5.3.0",
        "objects": []map[string]interface{}{
            {"type": "rect", "fill": "#667eea", "width": 1200, "height": 630},
            {"type": "textbox", "text": "{{title}}", "fill": "white", "fontSize": 48},
        },
    },
    Variables: map[string]interface{}{
        "title": "Dynamic Content",
    },
    Width:  1200,
    Height: 630,
})

Agent Screenshot

image, err := client.Images.AgentScreenshot(&pictify.AgentScreenshotParams{
    Prompt: "Take a screenshot of the pricing section on stripe.com",
})

GIFs

Create from HTML

gif, err := client.GIFs.Create(&pictify.GIFCreateParams{
    HTML: `
        <div class="container">
            <style>
                @keyframes pulse {
                    0% { transform: scale(1); }
                    50% { transform: scale(1.1); }
                    100% { transform: scale(1); }
                }
                .pulse { animation: pulse 1s infinite; }
            </style>
            <div class="pulse">Animated!</div>
        </div>
    `,
    Width:  400,
    Height: 400,
})

Capture from URL

gif, err := client.GIFs.Capture(&pictify.GIFCaptureParams{
    URL:                  "https://example.com/animated",
    Width:                800,
    Height:               600,
    Quality:              pictify.QualityHigh,
    FrameDurationSeconds: 5,
})

PDFs

Single Page

pdf, err := client.PDFs.Render(&pictify.PDFRenderParams{
    TemplateUID: "tmpl_invoice",
    Variables: map[string]interface{}{
        "invoiceNumber": "INV-001",
        "items": []map[string]interface{}{
            {"name": "Service", "price": 99.00},
        },
        "total": 99.00,
    },
    Options: &pictify.PDFOptions{
        Preset: pictify.PresetA4,
        Margins: &pictify.Margins{
            Top: 40, Bottom: 40, Left: 30, Right: 30,
        },
    },
})

Multi-Page

pdf, err := client.PDFs.MultiPage(&pictify.PDFMultiPageParams{
    TemplateUID: "tmpl_report",
    VariableSets: []map[string]interface{}{
        {"pageTitle": "Introduction", "content": "..."},
        {"pageTitle": "Analysis", "content": "..."},
        {"pageTitle": "Conclusion", "content": "..."},
    },
    Options: &pictify.PDFOptions{
        Preset: pictify.PresetA4,
    },
})

Templates

List Templates

result, err := client.Templates.List(&pictify.ListParams{
    Page:  1,
    Limit: 20,
})

for _, tmpl := range result.Templates {
    fmt.Printf("%s: %s\n", tmpl.UID, tmpl.Name)
}

Create Template

template, err := client.Templates.Create(&pictify.TemplateCreateParams{
    Name:   "Social Card",
    HTML:   "<h1>{{title}}</h1><p>{{description}}</p>",
    Width:  1200,
    Height: 630,
})

Render Template

image, err := client.Templates.Render("tmpl_abc123", &pictify.RenderParams{
    Variables: map[string]interface{}{
        "title":       "My Post",
        "description": "An interesting article",
    },
    Format: pictify.FormatPNG,
})

Batch Render

batch, err := client.Templates.BatchRender("tmpl_abc123", &pictify.BatchRenderParams{
    VariableSets: []map[string]interface{}{
        {"title": "Post 1"},
        {"title": "Post 2"},
        {"title": "Post 3"},
    },
    WebhookURL: "https://your-server.com/webhooks/batch",
})

// Later, get results
results, err := client.Templates.GetBatchResults(batch.ID, nil)

Webhooks

Create Subscription

subscription, err := client.Webhooks.Create(&pictify.WebhookCreateParams{
    Event:     pictify.EventRenderCompleted,
    TargetURL: "https://your-server.com/webhooks/pictify",
})

// Store this secret securely!
fmt.Println(subscription.Secret)

Verify Signature

package main

import (
    "crypto/hmac"
    "crypto/sha256"
    "encoding/hex"
    "fmt"
    "math"
    "strconv"
    "strings"
    "time"
)

func VerifyWebhookSignature(payload, signatureHeader, secret string) error {
    parts := make(map[string]string)
    for _, pair := range strings.Split(signatureHeader, ",") {
        kv := strings.SplitN(pair, "=", 2)
        if len(kv) == 2 {
            parts[kv[0]] = kv[1]
        }
    }

    timestamp, _ := strconv.ParseInt(parts["t"], 10, 64)
    providedSignature := parts["v1"]

    // Reject if timestamp is older than 5 minutes
    if math.Abs(float64(time.Now().Unix()-timestamp)) > 300 {
        return fmt.Errorf("webhook timestamp too old")
    }

    signedPayload := fmt.Sprintf("%d.%s", timestamp, payload)
    mac := hmac.New(sha256.New, []byte(secret))
    mac.Write([]byte(signedPayload))
    expectedSignature := hex.EncodeToString(mac.Sum(nil))

    if !hmac.Equal([]byte(providedSignature), []byte(expectedSignature)) {
        return fmt.Errorf("invalid signature")
    }

    return nil
}

HTTP Handler

package main

import (
    "encoding/json"
    "io"
    "net/http"
)

func webhookHandler(w http.ResponseWriter, r *http.Request) {
    signature := r.Header.Get("X-Pictify-Signature")

    body, err := io.ReadAll(r.Body)
    if err != nil {
        http.Error(w, "Failed to read body", http.StatusBadRequest)
        return
    }

    if err := VerifyWebhookSignature(string(body), signature, webhookSecret); err != nil {
        http.Error(w, err.Error(), http.StatusUnauthorized)
        return
    }

    var event pictify.WebhookEvent
    if err := json.Unmarshal(body, &event); err != nil {
        http.Error(w, "Invalid JSON", http.StatusBadRequest)
        return
    }

    fmt.Printf("Received event: %s\n", event.Event)

    w.WriteHeader(http.StatusOK)
    w.Write([]byte("OK"))
}

Bindings

Create Binding

binding, err := client.Bindings.Create(&pictify.BindingCreateParams{
    Name:        "GitHub Stats",
    TemplateUID: "tmpl_github",
    DataURL:     "https://api.github.com/repos/your/repo",
    DataMapping: map[string]string{
        "stars": "stargazers_count",
        "forks": "forks_count",
    },
    Schedule: "0 * * * *", // Hourly
})

// Permanent image URL that auto-updates
fmt.Println(binding.ImageURL)

Trigger Refresh

err := client.Bindings.Refresh("bind_abc123")

Error Handling

image, err := client.Images.Create(&pictify.ImageCreateParams{...})
if err != nil {
    switch e := err.(type) {
    case *pictify.RateLimitError:
        // Wait and retry
        fmt.Printf("Rate limited. Retry after %d seconds\n", e.RetryAfter)
        time.Sleep(time.Duration(e.RetryAfter) * time.Second)
    case *pictify.ValidationError:
        // Fix validation issues
        fmt.Printf("Validation errors: %v\n", e.Errors)
    case *pictify.AuthenticationError:
        // Check API key
        fmt.Println("Invalid API key")
    case *pictify.APIError:
        // General API error
        fmt.Printf("API Error: %s (status: %d)\n", e.Message, e.Status)
    default:
        // Network or other error
        log.Fatal(err)
    }
}

Context Support

All methods support context for cancellation and timeouts:
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()

image, err := client.Images.CreateWithContext(ctx, &pictify.ImageCreateParams{
    HTML:   "<h1>Hello</h1>",
    Width:  1200,
    Height: 630,
})

Concurrent Requests

The client is safe for concurrent use:
var wg sync.WaitGroup
results := make(chan *pictify.Image, 10)

for i := 0; i < 10; i++ {
    wg.Add(1)
    go func(i int) {
        defer wg.Done()
        image, err := client.Images.Create(&pictify.ImageCreateParams{
            HTML:   fmt.Sprintf("<h1>Image %d</h1>", i),
            Width:  1200,
            Height: 630,
        })
        if err == nil {
            results <- image
        }
    }(i)
}

wg.Wait()
close(results)

for img := range results {
    fmt.Println(img.URL)
}

API Reference

See the API Reference for full endpoint documentation.