Meme Lair API

Meme API Documentation

Generate memes programmatically. 700+ templates, AI auto-captioning, simple REST API. Works with OpenClaw, LangChain, custom bots, and any HTTP client.

Quickstart — one API call

The simplest way to use the API. One call generates a meme and posts it to X:

Generate meme + post to X

curl -X POST https://www.memelair.com/api/v1/memes/generate-and-post \
  -H "Authorization: Bearer xp_your_key" \
  -H "Content-Type: application/json" \
  -d '{"text": "When the code works on the first try"}'

# → {"success":true,"tweet_url":"https://x.com/i/status/..."}

Or just get the meme image without posting:

Generate meme image only

curl -X POST https://www.memelair.com/api/v1/memes/generate \
  -H "Authorization: Bearer xp_your_key" \
  -H "Content-Type: application/json" \
  -d '{"text": "Monday morning standup"}'

# → {"image_base64":"...","template_name":"This Is Fine","texts":[...]}

That's it. AI picks the template, writes the captions, renders the image. Need more control? Keep reading for individual endpoints.

Authentication

All endpoints require a Bearer token. Generate an API key from your dashboardAPI Keys.

Authorization: Bearer xp_your_api_key_here

Keys start with xp_. They're hashed on our end — we never store the raw key. Revoke anytime from the dashboard.

Base URL

https://www.memelair.com/api/v1

Generate meme (one call)

POST/memes/generateAI picks template, writes captions, renders image

Send text, get a finished meme image. No need to list templates or call multiple endpoints.

Request

curl -X POST https://www.memelair.com/api/v1/memes/generate \
  -H "Authorization: Bearer xp_your_key" \
  -H "Content-Type: application/json" \
  -d '{"text": "When you deploy on a Friday"}'

Response

{
  "image_base64": "iVBORw0KGgoAAAANS...",
  "url": "data:image/png;base64,...",
  "content_type": "image/png",
  "template_id": "fine",
  "template_name": "This Is Fine",
  "texts": ["The servers are on fire", "This is fine"]
}

Generate meme + post to X (one call)

POST/memes/generate-and-postAI generates meme and posts it to X — all in one request

The ultimate one-liner. Your agent sends text, we generate the meme, attach it, and post to X. Returns the tweet URL.

Request

curl -X POST https://www.memelair.com/api/v1/memes/generate-and-post \
  -H "Authorization: Bearer xp_your_key" \
  -H "Content-Type: application/json" \
  -d '{"text": "When the intern pushes to main"}'

Response

{
  "success": true,
  "tweet_id": "1895123456789012345",
  "tweet_url": "https://x.com/i/status/1895123456789012345",
  "template_id": "fine",
  "template_name": "This Is Fine",
  "meme_texts": ["The production database", "This is fine"]
}

Text must be ≤280 characters. Requires an X account linked at the Meme Lair dashboard.

OpenAPI spec

Machine-readable spec for auto-discovery in agent frameworks (OpenClaw, LangChain, AutoGPT):

GET https://www.memelair.com/api/v1/openapi.json

Returns OpenAPI 3.1 JSON. Point your agent framework at this URL and it will discover all endpoints automatically.


Advanced endpoints — for agents that need more control

List templates

GET/memesList all meme templates

Returns all 700+ templates with IDs, names, box counts, and preview URLs.

Request

curl https://www.memelair.com/api/v1/memes \
  -H "Authorization: Bearer xp_your_key"

Response

{
  "memes": [
    {
      "id": "drake",
      "name": "Drake Hotline Bling",
      "box_count": 2,
      "preview_url": "https://www.memelair.com/api/v1/memes/drake/image"
    },
    {
      "id": "distracted",
      "name": "Distracted Boyfriend",
      "box_count": 3,
      "preview_url": "https://www.memelair.com/api/v1/memes/distracted/image"
    }
  ],
  "total": 210
}

Template image

GET/memes/{'{id}'}/imageGet template preview image

Returns the raw image file. Add ?thumb=1 for a 300x300 JPEG thumbnail.

Request

# Full size
curl https://www.memelair.com/api/v1/memes/drake/image \
  -H "Authorization: Bearer xp_your_key" -o drake.png

# Thumbnail
curl "https://www.memelair.com/api/v1/memes/drake/image?thumb=1" \
  -H "Authorization: Bearer xp_your_key" -o drake-thumb.jpg

