Pikzor Documentation
Generate branded OG images for every page on your site — no design tools, no image hosting, no per-post manual work.
Quick Start
You can be up and running in under 5 minutes:
- Sign up at pikzor.com/dashboard
- Choose a template and enter your brand color
- Copy your token URL — it looks like
/og/t_xK9mP2 - Add it to your site as the
og:imagemeta tag
Your OG image URL looks like this:
https://pikzor.com/og/t_xK9mP2?title=My+Post+Title&author=Jane
That's it. Pikzor generates the image on first request and caches it for 30 days.
/og/test?template=og-gradient&title=Hello to try the renderer without signing up.
URL-Based Usage
The simplest way to use Pikzor is by constructing a URL with query parameters. No API key needed for your own token.
Example
GET /og/t_xK9mP2?title=How+I+Deployed+in+5+Minutes&author=Jane+Doe&date=April+5%2C+2026
Returns a 1200×630 PNG image. The response includes:
Content-Type: image/pngCache-Control: public, max-age=2592000, immutableX-Cache: HITorMISS
HTML / Static Sites
Add these tags inside your <head>. Works with any static site generator (Astro, Hugo, Eleventy, plain HTML):
<meta property="og:title" content="My Post Title" />
<meta property="og:description" content="A short description." />
<meta property="og:image"
content="https://pikzor.com/og/t_xK9mP2?title=My+Post+Title&author=Jane" />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:image"
content="https://pikzor.com/og/t_xK9mP2?title=My+Post+Title&author=Jane" />
Template variable syntax
In Liquid (Jekyll/Eleventy):
<meta property="og:image"
content="https://pikzor.com/og/t_xK9mP2?title={{ page.title | url_encode }}&author={{ page.author | url_encode }}" />
In Go templates (Hugo):
<meta property="og:image"
content="https://pikzor.com/og/t_xK9mP2?title={{ .Title | urlize }}&author={{ .Params.author | urlize }}" />
Next.js
In app/blog/[slug]/page.tsx:
import type { Metadata } from 'next'
interface Props {
params: { slug: string }
}
export async function generateMetadata({ params }: Props): Promise<Metadata> {
const post = await getPost(params.slug)
const ogImageUrl = new URL('https://pikzor.com/og/t_xK9mP2')
ogImageUrl.searchParams.set('title', post.title)
ogImageUrl.searchParams.set('author', post.author)
return {
title: post.title,
description: post.excerpt,
openGraph: {
title: post.title,
description: post.excerpt,
images: [{ url: ogImageUrl.toString(), width: 1200, height: 630 }],
},
twitter: {
card: 'summary_large_image',
images: [ogImageUrl.toString()],
},
}
}
In your blog post component:
import Head from 'next/head'
export default function BlogPost({ post }) {
const ogImageUrl =
`https://pikzor.com/og/t_xK9mP2` +
`?title=${encodeURIComponent(post.title)}` +
`&author=${encodeURIComponent(post.author)}`
return (
<>
<Head>
<title>{post.title}</title>
<meta property="og:title" content={post.title} />
<meta property="og:description" content={post.excerpt} />
<meta property="og:image" content={ogImageUrl} />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:image" content={ogImageUrl} />
</Head>
{/* post content */}
</>
)
}
JavaScript
Build the URL in any JavaScript environment:
function pikzorUrl(token, params) {
const url = new URL(`https://pikzor.com/og/${token}`)
for (const [key, value] of Object.entries(params)) {
if (value) url.searchParams.set(key, value)
}
return url.toString()
}
// Usage
const imageUrl = pikzorUrl('t_xK9mP2', {
title: post.title,
author: post.author,
date: post.date,
})
POST /api/v1/render
Programmatic rendering via API key. Returns JSON with a hosted image URL instead of the binary PNG.
Request headers
Authorization: Bearer sk_live_<your-api-key>
Content-Type: application/json
Request body
{
"templateId": "og-gradient",
"brandColor": "#3B82F6",
"title": "My Post Title",
"author": "Jane Doe",
"date": "April 5, 2026"
}
| Field | Type | Required | Description |
|---|---|---|---|
templateId | string | yes | One of the 5 template IDs |
brandColor | string | no | 6-digit hex, e.g. #3B82F6 |
title | string | no | Main heading text |
author | string | no | Shown in card footer |
date | string | no | Shown in card footer |
description | string | no | Subtitle (og-light template only) |
Response
{
"url": "https://pikzor.com/images/api/a1b2c3d4e5f6.png",
"cached": false,
"width": 1200,
"height": 630
}
curl example
curl -X POST https://pikzor.com/api/v1/render \
-H "Authorization: Bearer sk_live_your_key_here" \
-H "Content-Type: application/json" \
-d '{
"templateId": "og-gradient",
"brandColor": "#6366f1",
"title": "Hello from curl",
"author": "Jane Doe"
}'
Authentication
Signup
{ "email": "you@example.com", "password": "mypassword" }
// → { "token": "eyJ...", "user": { "id": "...", "email": "...", "plan": "free" } }
Login
{ "email": "you@example.com", "password": "mypassword" }
// → { "token": "eyJ...", "user": { ... } }
Get current user
// Header: Authorization: Bearer <jwt>
// → { "id": "...", "email": "...", "plan": "free", "brand_color": "#3B82F6" }
Generate API key
{ "name": "My App" }
// → { "key": "sk_live_...", "prefix": "sk_live_12345", "warning": "Save this key..." }
List API keys
Delete API key
Update brand settings
{ "brandColor": "#6366f1", "logoUrl": "https://example.com/logo.png" }
Setup Endpoints
Setup endpoints create the short token URLs (/og/t_xK9mP2) that power your OG images.
Create a token
{
"templateId": "og-gradient",
"brandColor": "#3B82F6",
"logoUrl": "https://example.com/logo.png",
"domain": "myblog.com",
"label": "Blog posts"
}
// → { "token": "t_xK9mP2", "url": "https://pikzor.com/og/t_xK9mP2?title=Your+Title", "id": "..." }
List your tokens
Delete a token
Usage Endpoint
// → {
// "used": 12,
// "cached": 88,
// "limit": 50,
// "plan": "free",
// "month": "2026-04"
// }
limit is null for unlimited plans. used counts only fresh renders (cache hits are free and don't count toward the limit).
Templates
| ID | Name | Best for |
|---|---|---|
og-blog-minimal | Minimal | Clean, text-heavy blogs |
og-gradient | Gradient | Visual, modern tech blogs |
og-split | Split | Corporate or business blogs |
og-bold | Bold | High-impact announcements |
og-light | Light | Lightweight, readable design |
Preview any template without a token:
GET /og/test?template=og-gradient&title=Preview+Title&author=Jane&brandColor=%236366f1
URL Parameters
| Param | Description | Example |
|---|---|---|
title | Main heading | My+Blog+Post |
author | Author name in footer | Jane+Doe |
date | Date in footer | April+5%2C+2026 |
description | Subtitle (og-light only) | A+short+desc |
brandColor | Override brand color (test endpoint only) | %236366f1 |
encodeURIComponent() in JavaScript or url_encode in template engines to avoid broken URLs for titles with special characters.
Plans & Limits
| Plan | Fresh renders/month | Rate limit | Watermark |
|---|---|---|---|
| Free | 50 | 10 req/min | Yes |
| Starter | 1,000 | 30 req/min | No |
| Pro | 5,000 | 60 req/min | No |
| Business | Unlimited | 60 req/min | No |
Cache hits are always free and don't count toward your monthly limit. The first render generates the image; all subsequent requests return the cached version instantly.
When you exceed your monthly limit, the API returns 402 Payment Required:
{
"error": "Monthly render limit reached",
"limit": 50,
"used": 50,
"upgrade_url": "/pricing"
}
Caching
Pikzor uses a two-layer cache:
- Redis — hot cache for frequently requested images
- Filesystem — persistent storage at
/images/<namespace>/<hash>.png
Cache keys are derived from a SHA-256 hash of the template token + sorted parameters. This means:
- Parameter order doesn't matter:
?title=A&author=B=?author=B&title=A - Changing any parameter generates a new image
- Free and paid renders are cached separately (to prevent serving watermarked images after upgrade)
The X-Cache response header tells you whether the image was served from cache (HIT) or freshly rendered (MISS).