Meme Generator API: Build Your Own Meme Platform
I built a meme generator as a side project in 2018. It went viral on Product Hunt, crashed my server, and taught me that people really love making memes.
The technical part wasn't hard. Take an image, overlay text on top and bottom, return the meme. What surprised me was how much effort went into making text look right—the font, the stroke, the positioning. Get those details wrong and your memes look amateur.
Five years later, I rebuilt it with an image generation API. Same functionality, 80% less code, and it scaled to 50,000 memes per day without breaking a sweat.
Here's how to build a meme generator that actually works.
Why build a meme generatorWhy Build a Meme Generator?
Memes are internet currency. They're shareable, engaging, and drive massive traffic.
Common use cases:
- Social media tools (let users create memes to share)
- Marketing campaigns (branded meme generators for viral content)
- Community platforms (meme creation as a feature)
- Content creation tools (memes as part of a larger toolkit)
- Educational tools (surprisingly, teachers use memes in lessons)
Building a meme generator from scratch means dealing with image manipulation, text rendering, font management, and performance optimization. Using an API handles all that complexity.
How meme generation worksHow Meme Generation Works
The concept is simple: background image + text overlay = meme.
The flow:
- User selects a meme template (or uploads their own)
- User enters top text and bottom text
- API renders the image with text overlaid
- Return the meme to download or share
The complexity is in rendering text properly. Professional memes have:
- Bold, readable fonts (usually Impact)
- White text with black stroke (readable on any background)
- Centered alignment
- Proper spacing and sizing
- Text that doesn't overflow or look cramped
Popular meme formatsPopular Meme Formats
Different meme templates need different layouts.
Topbottom text memes classicTop/Bottom Text Memes (Classic)
The original meme format. Text at the top, text at the bottom, image in the middle.
Examples:
- Distracted Boyfriend
- Drake Hotline Bling
- Two Buttons
- Always Has Been
Template structure:
- Top text area (centered, large font)
- Image content in middle
- Bottom text area (centered, large font)
Single caption memesSingle Caption Memes
Modern memes often use just one caption, usually at the bottom.
Examples:
- This Is Fine
- Woman Yelling at Cat
- Is This a Pigeon?
Template structure:
- Full image
- Single text area (top or bottom, depending on template)
Multi panel memesMulti-Panel Memes
Memes with multiple frames telling a story.
Examples:
- Brain Meme (expanding brain)
- Gru's Plan (4-panel)
- Surprised Pikachu
Template structure:
- Multiple image sections
- Text areas specific to each panel
- Sometimes text inside panels, sometimes captions below
Label memesLabel Memes
Templates where you label different parts of the image.
Examples:
- Spider-Man Pointing (labels on each Spider-Man)
- Galaxy Brain (labels on each brain level)
- Expanding Brain (similar)
Template structure:
- Multiple small text areas positioned on specific image elements
How to build a meme generatorHow to Build a Meme Generator
The technical implementation is straightforward with an API.
Step 1 create meme templatesStep 1: Create Meme Templates
Design templates for popular meme formats. Each template defines the background image and editable text areas.
For classic top/bottom memes, your template needs:
- Background image
- Top text field (large, white, black stroke)
- Bottom text field (large, white, black stroke)
In Imejis.io, upload the meme background and add two text layers positioned at top and bottom. Mark them as editable so the API can change them.
Step 2 build the user interfaceStep 2: Build the User Interface
Create a simple form where users enter their meme text.
<form id="meme-generator">
<select id="template">
<option value="distracted-boyfriend">Distracted Boyfriend</option>
<option value="drake">Drake Hotline Bling</option>
<option value="two-buttons">Two Buttons</option>
</select>
<input type="text" id="top-text" placeholder="Top text" maxlength="100" />
<input
type="text"
id="bottom-text"
placeholder="Bottom text"
maxlength="100"
/>
<button type="submit">Generate Meme</button>
</form>
<div id="meme-result">
<!-- Generated meme appears here -->
</div>Step 3 call the api to generate memesStep 3: Call the API to Generate Memes
When the user submits, send their text to the API and display the generated meme.
document
.getElementById("meme-generator")
.addEventListener("submit", async (e) => {
e.preventDefault()
const template = document.getElementById("template").value
const topText = document.getElementById("top-text").value
const bottomText = document.getElementById("bottom-text").value
const meme = await generateMeme(template, topText, bottomText)
// Display the meme
document.getElementById(
"meme-result"
).innerHTML = `<img src="${meme.url}" alt="Generated meme" />
<a href="${meme.url}" download>Download Meme</a>`
})
async function generateMeme(template, topText, bottomText) {
const templateIds = {
"distracted-boyfriend": "TEMPLATE_ID_1",
drake: "TEMPLATE_ID_2",
"two-buttons": "TEMPLATE_ID_3",
}
const response = await fetch(
`https://render.imejis.io/v1/${templateIds[template]}`,
{
method: "POST",
headers: {
Authorization: "Bearer YOUR_API_KEY",
"Content-Type": "application/json",
},
body: JSON.stringify({
top_text: topText.toUpperCase(), // Classic memes use uppercase
bottom_text: bottomText.toUpperCase(),
}),
}
)
const blob = await response.blob()
const url = URL.createObjectURL(blob)
return { url }
}Complete code examplesComplete Code Examples
Here's how to build a meme generator in different environments.
React meme generatorReact Meme Generator
import React, { useState } from "react"
import axios from "axios"
function MemeGenerator() {
const [template, setTemplate] = useState("distracted-boyfriend")
const [topText, setTopText] = useState("")
const [bottomText, setBottomText] = useState("")
const [memeUrl, setMemeUrl] = useState(null)
const [loading, setLoading] = useState(false)
const templates = {
"distracted-boyfriend": "TEMPLATE_ID_1",
drake: "TEMPLATE_ID_2",
"two-buttons": "TEMPLATE_ID_3",
}
const generateMeme = async () => {
setLoading(true)
try {
const response = await axios({
method: "post",
url: `https://render.imejis.io/v1/${templates[template]}`,
headers: {
Authorization: "Bearer YOUR_API_KEY",
"Content-Type": "application/json",
},
data: {
top_text: topText.toUpperCase(),
bottom_text: bottomText.toUpperCase(),
},
responseType: "blob",
})
const url = URL.createObjectURL(response.data)
setMemeUrl(url)
} catch (error) {
console.error("Error generating meme:", error)
alert("Failed to generate meme. Please try again.")
} finally {
setLoading(false)
}
}
return (
<div className="meme-generator">
<h1>Meme Generator</h1>
<div className="controls">
<select value={template} onChange={(e) => setTemplate(e.target.value)}>
<option value="distracted-boyfriend">Distracted Boyfriend</option>
<option value="drake">Drake Hotline Bling</option>
<option value="two-buttons">Two Buttons</option>
</select>
<input
type="text"
placeholder="Top text"
value={topText}
onChange={(e) => setTopText(e.target.value)}
maxLength={100}
/>
<input
type="text"
placeholder="Bottom text"
value={bottomText}
onChange={(e) => setBottomText(e.target.value)}
maxLength={100}
/>
<button onClick={generateMeme} disabled={loading}>
{loading ? "Generating..." : "Generate Meme"}
</button>
</div>
{memeUrl && (
<div className="meme-result">
<img src={memeUrl} alt="Generated meme" />
<a href={memeUrl} download="meme.png">
Download Meme
</a>
<button
onClick={() =>
navigator.share({ files: [new File([memeUrl], "meme.png")] })
}
>
Share Meme
</button>
</div>
)}
</div>
)
}
export default MemeGeneratorPython flask backendPython Flask Backend
from flask import Flask, request, jsonify, send_file
import requests
import io
app = Flask(__name__)
TEMPLATES = {
'distracted-boyfriend': 'TEMPLATE_ID_1',
'drake': 'TEMPLATE_ID_2',
'two-buttons': 'TEMPLATE_ID_3'
}
@app.route('/generate-meme', methods=['POST'])
def generate_meme():
data = request.json
template = data.get('template', 'distracted-boyfriend')
top_text = data.get('top_text', '').upper()
bottom_text = data.get('bottom_text', '').upper()
# Validate input
if not top_text and not bottom_text:
return jsonify({'error': 'Please provide at least one text field'}), 400
# Generate meme
template_id = TEMPLATES.get(template)
response = requests.post(
f'https://render.imejis.io/v1/{template_id}',
headers={
'Authorization': 'Bearer YOUR_API_KEY',
'Content-Type': 'application/json'
},
json={
'top_text': top_text,
'bottom_text': bottom_text
}
)
if response.status_code == 200:
# Return image directly
return send_file(
io.BytesIO(response.content),
mimetype='image/png',
as_attachment=True,
download_name='meme.png'
)
else:
return jsonify({'error': 'Failed to generate meme'}), 500
@app.route('/templates', methods=['GET'])
def list_templates():
return jsonify({
'templates': [
{'id': 'distracted-boyfriend', 'name': 'Distracted Boyfriend'},
{'id': 'drake', 'name': 'Drake Hotline Bling'},
{'id': 'two-buttons', 'name': 'Two Buttons'}
]
})
if __name__ == '__main__':
app.run(debug=True)Nodejs express apiNode.js + Express API
const express = require("express")
const axios = require("axios")
const app = express()
app.use(express.json())
const TEMPLATES = {
"distracted-boyfriend": "TEMPLATE_ID_1",
drake: "TEMPLATE_ID_2",
"two-buttons": "TEMPLATE_ID_3",
}
app.post("/api/generate-meme", async (req, res) => {
const { template, topText, bottomText } = req.body
// Validate input
if (!topText && !bottomText) {
return res
.status(400)
.json({ error: "Please provide at least one text field" })
}
const templateId = TEMPLATES[template] || TEMPLATES["distracted-boyfriend"]
try {
const response = await axios({
method: "post",
url: `https://render.imejis.io/v1/${templateId}`,
headers: {
Authorization: "Bearer YOUR_API_KEY",
"Content-Type": "application/json",
},
data: {
top_text: topText.toUpperCase(),
bottom_text: bottomText.toUpperCase(),
},
responseType: "arraybuffer",
})
// Return image
res.set("Content-Type", "image/png")
res.send(response.data)
} catch (error) {
console.error("Error generating meme:", error)
res.status(500).json({ error: "Failed to generate meme" })
}
})
app.get("/api/templates", (req, res) => {
res.json({
templates: [
{ id: "distracted-boyfriend", name: "Distracted Boyfriend" },
{ id: "drake", name: "Drake Hotline Bling" },
{ id: "two-buttons", name: "Two Buttons" },
],
})
})
app.listen(3000, () => {
console.log("Meme generator API running on port 3000")
})Advanced featuresAdvanced Features
Take your meme generator beyond basic top/bottom text.
User uploaded imagesUser-Uploaded Images
Let users upload their own images as meme backgrounds.
async function uploadAndCreateMeme(imageFile, topText, bottomText) {
// Upload image to your storage
const formData = new FormData()
formData.append("image", imageFile)
const uploadResponse = await fetch("/api/upload-image", {
method: "POST",
body: formData,
})
const { imageUrl } = await uploadResponse.json()
// Generate meme with uploaded image
const response = await fetch(
"https://render.imejis.io/v1/CUSTOM_TEMPLATE_ID",
{
method: "POST",
headers: {
Authorization: "Bearer YOUR_API_KEY",
"Content-Type": "application/json",
},
body: JSON.stringify({
background_image: imageUrl,
top_text: topText.toUpperCase(),
bottom_text: bottomText.toUpperCase(),
}),
}
)
return response.blob()
}Text customizationText Customization
Let users customize font size, color, and position.
{
top_text: "When you finally fix the bug",
bottom_text: "But introduce two more",
text_size: 48,
text_color: "#FFFFFF",
stroke_color: "#000000",
stroke_width: 2,
text_align: "center"
}Meme galleryMeme Gallery
Store generated memes and display a gallery of recent or popular memes.
// Save meme to database after generation
await saveMeme({
template: template,
topText: topText,
bottomText: bottomText,
imageUrl: memeUrl,
createdBy: userId,
likes: 0,
})
// Display gallery
const recentMemes = await getRecentMemes(20)Social sharingSocial Sharing
Add one-click sharing to social media platforms.
// Web Share API
async function shareMeme(memeUrl) {
if (navigator.share) {
try {
const response = await fetch(memeUrl)
const blob = await response.blob()
const file = new File([blob], "meme.png", { type: "image/png" })
await navigator.share({
title: "Check out this meme!",
files: [file],
})
} catch (error) {
console.error("Error sharing:", error)
}
} else {
// Fallback: copy link or open share dialog
copyToClipboard(memeUrl)
}
}Ai generated captionsAI-Generated Captions
Use AI to suggest funny captions based on the template.
async function suggestCaptions(template) {
// Call AI API (OpenAI, Claude, etc.)
const response = await fetch("/api/suggest-captions", {
method: "POST",
body: JSON.stringify({ template }),
})
const { suggestions } = await response.json()
// Display suggestions to user
return suggestions
}Template design best practicesTemplate Design Best Practices
Good meme templates make text readable and positioning easy.
Classic impact font styleClassic Impact Font Style
The standard meme text style:
- Font: Impact or Arial Black
- Color: White (#FFFFFF)
- Stroke: Black (#000000), 2-4px width
- Transform: Uppercase
- Alignment: Center
This works on any background because of the black stroke.
Text positioningText Positioning
Leave enough space for text without covering important image elements.
Top text area:
- 10-15% from the top
- Centered horizontally
- Large enough for 2 lines if needed
Bottom text area:
- 10-15% from the bottom
- Centered horizontally
- Large enough for 2 lines if needed
Handle text overflowHandle Text Overflow
Users will try to write novels. Plan for it.
Solutions:
- Set character limits (100 characters per text field)
- Auto-size text (shrink font if text is long)
- Wrap text to multiple lines (max 2-3 lines)
- Truncate with ellipsis if really needed
Test your templates with the longest realistic captions before launching.
Mobile optimizationMobile Optimization
Most memes are created and viewed on mobile.
- Readable at small sizes (600x600px minimum)
- Touch-friendly UI elements
- Fast image loading
- Easy sharing to social apps
Content moderationContent Moderation
Public meme generators attract trolls. Plan for moderation.
Text filteringText Filtering
Filter inappropriate text before generation.
const bannedWords = ['offensive', 'words', 'here'];
function moderateText(text) {
const lowerText = text.toLowerCase();
for (const word of bannedWords) {
if (lowerText.includes(word)) {
return false; // Reject
}
}
return true; // Allow
}
// Before generating
if (!moderateText(topText) || !moderateText(bottomText)) {
return res.status(400).json({ error: 'Inappropriate content detected' });
}Image moderationImage Moderation
If users upload images, moderate them before processing.
Use image moderation APIs (AWS Rekognition, Google Cloud Vision, or dedicated services) to detect inappropriate content.
Rate limitingRate Limiting
Prevent abuse with rate limits.
const rateLimit = require("express-rate-limit")
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 20, // Max 20 memes per 15 minutes
message: "Too many memes created. Please try again later.",
})
app.use("/api/generate-meme", limiter)Monetization ideasMonetization Ideas
Turn your meme generator into a business.
Freemium modelFreemium Model
- Free tier: Generate memes with watermark
- Paid tier: Watermark-free memes, more templates, higher resolution
Template marketplaceTemplate Marketplace
Sell premium meme templates to power users.
White label solutionWhite-Label Solution
License your meme generator to brands for their campaigns.
Api accessAPI Access
Offer API access to developers building apps that need meme generation.
AdvertisingAdvertising
Show ads on your meme generator site. Meme platforms get high traffic and engagement.
FaqFAQ
Can i use copyrighted images for memesCan I use copyrighted images for memes?
Legally risky. Most popular meme images exist in a gray area. For commercial platforms, use public domain images, Creative Commons content, or create original templates. The API itself is just a tool—you're responsible for image rights.
How do i handle text wrapping for long captionsHow do I handle text wrapping for long captions?
Design templates with auto-sizing text or set character limits. Most meme generators limit top text to 100 characters, bottom text to 100 characters. Test with your longest realistic input before launching.
Whats the best font for meme textWhat's the best font for meme text?
Impact font is the classic meme standard. Bold, white text with black stroke (outline). It's readable over any background. Most meme generators also offer Arial Black or Anton as alternatives for different meme styles.
Can users upload their own imagesCan users upload their own images?
Yes. Accept image uploads, store them temporarily or in your CDN, then use that URL as the background in your meme template. Remember to moderate uploads if this is a public platform to avoid inappropriate content.
How do i prevent abuse of a public meme generatorHow do I prevent abuse of a public meme generator?
Rate limit API calls per user or IP. Add content moderation for text (filter profanity, hate speech). Consider requiring sign-in for generation. Monitor usage patterns and block bad actors. Most meme platforms use a combination of these methods.
Build the next viral meme platformBuild the Next Viral Meme Platform
Memes drive massive engagement. Every successful social platform has meme generation or sharing at its core.
Building a meme generator from scratch means dealing with image processing, text rendering, font management, and infrastructure scaling. Using an image generation API lets you focus on the user experience and go viral faster.
Design great templates, build a simple interface, connect to the API, and watch people create millions of memes. The technical part is the easy part now.