Endpoints#

Base URL: https://api.htmlpix.com

All endpoints except GET /v1/image require authentication.


POST /v1/url#

Mint a signed public URL for a single image. Each successful response consumes 1 quota unit.

Request body#

{
  "templateId": "k57k8w9n7n9r8b6r2x6f2q9v1h7p4y0c",
  "variables": { "title": "Launch Day" }
}
FieldTypeRequiredDescription
templateIdstringYesTemplate ID
variablesobjectNoKey-value pairs for template variables
widthnumberNo1–4096 (default from template or 1200)
heightnumberNo1–4096 (default from template or 630)
formatstringNopng, jpeg, or webp (default webp)
qualitynumberNo0–100, applies to jpeg and webp
devicePixelRationumberNoPositive number for HiDPI (e.g. 2)
tvstringNoTemplate version override for cache busting

Response 200#

{
  "url": "https://image.htmlpix.com/v1/image?templateId=...&sig=...",
  "expiresAt": 1914998400000
}

POST /v1/urls#

Batch mint multiple signed URLs in one call. Each item in the response consumes 1 quota unit.

Request body#

{
  "items": [
    { "templateId": "tmpl_a", "variables": { "title": "Page A" } },
    { "templateId": "tmpl_b", "variables": { "title": "Page B" } }
  ]
}

Each entry in items accepts the same fields as POST /v1/url. Maximum 25 items per request.

Response 200#

{
  "urls": [
    {
      "templateId": "tmpl_a",
      "url": "https://image.htmlpix.com/v1/image?...&sig=...",
      "expiresAt": 1914998400000
    }
  ]
}

GET /v1/templates#

List templates available to your account.

Query parameters#

ParameterTypeRequiredDescription
scopestringNoall (default), mine, or starter
  • all — your templates plus public starter templates
  • mine — only templates you created
  • starter — only public starter templates

Response 200#

{
  "scope": "all",
  "templates": [
    {
      "_id": "k57k8w9n7n9r8b6r2x6f2q9v1h7p4y0c",
      "name": "Blog Post OG",
      "description": "Card layout for blog posts",
      "variables": [{ "name": "title", "defaultValue": "Hello" }],
      "width": 1200,
      "height": 630,
      "format": "webp",
      "devicePixelRatio": 1,
      "signedPreviewUrl": "https://image.htmlpix.com/v1/image?...",
      "isPublic": false
    }
  ]
}

POST /v1/templates#

Create a new template from JSX code.

Request body#

{
  "name": "Launch Card",
  "description": "Marketing launch visual",
  "code": "export default function Template({ title = \"Launch Day\" }) {\n  return <div style={{ ... }}>{title}</div>;\n}\n\nexport const options = { width: 1200, height: 630, format: \"webp\" };"
}
FieldTypeRequiredDescription
namestringYesTemplate name (max 120 chars)
descriptionstringNoTemplate description (max 2000 chars)
codestringYesJSX template source (max 180K chars)

Templates are compiled server-side after creation. Export an options object from your code to set default width, height, format, quality, and devicePixelRatio.

Response 201#

{
  "templateId": "k57k8w9n7n9r8b6r2x6f2q9v1h7p4y0c",
  "template": { "..." }
}

POST /v1/templates/generate#

Generate templates using AI. Sends a batch of prompts and returns generated templates. Each successful generation consumes 1 AI quota unit.

Request body#

{
  "items": [
    {
      "prompt": "A professional blog post OG image with title and author",
      "width": 1200,
      "height": 630
    }
  ]
}
FieldTypeRequiredDescription
itemsarrayYesArray of generation requests (max 5)
items[].promptstringYesDescription of the template to generate (max 2000 chars)
items[].widthnumberNoImage width, 1–4096
items[].heightnumberNoImage height, 1–4096
items[].imageDataUrlstringNoBase64 data URL of a reference image (e.g. data:image/png;base64,...). The image is sent to the AI alongside your prompt — no predefined instructions are added.

Response 200#

{
  "results": [
    {
      "ok": true,
      "result": { "templateId": "...", "template": { "..." } }
    }
  ]
}

Individual items can fail independently:

{
  "results": [
    {
      "ok": false,
      "error": { "code": "GENERATION_FAILED", "message": "..." }
    }
  ]
}

GET /v1/templates/:templateId#

Fetch a single template by ID. Returns templates you own or public starter templates.

Response 200#

{
  "template": {
    "_id": "k57k8w9n7n9r8b6r2x6f2q9v1h7p4y0c",
    "name": "Blog Post OG",
    "code": "export default function Template({ title }) { ... }",
    "variables": [{ "name": "title", "defaultValue": "Hello" }],
    "width": 1200,
    "height": 630,
    "format": "webp"
  }
}

PATCH /v1/templates/:templateId#

Update a template you own. Only include the fields you want to change.

Request body#

{
  "name": "Launch Card v2",
  "code": "export default function Template({ title = \"Updated\" }) { ... }"
}
FieldTypeRequiredDescription
namestringNoNew template name (max 120 chars)
descriptionstringNoNew description (max 2000 chars)
codestringNoNew JSX source (max 180K chars)

At least one field must be provided.

Response 200#

{
  "templateId": "k57k8w9n7n9r8b6r2x6f2q9v1h7p4y0c",
  "template": { "..." }
}

GET /v1/image#

Public image delivery endpoint. This is the URL you put in your meta tags. Not billable — fetches are free and cached.

This endpoint is served from image.htmlpix.com via Cloudflare's edge network.

How it works#

The signed URL returned by POST /v1/url points here. When a crawler or browser requests it, the image is rendered (or served from cache) and returned as binary data.

Signed query parameters#

These parameters are set automatically when you mint a URL. Do not modify them.

ParameterDescription
templateIdTemplate to render
uidUser ID
expExpiration timestamp
sigHMAC signature
rvRender version
tvTemplate version
widthImage width
heightImage height
formatOutput format
qualityCompression quality
dprDevice pixel ratio

Template variables are passed with a v_ prefix (e.g. v_title=Launch+Day).

Response#

  • 200 — image bytes (image/png, image/jpeg, or image/webp)
  • 304 — not modified (when If-None-Match matches the ETag)

Cache headers#

HeaderValue
Cache-Controlpublic, max-age=86400
ETagContent hash
X-Cacher2 (cached) or render (fresh)
Server-TimingRender duration (fresh renders only)