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

# Node.js SDK

> Official Pictify SDK for Node.js

# Node.js SDK

The official Pictify SDK for Node.js provides a type-safe, promise-based interface for the Pictify API — generate images, PDFs, and GIFs from raw HTML, live URLs, and reusable templates.

## Installation

<CodeGroup>
  ```bash npm theme={null}
  npm install @pictify/sdk
  ```

  ```bash yarn theme={null}
  yarn add @pictify/sdk
  ```

  ```bash pnpm theme={null}
  pnpm add @pictify/sdk
  ```
</CodeGroup>

## Quick Start

```typescript theme={null}
import { Pictify } from '@pictify/sdk';

const pictify = new Pictify({
  apiKey: process.env.PICTIFY_API_KEY!,
});

// Render raw HTML to a PNG
const image = await pictify.renderHtml({
  html: '<div style="font-size:48px;padding:40px">Hello World</div>',
  width: 1200,
  height: 630,
});

console.log('Image URL:', image.url);

// Render a reusable template
const result = await pictify.render({
  templateId: 'XL13XACH2V',
  variables: { name: 'Ada', company: 'Pictify' },
});

console.log('Image URL:', result.url);
```

## Configuration

The client is constructed with a config object. `apiKey` is required; every request is sent with an `Authorization: Bearer <API_KEY>` header.

```typescript theme={null}
const pictify = new Pictify({
  apiKey: process.env.PICTIFY_API_KEY!, // Required: your Pictify API key
  baseUrl: 'https://api.pictify.io',    // Optional: API base URL (default: https://api.pictify.io)
  timeout: 30000,                       // Optional: request timeout in ms (default: 30000)
  retries: 3,                           // Optional: retries on 5xx / network errors (default: 3)
});
```

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

## Render an Image from HTML

`renderHtml(options)` — `POST /image`. Returns `{ url, id, createdAt }`.

```typescript theme={null}
const image = await pictify.renderHtml({
  html: '<div style="padding:40px">Hello</div>',
  css: 'div { color: blue; }', // optional — inlined into a <style> tag before the HTML
  width: 1200,                 // optional (default: 1280)
  height: 630,                 // optional (default: 720)
  selector: '#card',           // optional — crop to a specific element
  format: 'png',               // optional: 'png' | 'jpg' | 'jpeg' | 'webp' | 'pdf' (default: png)
});

console.log(image.url);       // https://cdn.pictify.io/...
console.log(image.id);
console.log(image.createdAt);
```

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

## Screenshot a Live URL

`renderUrl(options)` — `POST /image` with `url`. Returns `{ url, id, createdAt }`.

```typescript theme={null}
const screenshot = await pictify.renderUrl({
  url: 'https://example.com',
  width: 1280,         // optional (default: 1280)
  height: 720,         // optional (default: 720)
  selector: '.hero',   // optional — crop to a specific element
  format: 'png',       // optional
});

console.log(screenshot.url);
```

## Render a Template

`render(options)` — `POST /templates/:uid/render`. Returns a `results[]` envelope with a convenience `url` getter (`results[0]?.url`).

```typescript theme={null}
const result = await pictify.render({
  templateId: 'XL13XACH2V',
  variables: { name: 'Ada', company: 'Pictify' },
  format: 'png',  // optional: 'png' | 'jpg' | 'jpeg' | 'webp' | 'pdf' (default: png)
  quality: 0.9,   // optional render quality, 0.1–1.0 (default: 0.9)
  width: 1200,    // optional
  height: 630,    // optional
});

// `result.url` is a convenience accessor for result.results[0]?.url
console.log(result.url);
console.log(result.results[0]); // { layout, url, width, height, format, name, id, createdAt }
```

The response shape:

```typescript theme={null}
{
  results: RenderResultItem[];   // one entry per rendered layout
  errors: RenderErrorEntry[];    // { layout, error } for any failed layout
  totalLayouts: number;
  totalRendered: number;
  totalErrors: number;
  templateUid: string;
  url: string | undefined;       // convenience getter == results[0]?.url
}
```

### Render a Specific Layout