Create a captioned meme

POST/memes/captionOverlay text on a meme template

Send a template_id and an array of texts matching the template's box_count.

Request

curl -X POST https://www.memelair.com/api/v1/memes/caption \
  -H "Authorization: Bearer xp_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "template_id": "drake",
    "texts": ["Running a meme server", "One API call to Meme Lair"]
  }'

Response (JSON, default)

{
  "url": "data:image/png;base64,...",
  "image_base64": "iVBORw0KGgo...",
  "content_type": "image/png"
}

Output formats:

image_base64 — raw base64 string, decode to get PNG bytes

url — data URI, embed directly in HTML

Set "format": "raw" in body or Accept: image/png header → returns raw PNG binary

Mork auto-generate meme

POST/memes/autoAI picks template + writes captions

Send text, and Mork picks the funniest template and writes the captions. Then pass the result to /memes/caption to render.

Request

curl -X POST https://www.memelair.com/api/v1/memes/auto \
  -H "Authorization: Bearer xp_your_key" \
  -H "Content-Type: application/json" \
  -d '{"text": "Just mass-deployed to prod on a Friday"}'

Response

{
  "template_id": "fine",
  "template_name": "This Is Fine",
  "texts": ["The servers are on fire", "This is fine"]
}

This endpoint only returns the template selection + text. Chain it with POST /memes/caption to get the image.

Post to X

POST/postPost a tweet (with optional image) to X

Post text and an optional image to X on behalf of your connected account. Pass the meme's url (data URI) from /memes/caption as the image field.

Request

curl -X POST https://www.memelair.com/api/v1/post \
  -H "Authorization: Bearer xp_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "text": "When the code works on first try 😂",
    "image": "data:image/png;base64,iVBORw0KGgo..."
  }'

Response

{
  "success": true,
  "tweet_id": "1895123456789",
  "tweet_url": "https://x.com/i/status/1895123456789"
}

Notes:

text — required, max 280 characters

image — optional, data URL or HTTP image URL

User must have connected their X account at the Meme Lair dashboard first

Image uploads require the "Enable image uploads" one-time setup in the dashboard

Full example

Generate a meme and post it to X in 3 API calls:

curl — AI meme → post to X (3 calls)

# Step 1: AI picks template + writes text
RESULT=$(curl -s -X POST https://www.memelair.com/api/v1/memes/auto \
  -H "Authorization: Bearer xp_your_key" \
  -H "Content-Type: application/json" \
  -d '{"text": "Just shipped a feature nobody asked for"}')

TEMPLATE=$(echo $RESULT | jq -r .template_id)
TEXTS=$(echo $RESULT | jq -c .texts)

# Step 2: Render the meme image
MEME=$(curl -s -X POST https://www.memelair.com/api/v1/memes/caption \
  -H "Authorization: Bearer xp_your_key" \
  -H "Content-Type: application/json" \
  -d "{\"template_id\":\"$TEMPLATE\",\"texts\":$TEXTS}")

IMAGE_URL=$(echo $MEME | jq -r .url)

# Step 3: Post to X with the meme
curl -X POST https://www.memelair.com/api/v1/post \
  -H "Authorization: Bearer xp_your_key" \
  -H "Content-Type: application/json" \
  -d "{\"text\":\"Just shipped a feature nobody asked for 😂\",\"image\":\"$IMAGE_URL\"}"

# Response: {"success":true,"tweet_id":"...","tweet_url":"https://x.com/i/status/..."}

Agent integration

Drop-in examples for popular agent frameworks. Your agent writes the content, our API generates the meme.

Python — full flow: generate meme + post to X

import requests

API_KEY = "xp_your_key"
BASE = "https://www.memelair.com/api/v1"
HEADERS = {
    "Authorization": f"Bearer {API_KEY}",
    "Content-Type": "application/json"
}

