> ## 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.

# Ruby SDK

> Official Pictify SDK for Ruby

# Ruby SDK

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

## Installation

Add to your Gemfile:

```ruby theme={null}
gem "pictify"
```

Then run:

```bash theme={null}
bundle install
```

Or install directly:

```bash theme={null}
gem install pictify
```

## Quick Start

```ruby theme={null}
require "pictify"

client = Pictify::Client.new(api_key: "your-api-key")

# Render raw HTML to a PNG
image = client.render_html(
  html: "<div style='font-size:48px;padding:40px'>Hello World</div>",
  width: 1200,
  height: 630
)
puts image.url

# Render a template
result = client.render(
  template_id: "XL13XACH2V",
  variables: { name: "Ada", company: "Pictify" }
)
puts result.url
```

## Configuration

The client is constructed with a keyword `api_key`. Every request is sent with an `Authorization: Bearer <API_KEY>` header.

```ruby theme={null}
client = Pictify::Client.new(
  api_key: "your-api-key",
  base_url: "https://api.pictify.io",  # optional: API base URL (default: https://api.pictify.io)
  timeout: 30,                         # optional: request timeout in seconds (default: 30)
  max_retries: 3                       # optional: retries on 5xx / network errors (default: 3)
)
```

<Warning>
  Keep your API key secret. Read it from an environment variable (e.g. `ENV["PICTIFY_API_KEY"]`) and never expose it in client-side code or public repositories.
</Warning>

## Rendering Images

### From HTML — `POST /image`

Returns an `ImageResult` with `url`, `id`, and `created_at`.

```ruby theme={null}
image = client.render_html(
  html: "<div>Hello</div>",
  css: "div { color: blue; }",  # injected as a <style> tag before the HTML
  width: 1200,                  # default 1280
  height: 630,                  # default 720
  selector: "#card",            # crop to a specific element (optional)
  format: :png                  # :png, :jpg, :jpeg, :webp, :pdf — mapped to fileExtension
)

puts image.url         # CDN URL
puts image.id          # image id
puts image.created_at  # ISO timestamp
```

The `/image` endpoint accepts a single `html` field, so any `css` you pass is injected into a `<style>` tag prepended to the HTML.

### From a live URL (screenshot) — `POST /image`

```ruby theme={null}
image = client.render_url(
  url: "https://example.com",
  width: 1280,
  height: 720,
  selector: "#main",  # optional
  format: :png        # optional
)
puts image.url
```

## Rendering Templates

### Single render — `POST /templates/:uid/render`

Returns a `RenderResult` `results` array (one item per rendered layout). `result.url` is a convenience accessor for the first result's URL.

```ruby theme={null}
result = client.render(
  template_id: "XL13XACH2V",
  variables: { name: "Ada", company: "Pictify" },
  format: :png,    # :png, :jpg, :jpeg, :webp, :pdf
  quality: 0.9,    # raster quality 0.1–1.0
  width: 1200,     # optional override
  height: 630      # optional override
)

puts result.url               # results.first.url
puts result.template_uid
result.results.each do |item|
  puts "#{item.layout}: #{item.url} (#{item.width}x#{item.height})"
end
```

You can also render a single named variant via `render(..., layout: "square")`.

### Multiple layout variants — `POST /templates/:uid/render`

Templates can have multiple layout variants (e.g. landscape, square, story) created via AI Resize in the Pictify editor. Render several in one call (max 20); layouts that fail land in `errors`.

```ruby theme={null}
result = client.render_layouts(
  template_id: "XL13XACH2V",
  variables: { name: "Ada" },
  layouts: ["default", "twitter-post", "instagram-story"]
)

result.results.each do |item|
  puts "#{item.layout}: #{item.url} (#{item.width}x#{item.height})"
end
result.errors.each do |err|
  puts "#{err.layout} failed: #{err.error}"
end

puts "rendered #{result.total_rendered} of #{result.total_layouts}"
```

## Rendering GIFs — `POST /gif`

Provide exactly one source: `html`, `url`, or `template_id` (+ optional `variables`). The source must contain motion (CSS animation, etc.). The `{ gif: {...} }` envelope is flattened to a `GifRenderResult`.