Pass a single `layout` to render one named variant.

```typescript theme={null}
const result = await pictify.render({
  templateId: 'XL13XACH2V',
  variables: { name: 'Ada' },
  layout: 'square',
});
```

### Render Multiple Layouts

`renderLayouts(options)` — `POST /templates/:uid/render` with `layouts` (max 20).

Templates can have layout variants (e.g. `square`, `story`) created via AI Resize in the Pictify editor. Use `default` for the base layout. Missing or invalid layouts come back in `errors[]` rather than throwing.

```typescript theme={null}
const result = await pictify.renderLayouts({
  templateId: 'XL13XACH2V',
  variables: { name: 'Ada' },
  layouts: ['default', 'square', 'story'], // max 20
});

for (const item of result.results) {
  console.log(`${item.layout}: ${item.url} (${item.width}x${item.height})`);
}
for (const err of result.errors) {
  console.log(`${err.layout} failed: ${err.error}`);
}
```

## Render an Animated GIF

`renderGif(options)` — `POST /gif`. Provide exactly one source: `html`, `url`, or `templateId`. The API's nested `{ gif: {...} }` envelope is flattened to `{ url, uid, width, height, animationLength }`.

```typescript theme={null}
const gif = await pictify.renderGif({
  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>',
  // or: url: 'https://example.com'
  // or: templateId: 'XL13XACH2V', variables: { name: 'Ada' }
  width: 400,         // optional (default: 800)
  height: 200,        // optional (default: 600)
  quality: 'medium',  // optional: 'low' | 'medium' | 'high' (default: medium)
});

console.log(gif.url, gif.uid, gif.animationLength);
```

<Note>
  The source HTML/URL must contain motion (e.g. a CSS animation). Static content produces no frames and returns a render error (HTTP 422).
</Note>

## Batch Rendering (async)

`renderBatch(options)` — `POST /templates/:uid/batch-render`. Returns immediately (HTTP 202) with a `batchId`; poll `getBatchResults(batchId)` (`GET /templates/batch/:batchId/results`) to track progress.

```typescript theme={null}
const job = await pictify.renderBatch({
  templateId: 'XL13XACH2V',
  variableSets: [
    { name: 'Ada', company: 'Pictify' },
    { name: 'Grace', company: 'Pictify' },
  ], // max 100 per batch
  format: 'png',     // optional
  quality: 0.9,      // optional, 0.1–1.0
  concurrency: 5,    // optional, 1–10 (default: 5)
  // layout: 'square' or layouts: ['default', 'square'] — optional
});

// { batchId, status, totalItems }
console.log(job.batchId);

// Poll for progress.
const status = await pictify.getBatchResults(job.batchId);
console.log(status.status);          // 'pending' | 'processing' | 'completed' | 'partial' | 'failed' | 'cancelled'
console.log(status.completedItems, 'of', status.totalItems);
for (const item of status.results) {
  console.log(`item ${item.index}: success=${item.success}`);
}
```

<Warning>
  **Rendered URLs are not returned by the poll endpoint.** `getBatchResults` reports per-item `{ index, success, variables }` (and `error` on failures). Final image URLs are delivered via the `render.completed` webhook — subscribe to webhooks to collect batch output.
</Warning>

## Templates

### Get a Template

`getTemplate(templateId)` — `GET /templates/:uid`. Unwraps the `{ template }` envelope.

```typescript theme={null}
const template = await pictify.getTemplate('XL13XACH2V');

// { uid, name, html, width, height, engine, outputFormat,
//   variables: string[], variableDefinitions: [...], createdAt, ... }
console.log(template.uid, template.name);
```

### List Templates

`listTemplates(options)` — `GET /templates`. Returns `{ templates, pagination }`.

```typescript theme={null}
const { templates, pagination } = await pictify.listTemplates({
  page: 1,        // optional (default: 1)
  limit: 20,      // optional, max 100 (default: 12)
  sort: 'newest', // optional: 'newest' | 'oldest' | 'name'
});

console.log(pagination.total, 'templates');
for (const t of templates) console.log(t.uid, t.name);
```

### Create a Template