def generate_and_post_meme(post_text: str):
    """Generate a meme from text and post it to X — 3 API calls."""
    
    # Step 1: AI picks template + writes captions
    auto = requests.post(
        f"{BASE}/memes/auto",
        headers=HEADERS,
        json={"text": post_text}
    ).json()
    print(f"Template: {auto['template_name']}")
    print(f"Captions: {auto['texts']}")
    
    # Step 2: Render the meme image
    meme = requests.post(
        f"{BASE}/memes/caption",
        headers=HEADERS,
        json={
            "template_id": auto["template_id"],
            "texts": auto["texts"]
        }
    ).json()
    
    # Step 3: Post to X with the meme image
    result = requests.post(
        f"{BASE}/post",
        headers=HEADERS,
        json={
            "text": post_text,
            "image": meme["url"]   # pass the data URL directly
        }
    ).json()
    
    print(f"Posted! {result['tweet_url']}")
    return result

# One function call does everything:
generate_and_post_meme("When the code works on first try 😂")

Python — OpenClaw / LangChain tool definition

from langchain.tools import tool

@tool
def post_meme(text: str) -> str:
    """Generate an AI meme and post it to X with the given text."""
    import requests
    
    KEY = "xp_your_key"
    BASE = "https://www.memelair.com/api/v1"
    H = {"Authorization": f"Bearer {KEY}", "Content-Type": "application/json"}
    
    auto = requests.post(f"{BASE}/memes/auto", headers=H, json={"text": text}).json()
    meme = requests.post(f"{BASE}/memes/caption", headers=H, json={
        "template_id": auto["template_id"], "texts": auto["texts"]
    }).json()
    result = requests.post(f"{BASE}/post", headers=H, json={
        "text": text, "image": meme["url"]
    }).json()
    
    return f"Posted: {result.get('tweet_url', 'unknown')}"

# Your agent can now call post_meme("Just mass-deployed on a Friday")

Node.js / TypeScript — full flow

const API_KEY = process.env.MEMELAIR_API_KEY!;
const BASE = "https://www.memelair.com/api/v1";
const headers = {
  Authorization: `Bearer ${API_KEY}`,
  "Content-Type": "application/json",
};

async function generateAndPostMeme(postText: string) {
  // Step 1: AI picks the best meme
  const auto = await fetch(`${BASE}/memes/auto`, {
    method: "POST", headers,
    body: JSON.stringify({ text: postText }),
  }).then((r) => r.json());

  // Step 2: Render it
  const meme = await fetch(`${BASE}/memes/caption`, {
    method: "POST", headers,
    body: JSON.stringify({
      template_id: auto.template_id,
      texts: auto.texts,
    }),
  }).then((r) => r.json());

  // Step 3: Post to X with the meme
  const result = await fetch(`${BASE}/post`, {
    method: "POST", headers,
    body: JSON.stringify({
      text: postText,
      image: meme.url,
    }),
  }).then((r) => r.json());

  console.log("Posted!", result.tweet_url);
  return result;
}

// Usage:
await generateAndPostMeme("Nobody asked for this feature but here it is 😅");

Post with your own service (just text, no meme)

# You can also post plain text — no meme required
curl -X POST https://www.memelair.com/api/v1/post \
  -H "Authorization: Bearer xp_your_key" \
  -H "Content-Type: application/json" \
  -d '{"text": "Hello from my agent bot 🤖"}'

# Response: {"success":true,"tweet_id":"...","tweet_url":"..."}

Pick a specific template (skip AI)

# List templates first
templates = requests.get(
    f"{BASE}/memes",
    headers=HEADERS
).json()["memes"]

# Find one by name
drake = next(t for t in templates if t["id"] == "drake")

# Generate with custom text
meme = requests.post(
    f"{BASE}/memes/caption",
    headers=HEADERS,
    json={
        "template_id": "drake",
        "texts": ["Writing unit tests", "Shipping to prod and praying"]
    }
).json()

image_bytes = base64.b64decode(meme["image_base64"])

Pricing & limits

API usage counts against your plan. Same account, same limits.

PlanPriceMemes/monthAI auto-memeWatermark
Free$02525Yes
Lite$9/mo200200No
Unlimited$29/moUnlimitedUnlimitedNo

When you hit the limit, the API returns 429 with code: "limit_reached".

Error codes

HTTPMeaningFix
401Missing or invalid API keyCheck your Authorization header
400Bad requestCheck required fields (template_id, texts)
404Template not foundUse GET /memes to list valid IDs
429Monthly limit reachedUpgrade your plan or wait for next period
502AI service errorRetry after a moment (auto-meme only)

Error response format

{
  "error": "Monthly limit reached (25/25). Upgrade for more.",
  "code": "limit_reached"
}