```ruby theme={null}
gif = client.render_gif(
  html: "<style>@keyframes p{0%{opacity:.2}50%{opacity:1}100%{opacity:.2}}" \
        "div{animation:p 2s infinite}</style><div>Hi</div>",
  width: 400,       # default 800
  height: 200,      # default 600
  quality: :medium  # :low, :medium, :high
)
puts gif.url
puts gif.uid
puts gif.animation_length

# From a template
gif = client.render_gif(template_id: "XL13XACH2V", variables: { name: "Ada" })

# From a live URL
gif = client.render_gif(url: "https://example.com/animated-page")
```

<Note>
  Static content produces no frames and returns a render error (HTTP 422).
</Note>

## Batch Rendering (async) — `POST /templates/:uid/batch-render`

Batch rendering is asynchronous. Submitting returns a `batch_id` immediately (HTTP 202); poll `get_batch_results` to track progress.

```ruby theme={null}
job = client.render_batch(
  template_id: "XL13XACH2V",
  variable_sets: [
    { name: "Card 1", company: "X" },
    { name: "Card 2", company: "Y" }
  ],
  format: :png,
  quality: 0.9,    # optional
  concurrency: 5,  # optional, 1–10
  layouts: ["default", "twitter-post"]  # optional
)

puts job.batch_id
puts job.status      # "pending"
puts job.total_items

# Poll for progress
results = client.get_batch_results(job.batch_id)
puts "status: #{results.status} (#{results.progress}%)"
puts "completed: #{results.completed_items} / #{results.total_items}"
results.results.each do |item|
  puts "Item #{item.index}: success=#{item.success?} vars=#{item.variables}"
end
```

<Warning>
  The poll endpoint reports per-item `index`, `success`, and `variables` but **not** rendered URLs — URLs are delivered via the `render.completed` webhook.
</Warning>

## Template Management

```ruby theme={null}
# Get a template by UID — GET /templates/:uid
template = client.get_template("XL13XACH2V")
puts "Template: #{template.name} (#{template.uid})"
template.variable_definitions.each do |var|
  puts "  - #{var.name} (#{var.type})"
end

# List templates — GET /templates
result = client.list_templates(page: 1, limit: 20, sort: :newest)
result.templates.each { |t| puts "#{t.uid}: #{t.name}" }
puts "total: #{result.pagination.total}"

# Create a template from HTML — POST /templates
# Variables are auto-discovered from {{variableName}} tokens.
template = client.create_template(
  html: "<div>Hi {{firstName}}</div>",
  name: "Welcome Card",
  width: 600,
  height: 200,
  output_format: "image"  # "image" | "pdf"
)
puts template.uid
```

## Error Handling

```ruby theme={null}
begin
  result = client.render(template_id: "XL13XACH2V", variables: { name: "Ada" })
rescue Pictify::AuthenticationError
  puts "Invalid API key"
rescue Pictify::TemplateNotFoundError => e
  puts "Template not found: #{e.message}"
rescue Pictify::QuotaExceededError
  puts "Render quota exceeded"
rescue Pictify::RateLimitError => e
  puts "Rate limited. Retry after: #{e.retry_after}s"
rescue Pictify::RenderError => e
  puts "Render/validation failed: #{e.message}"
  puts e.errors  # field-level validation errors when present (422)
rescue Pictify::ServerError => e
  puts "Server error: #{e.message}"
rescue Pictify::NetworkError => e
  puts "Network error: #{e.message}"
rescue Pictify::TimeoutError
  puts "Request timed out"
rescue Pictify::Error => e
  puts "Error: #{e.message}"
end
```

| Status                 | Error class                               |
| ---------------------- | ----------------------------------------- |
| 401                    | `Pictify::AuthenticationError`            |
| 402                    | `Pictify::QuotaExceededError`             |
| 404                    | `Pictify::TemplateNotFoundError`          |
| 422                    | `Pictify::RenderError` (carries `errors`) |
| 429 (`quota_exceeded`) | `Pictify::QuotaExceededError`             |
| 429 (other)            | `Pictify::RateLimitError`                 |
| other 4xx              | `Pictify::RenderError`                    |
| 5xx                    | `Pictify::ServerError`                    |

Only 5xx responses and network failures are retried (with exponential backoff); 4xx responses (including 429) are never retried.

## Framework Example — Rails

```ruby theme={null}
class OgImagesController < ApplicationController
  def show
    client = Pictify::Client.new(api_key: ENV["PICTIFY_API_KEY"])
    result = client.render(
      template_id: "og-image-template",
      variables: { title: params[:title] }
    )
    redirect_to result.url, allow_other_host: true
  end
end
```

## API Reference

See the [API Reference](/api-reference/overview) for full endpoint documentation.
