createa.meme

Code samples

Copy-paste examples in curl, TypeScript, and Python for every live endpoint.

Every example uses the placeholder cam_live_xxxxxxxxxxxxxxxxxxxx — replace with your real key from the dashboard.

Setup

# Get a key
open https://dashboard.createa.meme

# Set it once in your shell
export CAM_API_KEY="cam_live_xxxxxxxxxxxxxxxxxxxx"
// TypeScript
const CAM_API_KEY = process.env.CAM_API_KEY!
const BASE = 'https://api.createa.meme'

async function camFetch(path: string, init?: RequestInit) {
  const res = await fetch(`${BASE}${path}`, {
    ...init,
    headers: {
      Authorization: `Bearer ${CAM_API_KEY}`,
      'Content-Type': 'application/json',
      ...(init?.headers ?? {}),
    },
  })
  if (!res.ok) {
    const body = await res.text()
    throw new Error(`Createa API ${res.status}: ${body}`)
  }
  return res.json()
}
# Python (requires `pip install httpx`)
import os, httpx

CAM_API_KEY = os.environ["CAM_API_KEY"]
BASE = "https://api.createa.meme"

def cam(path: str, **kwargs):
    headers = {"Authorization": f"Bearer {CAM_API_KEY}"}
    headers.update(kwargs.pop("headers", {}))
    res = httpx.request(headers=headers, **kwargs, url=f"{BASE}{path}")
    res.raise_for_status()
    return res.json()

GET /v1/templates — list templates

curl -s "https://api.createa.meme/v1/templates?limit=10&category=reactions" \
  -H "Authorization: Bearer $CAM_API_KEY"
const { data } = await camFetch('/v1/templates?limit=10&category=reactions')
console.log(data.templates) // [{ id, name, slug, image_url, ... }]
data = cam("/v1/templates", method="GET", params={"limit": 10, "category": "reactions"})["data"]
print(data["templates"][0]["name"])

GET /v1/templates/{id} — single template detail

curl -s "https://api.createa.meme/v1/templates/tpl_drake" \
  -H "Authorization: Bearer $CAM_API_KEY"
const { data } = await camFetch('/v1/templates/tpl_drake')
// data.box_positions describes each region's role + max_chars
data = cam("/v1/templates/tpl_drake", method="GET")["data"]
for box in data["box_positions"]:
    print(box["name"], box["role"], box["max_chars"])

GET /v1/templates/{id}/image — blank template image (302 redirect)

curl -sL -o template.png "https://api.createa.meme/v1/templates/tpl_drake/image" \
  -H "Authorization: Bearer $CAM_API_KEY"
// Follow the 302 to the CDN URL
const res = await fetch(`${BASE}/v1/templates/tpl_drake/image`, {
  headers: { Authorization: `Bearer ${CAM_API_KEY}` },
  redirect: 'follow',
})
const buffer = Buffer.from(await res.arrayBuffer())
curl -s "https://api.createa.meme/v1/trending" \
  -H "Authorization: Bearer $CAM_API_KEY"
const { data } = await camFetch('/v1/trending')
console.log(data.templates.slice(0, 5)) // top 5 trending

GET /v1/models — available AI models

curl -s "https://api.createa.meme/v1/models" \
  -H "Authorization: Bearer $CAM_API_KEY"
const { data } = await camFetch('/v1/models')
// data.image_models = [{ id: 'gpt-image', cost_credits: 3, ... }, ...]
// data.caption_models = [{ id: 'gpt-5.5' }, { id: 'claude-sonnet-4-6' }, ...]

POST /v1/meme/caption — caption a known template (1 credit)

curl -s -X POST "https://api.createa.meme/v1/meme/caption" \
  -H "Authorization: Bearer $CAM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "template_id": "tpl_drake",
    "captions": [
      { "text": "writing tests", "position": "top" },
      { "text": "writing the test that proves the bug exists", "position": "bottom" }
    ]
  }'
