> ## Documentation Index
> Fetch the complete documentation index at: https://docs.pictify.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Go SDK

> Official Pictify SDK for Go

# 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

```bash theme={null}
go get github.com/pictify-io/pictify-go
```

## Quick Start

```go theme={null}
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.

```go theme={null}
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,
        },
    }),
)
```

<Warning>
  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.
</Warning>

## Render an Image from HTML

`POST /image` — returns `{URL, ID, CreatedAt}`.

```go theme={null}
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`.

```go theme={null}
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.

```go theme={null}
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.

```go theme={null}
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`.

```go theme={null}
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.

```go theme={null}
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)
```

<Warning>
  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).
</Warning>

## Template Management

```go theme={null}
// 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`                                                              |

```go theme={null}
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:

```go theme={null}
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:

```go theme={null}
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](/api-reference/overview) for full endpoint documentation.