`createTemplate(options)` — `POST /templates`. Variables are auto-discovered from `{{variableName}}` tokens in the HTML body.

```typescript theme={null}
const template = await pictify.createTemplate({
  html: '<div>Hi {{firstName}}</div>',
  name: 'Welcome Card',     // optional
  width: 600,               // optional
  height: 200,              // optional
  variableDefinitions: [],  // optional — auto-extracted from the HTML when omitted
  outputFormat: 'image',    // optional: 'image' | 'pdf'
});

console.log(template.uid);
```

## Error Handling

All API errors throw a typed subclass of `PictifyError`.

```typescript theme={null}
import {
  Pictify,
  PictifyError,
  AuthenticationError,
  TemplateNotFoundError,
  RateLimitError,
  QuotaExceededError,
  RenderError,
} from '@pictify/sdk';

try {
  const result = await pictify.render({ templateId: 'XL13XACH2V' });
} catch (error) {
  if (error instanceof RateLimitError) {
    console.log('Rate limited — slow down');
  } else if (error instanceof QuotaExceededError) {
    console.log('Quota exceeded — upgrade your plan');
  } else if (error instanceof RenderError) {
    console.error('Render/validation failed:', error.message, error.errors);
  } else if (error instanceof PictifyError) {
    console.error('Pictify error:', error.code, error.message);
  } else {
    throw error;
  }
}
```

### Error Types

| Error Class             | Code                  | HTTP                | Description                                                                  |
| ----------------------- | --------------------- | ------------------- | ---------------------------------------------------------------------------- |
| `AuthenticationError`   | `INVALID_API_KEY`     | 401                 | Invalid or missing API key                                                   |
| `QuotaExceededError`    | `QUOTA_EXCEEDED`      | 402 / 429           | Render quota exceeded                                                        |
| `TemplateNotFoundError` | `TEMPLATE_NOT_FOUND`  | 404                 | Template (or batch job) not found                                            |
| `RenderError`           | `RENDER_FAILED`       | 422 (and other 4xx) | Render or input validation failed (`error.errors` holds field-level details) |
| `RateLimitError`        | `RATE_LIMIT_EXCEEDED` | 429                 | Too many requests                                                            |
| `ServerError`           | `SERVER_ERROR`        | 5xx                 | Server-side failure                                                          |
| `NetworkError`          | `NETWORK_ERROR`       | —                   | Network request failed                                                       |
| `TimeoutError`          | `TIMEOUT`             | —                   | Request timed out                                                            |

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

## CommonJS Support

```javascript theme={null}
const { Pictify } = require('@pictify/sdk');

const pictify = new Pictify({ apiKey: process.env.PICTIFY_API_KEY });
```

## TypeScript Support

The SDK is written in TypeScript and exports all types:

```typescript theme={null}
import {
  Pictify,
  ImageResult,
  RenderResult,
  RenderResultItem,
  GifRenderResult,
  BatchRenderResult,
  BatchResults,
  Template,
  ListTemplatesResult,
  ImageFormat,
  GifQuality,
} from '@pictify/sdk';
```

## Next.js Integration

### Route Handler (OG image)

```typescript theme={null}
// app/api/og/route.ts
import { Pictify } from '@pictify/sdk';
import { NextRequest, NextResponse } from 'next/server';

const pictify = new Pictify({ apiKey: process.env.PICTIFY_API_KEY! });

export async function GET(req: NextRequest) {
  const title = req.nextUrl.searchParams.get('title') ?? '';

  const result = await pictify.render({
    templateId: 'og-image-template',
    variables: { title },
  });

  return NextResponse.redirect(result.url!);
}
```

### Express.js OG-image route

```typescript theme={null}
import express from 'express';
import { Pictify } from '@pictify/sdk';

const app = express();
const pictify = new Pictify({ apiKey: process.env.PICTIFY_API_KEY! });

app.get('/og-image', async (req, res) => {
  const { title, description } = req.query as Record<string, string>;

  const result = await pictify.render({
    templateId: 'og-image-template',
    variables: { title, description },
  });

  res.redirect(result.url!);
});
```

## API Reference

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