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>.
| Method | Endpoint | Returns |
|---|
RenderHTML | POST /image | *ImageResult {URL, ID, CreatedAt} |
RenderURL | POST /image | *ImageResult |
Render | POST /templates/{uid}/render | *RenderResult (results[] envelope) |
RenderLayouts | POST /templates/{uid}/render | *RenderResult |
RenderGIF | POST /gif | *GIFResult (flattened) |
RenderBatch | POST /templates/{uid}/batch-render | *BatchJob (async, HTTP 202) |
GetBatchResults | GET /templates/batch/{id}/results | *BatchResults |
GetTemplate | GET /templates/{uid} | *Template (unwrapped) |
ListTemplates | GET /templates | *TemplateList {Templates, Pagination} |
CreateTemplate | POST /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.error → body.message → HTTP status text.
| Status | Error 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, ¬FoundErr):
log.Println("template not found")
case errors.As(err, &rateLimitErr):
log.Printf("rate limited; retry after %ds", rateLimitErr.RetryAfter)
case errors.As(err, "aErr):
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.