Skip to main content

Go SDK

The official Pictify SDK for Go provides an idiomatic Go interface for the Pictify API — generate images, PDFs, and GIFs from raw HTML, live URLs, and reusable templates.

Installation

go get github.com/pictify-io/pictify-go

Quick Start

package main

import (
    "context"
    "fmt"
    "log"

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

func main() {
    client := pictify.NewClient("your-api-key")

    // Render raw HTML to a PNG.
    img, err := client.RenderHTML(context.Background(), &pictify.RenderHTMLOptions{
        HTML:   `<div style="font-size:48px;padding:40px">Hello World</div>`,
        Width:  1200,
        Height: 630,
    })
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println(img.URL)
}

API

The SDK talks to the Pictify API at https://api.pictify.io. Every call sends Authorization: Bearer <api-key>.
MethodEndpointReturns
RenderHTMLPOST /image*ImageResult {URL, ID, CreatedAt}
RenderURLPOST /image*ImageResult
RenderPOST /templates/{uid}/render*RenderResult (results[] envelope)
RenderLayoutsPOST /templates/{uid}/render*RenderResult
RenderGIFPOST /gif*GIFResult (flattened)
RenderBatchPOST /templates/{uid}/batch-render*BatchJob (async, HTTP 202)
GetBatchResultsGET /templates/batch/{id}/results*BatchResults
GetTemplateGET /templates/{uid}*Template (unwrapped)
ListTemplatesGET /templates*TemplateList {Templates, Pagination}
CreateTemplatePOST /templates*Template (unwrapped)

Configuration

NewClient takes the API key plus functional options.
client := pictify.NewClient(
    "your-api-key",
    pictify.WithBaseURL("https://api.pictify.io"), // default
    pictify.WithTimeout(30*time.Second),
    pictify.WithMaxRetries(3),
    pictify.WithHTTPClient(&http.Client{
        Transport: &http.Transport{
            MaxIdleConns:    10,
            IdleConnTimeout: 30 * time.Second,
        },
    }),
)
Keep your API key secret. Read it from an environment variable (e.g. os.Getenv("PICTIFY_API_KEY")) and never expose it in client-side code or public repositories.

Render an Image from HTML

POST /image — returns {URL, ID, CreatedAt}.
img, err := client.RenderHTML(ctx, &pictify.RenderHTMLOptions{
    HTML:     `<div class="card">Hello</div>`,
    CSS:      ".card { padding: 40px; color: #0ea5e9; }", // injected as <style> before the HTML
    Width:    1200,                                       // default 1280
    Height:   630,                                        // default 720
    Selector: ".card",                                    // optional: crop to an element
    Format:   pictify.FormatPNG,                          // png (default), jpg, jpeg, webp, pdf
})
if err != nil {
    log.Fatal(err)
}
fmt.Println(img.URL)
The /image endpoint accepts a single html field, so any CSS you pass is injected into a <style> tag prepended to the HTML. Format maps to the endpoint’s fileExtension.

Screenshot a Live URL

POST /image with a url.
img, err := client.RenderURL(ctx, &pictify.RenderURLOptions{
    URL:    "https://example.com",
    Width:  1280,
    Height: 720,
})
if err != nil {
    log.Fatal(err)
}
fmt.Println(img.URL)

Render a Template

POST /templates/{uid}/render — returns a results[] envelope. Use result.URL() for the first result’s URL.
result, err := client.Render(ctx, &pictify.RenderOptions{
    TemplateID: "XL13XACH2V",
    Variables:  map[string]interface{}{"name": "Ada", "company": "Pictify"},
    Format:     pictify.FormatPNG, // template renders also support pdf
    Quality:    0.9,               // 0.1–1.0 for raster output
})
if err != nil {
    log.Fatal(err)
}
fmt.Println(result.URL())          // results[0].url
for _, item := range result.Results {
    fmt.Printf("%s: %s (%dx%d)\n", item.Layout, item.URL, item.Width, item.Height)
}

Layout Variants

Templates can have multiple layout variants (e.g. landscape, square, story) created via AI Resize in the Pictify editor. Render several at once with RenderLayouts (max 20). Use "default" for the base layout. Layouts that can’t be rendered are returned in result.Errors rather than failing the whole call.
result, err := client.RenderLayouts(ctx, &pictify.RenderOptions{
    TemplateID: "XL13XACH2V",
    Variables:  map[string]interface{}{"name": "Ada", "company": "Pictify"},
}, []string{"default", "twitter-post", "instagram-story"})
if err != nil {
    log.Fatal(err)
}
for _, item := range result.Results {
    fmt.Printf("%s: %s\n", item.Layout, item.URL)
}
for _, e := range result.Errors {
    fmt.Printf("layout %s failed: %s\n", e.Layout, e.Error)
}

Render an Animated GIF

POST /gif — provide exactly one source (HTML, URL, or TemplateID). The API’s nested {gif: {...}} envelope is flattened into *GIFResult.
gif, err := client.RenderGIF(ctx, &pictify.GIFOptions{
    HTML: `<style>@keyframes p{0%{opacity:.2}50%{opacity:1}100%{opacity:.2}}` +
        `div{font-size:40px;padding:30px;animation:p 2s infinite}</style>` +
        `<div>Hi</div>`,
    Width:   400,                     // default 800
    Height:  200,                     // default 600
    Quality: pictify.GIFQualityLow,   // low | medium (default) | high
})
if err != nil {
    log.Fatal(err)
}
fmt.Println(gif.URL, gif.UID, gif.AnimationLength)
To render a GIF from a template, set TemplateID (and optionally Variables). The source must be animated; a static source returns a render error (HTTP 422).

Batch Rendering (async)

POST /templates/{uid}/batch-render returns immediately (HTTP 202) with a BatchID. The job runs asynchronously — poll GetBatchResults to track progress.
job, err := client.RenderBatch(ctx, &pictify.BatchOptions{
    TemplateID: "XL13XACH2V",
    VariableSets: []map[string]interface{}{
        {"name": "Ada", "company": "Pictify"},
        {"name": "Grace", "company": "Pictify"},
    },
    Format:      pictify.FormatPNG,
    Concurrency: 5, // 1–10, default 5
})
if err != nil {
    log.Fatal(err)
}
fmt.Printf("batch %s: %s (%d items)\n", job.BatchID, job.Status, job.TotalItems)

results, err := client.GetBatchResults(ctx, job.BatchID)
if err != nil {
    log.Fatal(err)
}
fmt.Printf("status=%s completed=%d/%d failed=%d\n",
    results.Status, results.CompletedItems, results.TotalItems, results.FailedItems)
Rendered URLs are not returned by the poll endpoint; they are delivered via the render.completed webhook. The poll response reports each item’s Index, Success, and variable names (plus an Error on failures).

Template Management

// Create a template from HTML. Variables are auto-discovered from {{name}} tokens.
tmpl, err := client.CreateTemplate(ctx, &pictify.CreateTemplateOptions{
    HTML:   `<div style="padding:20px">Hi {{firstName}}</div>`,
    Name:   "Welcome Card",
    Width:  600,
    Height: 200,
})
if err != nil {
    log.Fatal(err)
}
fmt.Println(tmpl.UID)
for _, v := range tmpl.VariableDefinitions {
    fmt.Printf("  - %s (%s)\n", v.Name, v.Type)
}

// Fetch a single template by UID.
tmpl, err = client.GetTemplate(ctx, "XL13XACH2V")
if err != nil {
    log.Fatal(err)
}
fmt.Printf("%s: %s (%dx%d)\n", tmpl.UID, tmpl.Name, tmpl.Width, tmpl.Height)

// List templates with pagination.
list, err := client.ListTemplates(ctx, &pictify.ListTemplatesOptions{
    Page:  1,
    Limit: 20,           // max 100, default 12
    Sort:  "newest",     // newest | oldest | name
})
if err != nil {
    log.Fatal(err)
}
for _, t := range list.Templates {
    fmt.Printf("%s: %s\n", t.UID, t.Name)
}
fmt.Printf("page %d of %d (%d total)\n",
    list.Pagination.Page, list.Pagination.TotalPages, list.Pagination.Total)
Template keys by UID (not id) and declares variables in VariableDefinitions. Any unknown engine-specific fields are preserved in Template.Extra (a map[string]json.RawMessage).

Error Handling

HTTP errors are mapped to typed errors. Each one embeds *PictifyError, so you can match either the concrete type or the base with errors.As. The error message resolves as body.errorbody.message → HTTP status text.
StatusError type
401*AuthenticationError
402*QuotaExceededError
404*TemplateNotFoundError
422 / other 4xx*RenderError (422 carries field-level Errors)
429*QuotaExceededError (when code == "quota_exceeded") or *RateLimitError
5xx*ServerError
transport failure*NetworkError
timeout / context deadline*TimeoutError
import (
    "errors"

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

result, err := client.Render(ctx, opts)
if err != nil {
    var authErr *pictify.AuthenticationError
    var notFoundErr *pictify.TemplateNotFoundError
    var rateLimitErr *pictify.RateLimitError
    var quotaErr *pictify.QuotaExceededError
    var renderErr *pictify.RenderError
    var serverErr *pictify.ServerError
    var networkErr *pictify.NetworkError

    switch {
    case errors.As(err, &authErr):
        log.Println("invalid API key")
    case errors.As(err, &notFoundErr):
        log.Println("template not found")
    case errors.As(err, &rateLimitErr):
        log.Printf("rate limited; retry after %ds", rateLimitErr.RetryAfter)
    case errors.As(err, &quotaErr):
        log.Println("render quota exceeded")
    case errors.As(err, &renderErr):
        log.Printf("render failed: %s (%v)", renderErr.Message, renderErr.Errors)
    case errors.As(err, &serverErr):
        log.Printf("server error: %s", serverErr.Message)
    case errors.As(err, &networkErr):
        log.Printf("network error: %v", networkErr.Err)
    default:
        log.Printf("error: %v", err)
    }
}
Only 5xx and network failures are retried (with exponential backoff); 4xx responses are never retried.

Context Support

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

img, err := client.RenderHTML(ctx, &pictify.RenderHTMLOptions{
    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.ImageResult, 10)

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

wg.Wait()
close(results)

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

API Reference

See the API Reference for full endpoint documentation.