const { data } = await camFetch('/v1/meme/caption', {
  method: 'POST',
  body: JSON.stringify({
    template_id: 'tpl_drake',
    captions: [
      { text: 'writing tests', position: 'top' },
      { text: 'writing the test that proves the bug exists', position: 'bottom' },
    ],
  }),
})
console.log(data.url) // CDN URL
data = cam("/v1/meme/caption", method="POST", json={
    "template_id": "tpl_drake",
    "captions": [
        {"text": "writing tests", "position": "top"},
        {"text": "writing the test that proves the bug exists", "position": "bottom"},
    ],
})["data"]
print(data["url"])

POST /v1/meme/generate — text-to-meme, AI picks template + captions (2 credits)

curl -s -X POST "https://api.createa.meme/v1/meme/generate" \
  -H "Authorization: Bearer $CAM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "text": "the perfect monday morning: no meetings, hot coffee, boss is OOO",
    "count": 1,
    "tone": "dry"
  }'
const { data } = await camFetch('/v1/meme/generate', {
  method: 'POST',
  body: JSON.stringify({
    text: 'the perfect monday morning: no meetings, hot coffee, boss is OOO',
    count: 1,
    tone: 'dry',
  }),
})
const meme = data.memes[0]
console.log(meme.url, meme.template.name, meme.caption)
data = cam("/v1/meme/generate", method="POST", json={
    "text": "the perfect monday morning: no meetings, hot coffee, boss is OOO",
    "count": 1,
    "tone": "dry",
})["data"]
print(data["memes"][0]["url"])

POST /v1/meme/ai-image — original AI imagery (3 credits)

curl -s -X POST "https://api.createa.meme/v1/meme/ai-image" \
  -H "Authorization: Bearer $CAM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "prompt": "a confused cat looking at deployment errors",
    "caption": { "top": "WORKS ON MY MACHINE", "bottom": "MY MACHINE WAS LYING" },
    "model": "gpt-image",
    "aspect_ratio": "1:1",
    "output_format": "png"
  }'
const { data } = await camFetch('/v1/meme/ai-image', {
  method: 'POST',
  body: JSON.stringify({
    prompt: 'a confused cat looking at deployment errors',
    caption: { top: 'WORKS ON MY MACHINE', bottom: 'MY MACHINE WAS LYING' },
    model: 'gpt-image',
    aspect_ratio: '1:1',
    output_format: 'png',
  }),
})
console.log(data.url)
data = cam("/v1/meme/ai-image", method="POST", json={
    "prompt": "a confused cat looking at deployment errors",
    "caption": {"top": "WORKS ON MY MACHINE", "bottom": "MY MACHINE WAS LYING"},
    "model": "gpt-image",
    "aspect_ratio": "1:1",
    "output_format": "png",
})["data"]
print(data["url"])

Error handling

All errors return a consistent JSON shape:

{
  "success": false,
  "error": {
    "code": "RATE_LIMITED",
    "message": "Rate limit exceeded. Retry in 12s.",
    "doc_url": "https://docs.createa.meme/errors/rate-limited"
  }
}
async function camFetch(path: string, init?: RequestInit) {
  const res = await fetch(`https://api.createa.meme${path}`, {
    ...init,
    headers: {
      Authorization: `Bearer ${process.env.CAM_API_KEY}`,
      'Content-Type': 'application/json',
      ...(init?.headers ?? {}),
    },
  })
  const body = await res.json()
  if (!body.success) {
    const err = body.error
    if (err.code === 'RATE_LIMITED') {
      const retry = Number(res.headers.get('retry-after') ?? 1)
      await new Promise((r) => setTimeout(r, retry * 1000))
      return camFetch(path, init) // simple retry
    }
    throw new Error(`${err.code}: ${err.message}`)
  }
  return body
}

Coming soon: official SDK

We're shipping @createa/meme-api on npm — typed TypeScript client auto-generated from our OpenAPI spec. Watch the status page for release.

On this page