propzapi API reference
An image generation API. Send a template and your data, get back a rendered PNG, JPEG or WebP. Base URL https://api.propzapi.com. Every response is JSON, and the image comes back as a URL. propzapi renders your own HTML templates with headless Chromium; there is no design tool to log into.
Overview
A template is HTML and CSS with {{variable}} placeholders. You render it by POSTing its id plus a small object of values; the engine fills the placeholders, rasterises the page, and returns a URL to the image. Fourteen templates ship built in (including a QR code card), and you can create your own. Authenticate with an API key in a header, and read the exact credit cost of every render from a response header.
https://api.propzapi.comX-API-Key headerQuickstart
Get a free key with one POST (no card), then render an image. Under a minute end to end.
# 1. get a free key
curl -X POST https://api.propzapi.com/v1/register
# → { "api_key": "pk_live_…", "plan": "free", "credits": 50 }
# 2. render an image
curl -X POST https://api.propzapi.com/v1/images \
-H "X-API-Key: pk_live_…" -H "Content-Type: application/json" \
-d '{"template":"og-article","modifications":{"title":"Hello from propzapi"}}'
# → { "url": "…/renders/img_….png", "width": 1200, "height": 630, "format": "png" } import requests
# 1. free key, no card
key = requests.post("https://api.propzapi.com/v1/register").json()["api_key"]
# 2. render an image
r = requests.post(
"https://api.propzapi.com/v1/images",
headers={"X-API-Key": key},
json={"template": "og-article", "modifications": {"title": "Hello from propzapi"}},
)
print(r.json()["url"]) // native fetch, no dependency needed
const { api_key } = await (await fetch("https://api.propzapi.com/v1/register", { method: "POST" })).json();
const r = await fetch("https://api.propzapi.com/v1/images", {
method: "POST",
headers: { "X-API-Key": api_key, "Content-Type": "application/json" },
body: JSON.stringify({ template: "og-article", modifications: { title: "Hello from propzapi" } }),
});
console.log((await r.json()).url); Authentication
Every /v1 request needs your key in the X-API-Key header. Create and revoke keys in the dashboard. A missing or invalid key returns 401. Keys are secret; keep them server-side.
X-API-Key: pk_live_xxxxxxxxxxxxxxxxxxxxxxxx Credits & metering
One credit renders one image. Listing templates and all template CRUD are free. Metering is billed on delivery: you are charged only when a render succeeds, so a failed render returns X-Credits-Cost: 0. Every render also returns your balance in X-Credits-Remaining.
| Plan | Images / month | Price |
|---|---|---|
| Free trial | 50 (one-time) | $0, no card |
| Starter | 1,000 | $29 |
| Growth | 3,500 | $79 |
| Pro | 12,000 | $199 |
Plan credits refresh each billing cycle and do not roll over. Pay-as-you-go packs ($5 → 150 images, $15 → 500) never expire and need no subscription. Full detail on pricing.
Templates
A template holds your HTML and CSS with {{variable}} placeholders and a set of default values. When you render, your modifications override the defaults. Templates render through a sandboxed template engine, so they can branch and loop over your data — see Templating.
Templating
Templates render through sandboxed Jinja2, so a template plus an array of data becomes a table, a list, or a chart — not just fill-in-the-blank text.
{{ variable }} · inserted and HTML-escaped. Use {{ variable|safe }} to allow raw HTML.{% if x %}…{% endif %} · render a block only when a value is set.{% for row in rows %}…{% endfor %} · loop over an array — tables, lists, leaderboards, charts.upper, truncate(40), default("—"), plus number, currency, date.{% for item in items %}
<li>{{ item.name }} — {{ item.price|currency }}</li>
{% endfor %}
Total: {{ items|sum(attribute='price')|currency }} Output & headers
POST /v1/images returns a small JSON object; the image itself is at url. Two headers ride on every render:
X-Credits-Cost · the credits this render charged (0 if it failed)X-Credits-Remaining · your balance after the renderX-Cache · hit when an identical render (same template + data + format) was served from cache at the same URL, else miss{ "url": "…/renders/img_9f2c.png", "template": "og-article",
"width": 1200, "height": 630, "format": "png", "bytes": 91842 } Errors
Errors return a JSON envelope with a detail message and the matching HTTP status.
{ "detail": "Unknown template 'nope'. See GET /v1/templates." } | Code | Meaning |
|---|---|
400 | Bad request (unknown template, bad format, invalid dimensions) |
401 | Missing or invalid API key |
402 | Out of credits. Top up or upgrade. |
404 | Template not found (or not yours) |
429 | Rate limited (too many keys minted from this IP) |
502 | Render failed. Never charged. |
POST /v1/images
Render an image from a template and your data. Returns a URL to the image.
template · required. A built-in id (og-article, quote-square) or one of your tpl_… ids.modifications · object of variable → value overrides. Any variable you omit uses the template's default.format · png (default), jpeg, webp or pdf (a one-page PDF at the template's size).scale · 1 to 3, the device-pixel ratio. 2 doubles the output resolution (raster formats).quality · 1 to 100, for jpeg and webp.curl -X POST https://api.propzapi.com/v1/images \
-H "X-API-Key: pk_live_…" -H "Content-Type: application/json" \
-d '{"template":"og-article","format":"png","scale":2,
"modifications":{"title":"How we cut render costs by 95%","eyebrow":"ENGINEERING"}}' requests.post(
"https://api.propzapi.com/v1/images",
headers={"X-API-Key": "pk_live_…"},
json={
"template": "og-article",
"format": "png", "scale": 2,
"modifications": {"title": "How we cut render costs by 95%", "eyebrow": "ENGINEERING"},
},
).json()["url"] await fetch("https://api.propzapi.com/v1/images", {
method: "POST",
headers: { "X-API-Key": "pk_live_…", "Content-Type": "application/json" },
body: JSON.stringify({
template: "og-article", format: "png", scale: 2,
modifications: { title: "How we cut render costs by 95%", eyebrow: "ENGINEERING" },
}),
}); POST /v1/images/batch
Render many images in one call — the bulk "a card for every row" job. Send an array of up to 25 render specs, each the same shape as /v1/images. You get a result per item and pay one credit only for each image that actually renders; items that error, or that you can't afford, cost nothing and come back as per-item errors.
curl -X POST https://api.propzapi.com/v1/images/batch \
-H "X-API-Key: pk_live_…" -H "Content-Type: application/json" \
-d '{"images":[
{"template":"og-article","modifications":{"title":"First post"}},
{"template":"og-article","modifications":{"title":"Second post"}}
]}'
# → { "count": 2, "rendered": 2, "failed": 0, "credits_cost": 2,
# "results": [ {"url":"…/img_….png","format":"png","bytes":95332}, {"url":"…"} ] } POST /v1/screenshot
Screenshot any public web page to an image, or print it to a PDF — same engine and 1-credit meter as a template render. Private, localhost and cloud-metadata targets are blocked.
url · required. A public http(s) URL.full_page · capture the whole scrollable page (default false = just the viewport).width / height · viewport, 16 to 4000 (default 1280 × 800).format · png (default), jpeg, webp or pdf. scale 1 to 3.paper · when format:pdf — A4 (default), A3, A5, Letter, Legal, Tabloid or Ledger. landscape optional.curl -X POST https://api.propzapi.com/v1/screenshot \
-H "X-API-Key: pk_live_…" -H "Content-Type: application/json" \
-d '{"url":"https://example.com","full_page":true}'
# → { "url": "…/renders/shot_….png", "source": "https://example.com", "format": "png" }
# Same URL, printed to a paginated A4 PDF:
curl -X POST https://api.propzapi.com/v1/screenshot \
-H "X-API-Key: pk_live_…" -H "Content-Type: application/json" \
-d '{"url":"https://example.com","format":"pdf","paper":"A4"}'
# → { "url": "…/renders/shot_….pdf", "source": "https://example.com", "format": "pdf", "paper": "A4" } Embed URLs — a template as an og:image
Turn a template into a URL you drop straight into <meta og:image>. Sign it once; the URL renders on first fetch and is cached and free on every refetch after that (crawlers hit og:image constantly). Only the first render costs a credit, and the signature stops anyone forging a different render on your account.
curl -X POST https://api.propzapi.com/v1/embed-url \
-H "X-API-Key: pk_live_…" -H "Content-Type: application/json" \
-d '{"template":"og-article","modifications":{"title":"How we cut render costs by 95%"}}'
# → { "url": "…/v1/og/og-article?title=…&a=…&sig=…",
# "og_image_tag": "<meta property=\"og:image\" content=\"…\">" } Drop the returned og_image_tag into your page <head> and every share preview renders itself — no build step, no storing a URL per page.
Template CRUD
Create and manage the templates you render from. Every one of these is scoped to your account; you can only read and change your own.
| Method & path | Does |
|---|---|
POST /v1/templates | Create a template: name, width, height, html, variables. Returns its tpl_… id. |
GET /v1/templates | List built-in templates plus your own. |
GET /v1/templates/{id} | One template, including its HTML. |
PATCH /v1/templates/{id} | Update any of name, width, height, html, variables. |
DELETE /v1/templates/{id} | Delete your template. |
curl -X POST https://api.propzapi.com/v1/templates \
-H "X-API-Key: pk_live_…" -H "Content-Type: application/json" \
-d '{
"name": "Product card",
"width": 1200, "height": 675,
"html": "<div style=\"font:700 72px sans-serif;padding:80px\">{{title}}</div>",
"variables": { "title": "Default title" }
}'
# → { "template": "tpl_9c3f…", "name": "Product card", "width": 1200, "height": 675 } Iterating on a template? POST /v1/templates/preview renders your HTML (or a template id) and returns a data: image without spending a credit — for live preview while you edit. It's rate-limited and needs your key; the dashboard's template editor is built on it.
Built-in templates
Fourteen templates ship ready to render, including a QR code card and three data-driven charts (pass a data array) — see the live gallery. Pass any subset of a template's variables in modifications.
| Template | Size | Variables |
|---|---|---|
og-article | 1200 × 630 | title, eyebrow, author, site, accent, bg |
product-launch | 1200 × 630 | badge, title, tagline, brand, accent, bg |
youtube-thumbnail | 1280 × 720 | title, kicker, accent, bg |
testimonial | 1200 × 630 | quote, avatar, name, role, accent, bg |
quote-square | 1080 × 1080 | quote, author, brand, accent, bg |
stat-card | 1080 × 1080 | metric, label, context, brand, accent, bg |
event-ticket | 1200 × 630 | event, date, time, venue, seat, accent, bg |
code-snippet | 1200 × 675 | filename, code, accent |
certificate | 1600 × 1130 | recipient, course, date, issuer, accent |
instagram-story | 1080 × 1920 | title, subtitle, cta, accent, bg |
bar-chart | 1200 × 675 | title, subtitle, data, accent, bg |
line-chart | 1200 × 675 | title, subtitle, data, accent, bg |
donut-chart | 1080 × 1080 | title, subtitle, data, center_label, accent, bg |
Account & registration
POST /v1/register · mint a free key (50-image trial, no card, no auth). Returns the key, plan and balance.GET /v1/account · your plan and remaining credits. Needs your key.Recipe: an OG image on every publish
Generate the share image for a post from its own data at publish time, and store the returned URL on the record. Here with the built-in card; swap in your own template id once you have one.
import requests
def og_image(api_key, title, eyebrow="BLOG"):
r = requests.post(
"https://api.propzapi.com/v1/images",
headers={"X-API-Key": api_key},
json={"template": "og-article",
"modifications": {"title": title, "eyebrow": eyebrow}},
timeout=30,
)
r.raise_for_status()
return r.json()["url"] # save this on your post
print(og_image("pk_live_…", "How we cut render costs by 95%")) Recipe: create a template once, reuse it forever
Create a template with your own HTML, then render it per record by passing different variables. The template lives on your account, so you only design it once.
import requests
H = {"X-API-Key": "pk_live_…"}
tpl = requests.post("https://api.propzapi.com/v1/templates", headers=H, json={
"name": "Certificate", "width": 1400, "height": 990,
"html": "<div style='font:600 60px sans-serif;text-align:center;padding:120px'>"
"{{name}}<br><span style='font-size:30px'>{{course}}</span></div>",
"variables": {"name": "", "course": ""},
}).json()["template"]
for student in [("Ada Lovelace", "Systems"), ("Alan Turing", "Computation")]:
url = requests.post("https://api.propzapi.com/v1/images", headers=H, json={
"template": tpl,
"modifications": {"name": student[0], "course": student[1]},
}).json()["url"]
print(url) Recipe: render from an AI agent
An MCP client sends one tools/call. generate_image renders and returns the image itself as an image content block, so the agent has the picture, not just a URL.
POST /mcp
{
"jsonrpc": "2.0", "id": 1, "method": "tools/call",
"params": {
"name": "generate_image",
"arguments": {
"template": "og-article",
"modifications": { "title": "Generated by an agent" }
}
}
}
# → content: [ { type: "image", data: "<base64>", mimeType: "image/png" }, { type: "text", … } ] MCP & AI agents
propzapi is built for agents. It ships a Model Context Protocol server so an assistant can render images directly as a tool, plus a plain-text brief models read on their own.
https://api.propzapi.com/mcp · hosted MCP server, streamable HTTPhttps://api.propzapi.com/.well-known/mcp/server-card.json · server cardhttps://api.propzapi.com/openapi.json · OpenAPI 3.1 spec/llms.txt and /llms-full.txt · plain-text brief for modelsThe server exposes these image tools:
| Tool | Arguments | Returns |
|---|---|---|
generate_image | template (required), modifications, format | The rendered image as an MCP image block, plus a text line. 1 credit. |
screenshot_url | url (required), full_page, width, height, format | A screenshot of any public page as an MCP image block. 1 credit. |
list_templates | none | Built-in templates and your own, with each one's variables. Free. |
Point Claude, Cursor, Cline or n8n at the MCP server and an agent can render an image in one call. More on the agents page.
Zapier & Make (no code)
Wire propzapi into a no-code automation in minutes. Both work today with a generic HTTP step — no custom app needed.
Zapier · add a Webhooks by Zapier → Custom Request action: POST to https://api.propzapi.com/v1/images, header X-API-Key = your key, JSON body {"template":"og-article","modifications":{"title":"…"}}. The response url is your image.
Make · add an HTTP → Make a request module: same URL, method POST, header X-API-Key, raw JSON body, and turn on Parse response to map url downstream.
Official Zapier and Make apps (with a template dropdown and named actions) are on the way; the CLI app and Make blueprint live in the repo under integrations/.
Frequently asked questions
Do I need a credit card to use propzapi?
No. One POST to /v1/register mints a free key with a 50-image trial, no card and no signup form. When it runs out, buy a pay-as-you-go pack from $5 or subscribe from $29 a month. You can render your first image within a minute.
How much does a render cost?
One credit per image, and only when a render actually succeeds. A render that fails charges nothing. Every response returns the exact charge in the X-Credits-Cost header and your balance in X-Credits-Remaining, so you never guess.
Can I use my own templates?
Yes. Fourteen are built in, but the point is your own. POST /v1/templates with your HTML and {{variables}}, then render it as often as you like by passing different data. Caps: width/height 16 to 4000px, HTML up to 100 KB, up to 200 templates per account.
What image formats are supported?
png (default), jpeg with an optional quality, webp, and pdf — a one-page PDF at the template's size. Pass scale 1 to 3 for retina output on the raster formats.
How long do the returned image URLs last?
Treat them as short-lived. Rendered images are served by URL from the API; download the bytes or re-render when you need them again. Persistent object storage is being added.
Can an AI agent call propzapi directly?
Yes. There is a hosted Model Context Protocol server at /mcp with generate_image and list_templates tools, plus llms.txt and an OpenAPI spec. generate_image returns the actual image to the agent, not just a link.
Is there a rate limit?
There is no hard per-key limit today, so build normally. Anonymous key minting via /v1/register is throttled per IP and returns 429 on abuse. Concurrent renders per account are bounded so one caller can't starve the engine.
Changelog
- Aug 1, 2026 · User-created templates:
POST/GET/PATCH/DELETE /v1/templates. Build your own HTML templates and render them per record. - Aug 1, 2026 ·
generate_imageandlist_templatesadded to the MCP server. Agents render images and get the picture back inline. - Aug 1, 2026 · Image rendering shipped:
POST /v1/images, headless Chromium, PNG / JPEG / WebP, billed on delivery.