Generate beautiful Open Graph images with a single URL. No signup required.
Try the API right here β edit the parameters and see a live preview. No signup needed.
GET https://ogmagic.dev/api/og?template=gradient-mesh&title=Hello+World&description=Your+amazing+description+here&domain=example.com<meta property="og:image" content="https://ogmagic.dev/api/og?template=gradient-mesh&title=Hello+World&description=Your+amazing+description+here&domain=example.com" /> <meta name="twitter:card" content="summary_large_image" /> <meta name="twitter:image" content="https://ogmagic.dev/api/og?template=gradient-mesh&title=Hello+World&description=Your+amazing+description+here&domain=example.com" />
Generate an OG image by making a GET request. No API key needed for the free tier:
https://ogmagic.dev/api/og?title=My%20Blog%20Post&description=A%20great%20article&template=gradient-meshUse it as your og:image meta tag:
<meta property="og:image" content="https://ogmagic.dev/api/og?title=My+Blog+Post&template=gradient-mesh" />That's it. Your links now have beautiful social previews.
https://ogmagic.dev/api/ogReturns a 1200Γ630 PNG image.
All parameters are passed as URL query strings. The response is a PNG image with CDN cache headers (24h edge cache, 1h browser cache).
Copy-paste these examples to get OG images working in your project in under 2 minutes.
Dynamic OG images for any page using generateMetadata:
// app/blog/[slug]/page.tsx
import { Metadata } from 'next'
interface PageProps {
params: { slug: string }
}
export async function generateMetadata({ params }: PageProps): Promise<Metadata> {
const post = await getPost(params.slug) // Your data fetching
return {
title: post.title,
description: post.excerpt,
openGraph: {
images: [
{
url: `https://ogmagic.dev/api/og?template=gradient-mesh&title=${encodeURIComponent(post.title)}&description=${encodeURIComponent(post.excerpt)}&domain=myblog.com`,
width: 1200,
height: 630,
},
],
},
}
}Using getStaticProps or getServerSideProps:
// pages/blog/[slug].tsx
import Head from 'next/head'
interface PostPageProps {
post: { title: string; excerpt: string }
}
export default function PostPage({ post }: PostPageProps) {
return (
<>
<Head>
<meta property="og:image" content={`https://ogmagic.dev/api/og?template=gradient-mesh&title=${encodeURIComponent(post.title)}&domain=myblog.com`} />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<meta name="twitter:card" content="summary_large_image" />
</Head>
{/* Your page content */}
</>
)
}Create a reusable OGImage component:
// components/OGImage.tsx
interface OGImageProps {
title: string;
description?: string;
template?: string;
domain?: string;
}
export function OGImage({ title, description, template = "gradient-mesh", domain }: OGImageProps) {
const params = new URLSearchParams({
template,
title,
...(description && { description }),
...(domain && { domain }),
});
return (
<>
<meta property="og:image" content={`https://ogmagic.dev/api/og?${params.toString()}`} />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:image" content={`https://ogmagic.dev/api/og?${params.toString()}`} />
</>
);
}
// Usage in any page:
// <OGImage title="My Post" description="My description" domain="mysite.com" />Generate OG image URLs in your backend:
// utils/og-image.js
export function getOGImageUrl(title, options = {}) {
const { description, template = 'gradient-mesh', domain } = options;
const params = new URLSearchParams({
template,
title,
});
if (description) params.append('description', description);
if (domain) params.append('domain', domain);
return `https://ogmagic.dev/api/og?${params.toString()}`;
}
// Usage in Express route:
app.get('/blog/:slug', (req, res) => {
const post = getPost(req.params.slug);
const ogImage = getOGImageUrl(post.title, {
description: post.excerpt,
domain: 'myblog.com',
});
res.render('post', { post, ogImage });
});Add OG images to your Flask templates:
# app.py
from urllib.parse import urlencode
def get_og_image_url(title, description=None, template='gradient-mesh', domain=None):
params = {'template': template, 'title': title}
if description:
params['description'] = description
if domain:
params['domain'] = domain
return f"https://ogmagic.dev/api/og?{urlencode(params)}"
@app.route('/blog/<slug>')
def blog_post(slug):
post = get_post(slug)
og_image = get_og_image_url(
post['title'],
description=post['excerpt'],
domain='myblog.com'
)
return render_template('post.html', post=post, og_image=og_image)
# In your template:
# <meta property="og:image" content="{{ og_image }}" />Using useHead in Nuxt 3:
// pages/blog/[slug].vue
<script setup>
const route = useRoute()
const { data: post } = await useFetch(`/api/posts/${route.params.slug}`)
const ogImageUrl = computed(() => {
const params = new URLSearchParams({
template: 'gradient-mesh',
title: post.value.title,
description: post.value.excerpt,
domain: 'myblog.com',
})
return `https://ogmagic.dev/api/og?${params.toString()}`
})
useHead({
title: post.value.title,
meta: [
{ property: 'og:image', content: ogImageUrl.value },
{ property: 'og:image:width', content: '1200' },
{ property: 'og:image:height', content: '630' },
{ name: 'twitter:card', content: 'summary_large_image' },
{ name: 'twitter:image', content: ogImageUrl.value },
],
})
</script>Create a helper that generates OG URLs for your entire site:
// lib/og-generator.ts
interface OGOptions {
title: string;
description?: string;
template?: string;
domain?: string;
author?: string;
accent?: string;
}
export class OGImageGenerator {
private baseUrl = 'https://ogmagic.dev/api/og';
private defaultDomain: string;
constructor(defaultDomain: string) {
this.defaultDomain = defaultDomain;
}
generate(options: OGOptions): string {
const params = new URLSearchParams({
template: options.template || 'gradient-mesh',
title: options.title,
});
if (options.description) params.append('description', options.description);
if (options.domain || this.defaultDomain) {
params.append('domain', options.domain || this.defaultDomain);
}
if (options.author) params.append('author', options.author);
if (options.accent) params.append('accent', options.accent);
return `${this.baseUrl}?${params.toString()}`;
}
// Preset for blog posts
blogPost(title: string, excerpt: string): string {
return this.generate({
title,
description: excerpt,
template: 'gradient-mesh',
});
}
// Preset for product pages
product(name: string, price: string): string {
return this.generate({
title: name,
description: price + ' β Available now',
template: 'minimal-dark',
});
}
}
// Usage:
const og = new OGImageGenerator('myblog.com');
const blogOG = og.blogPost('My Post', 'Great excerpt');
const productOG = og.product('My Product', '$99');π‘ Pro tip: All examples use the free tier (50 calls/month, no signup). Add a key parameter with your Pro license key to unlock 5,000 calls/month and all 55+ templates.
| Parameter | Type | Required | Description |
|---|---|---|---|
title | string | No* | Main text. Defaults to "Hello World" |
description | string | No | Subtitle / description text |
template | string | No | Template ID. Defaults to "gradient-mesh" |
author | string | No | Author name (supported by some templates) |
domain | string | No | Domain / URL to display |
accent | string | No | Accent color override (e.g. %23ff6600 β URL-encode the #) |
width | number | No | Image width in px (Pro only, 200β2400, default 1200) |
height | number | No | Image height in px (Pro only, 200β1400, default 630) |
key | string | No | Pro license key for premium templates & higher limits |
OGMagic ships with 50 templates across categories like Gradient, Dark, Minimal, Bold, Professional, Retro, and more.
gradient-meshminimal-darksplit-cardbold-statementsunriseIncludes Tokyo Night, Dracula, Nord, RosΓ© Pine, Synthwave, Linear Style, Vercel Style, and many more. Browse all in the editor β
Get a list of all templates via the API:
GET https://ogmagic.dev/api/templatesFree tier: No authentication needed. Just use the URL directly.
Pro tier: Add your license key as a key query parameter:
https://ogmagic.dev/api/og?title=My+Post&template=tokyo-night&key=YOUR_LICENSE_KEYβ οΈ Keep your license key private. Use environment variables in CI/CD, not hardcoded in public repos.
50 requests/month
IP-based. No signup needed.
5,000 requests/month
Key-based. One-time purchase.
Rate limit info is returned in response headers: X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset.
Use the OGMagic API directly in your Next.js app β no packages to install.
// app/blog/[slug]/page.tsx
function ogUrl(params: Record<string, string>) {
const qs = new URLSearchParams(params).toString();
return `https://ogmagic.dev/api/og?${qs}`;
}
export function generateMetadata({ params }) {
const post = getPost(params.slug);
const image = ogUrl({
template: "minimal-dark",
title: post.title,
description: post.excerpt,
});
return {
openGraph: { images: [{ url: image, width: 1200, height: 630 }] },
twitter: { card: "summary_large_image", images: [image] },
};
}// pages/blog/[slug].tsx
import Head from "next/head";
export default function BlogPost({ post }) {
const ogImage = `https://ogmagic.dev/api/og?template=stripe-style&title=${encodeURIComponent(post.title)}&description=${encodeURIComponent(post.excerpt)}`;
return (
<>
<Head>
<meta property="og:image" content={ogImage} />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:image" content={ogImage} />
</Head>
<article>{/* ... */}</article>
</>
);
}Use OGMagic in your Remix routes with the meta export:
// app/routes/blog.$slug.tsx
import type { MetaFunction, LoaderFunctionArgs } from "@remix-run/node";
export const meta: MetaFunction<typeof loader> = ({ data }) => {
const ogImage = `https://ogmagic.dev/api/og?${new URLSearchParams({
template: "minimal-dark",
title: data.post.title,
description: data.post.excerpt,
domain: "yourblog.com",
})}`;
return [
{ property: "og:image", content: ogImage },
{ property: "og:image:width", content: "1200" },
{ property: "og:image:height", content: "630" },
{ name: "twitter:card", content: "summary_large_image" },
{ name: "twitter:image", content: ogImage },
];
};In SvelteKit, set OG images in your +page.server.ts or +layout.server.ts:
<!-- src/routes/blog/[slug]/+page.svelte -->
<script>
export let data;
const ogImage = `https://ogmagic.dev/api/og?${new URLSearchParams({
template: "stripe-style",
title: data.post.title,
description: data.post.excerpt,
})}`;
</script>
<svelte:head>
<meta property="og:image" content={ogImage} />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:image" content={ogImage} />
</svelte:head>Use the API URL directly in your Astro layouts β zero dependencies.
---
// src/layouts/BlogPost.astro
const { frontmatter } = Astro.props;
const ogParams = new URLSearchParams({
template: "minimal-dark",
title: frontmatter.title,
description: frontmatter.description || "",
}).toString();
const ogImage = `https://ogmagic.dev/api/og?${ogParams}`;
---
<html>
<head>
<meta property="og:image" content={ogImage} />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:image" content={ogImage} />
</head>
<body>
<slot />
</body>
</html>Add OG images to your Gatsby pages using the Seo component or gatsby-plugin-react-helmet:
// src/components/seo.tsx
import { Helmet } from "react-helmet";
export function Seo({ title, description }: { title: string; description?: string }) {
const ogImage = `https://ogmagic.dev/api/og?${new URLSearchParams({
template: "minimal-dark",
title,
description: description || "",
domain: "yourblog.com",
})}`;
return (
<Helmet>
<meta property="og:image" content={ogImage} />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:image" content={ogImage} />
</Helmet>
);
}
// Usage in a page or template:
// <Seo title={post.frontmatter.title} description={post.frontmatter.excerpt} />Works with both gatsby-plugin-react-helmet and Gatsby's built-in Head API (Gatsby 4.19+).
Add OG images to your Hugo site by editing your base template (e.g., layouts/partials/head.html):
{{/* layouts/partials/head.html */}}
{{ $ogTitle := .Title | urlquery }}
{{ $ogDesc := .Description | urlquery }}
{{ $ogDomain := .Site.BaseURL | urlquery }}
{{ $ogImage := printf "https://ogmagic.dev/api/og?template=stripe-style&title=%s&description=%s&domain=%s" $ogTitle $ogDesc $ogDomain }}
<meta property="og:image" content="{{ $ogImage }}" />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:image" content="{{ $ogImage }}" />Hugo's urlquery function handles URL-encoding automatically. You can also use different templates per section using Hugo's section conditionals.
Add OG images to your WordPress theme by editing functions.php or your theme's header.php:
<?php // functions.php
function ogmagic_meta_tags() {
if (is_singular()) {
$title = urlencode(get_the_title());
$excerpt = urlencode(get_the_excerpt());
$domain = urlencode(parse_url(home_url(), PHP_URL_HOST));
$og_image = "https://ogmagic.dev/api/og?template=stripe-style&title={$title}&description={$excerpt}&domain={$domain}";
echo '<meta property="og:image" content="' . esc_attr($og_image) . '" />' . "\n";
echo '<meta property="og:image:width" content="1200" />' . "\n";
echo '<meta property="og:image:height" content="630" />' . "\n";
echo '<meta name="twitter:card" content="summary_large_image" />' . "\n";
echo '<meta name="twitter:image" content="' . esc_attr($og_image) . '" />' . "\n";
}
}
add_action('wp_head', 'ogmagic_meta_tags');If you use Yoast SEO or Rank Math, you can use their OG image filter hooks instead. Works with any WordPress theme.
Use OGMagic in your Laravel Blade templates:
{{-- resources/views/layouts/app.blade.php --}}
@php
$ogImage = 'https://ogmagic.dev/api/og?' . http_build_query([
'template' => 'minimal-dark',
'title' => $title ?? config('app.name'),
'description' => $description ?? '',
'domain' => parse_url(config('app.url'), PHP_URL_HOST),
]);
@endphp
<meta property="og:image" content="{{ $ogImage }}" />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:image" content="{{ $ogImage }}" />Pass $title and $description from your controller or use the @section directive.
Add OG images in your Django templates:
{# base.html #}
{% load urlencode from django.utils.http %}
{% block og_meta %}
<meta property="og:image" content="https://ogmagic.dev/api/og?template=gradient-mesh&title={{ page_title|urlencode }}&description={{ page_description|urlencode }}&domain={{ request.get_host|urlencode }}" />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:image" content="https://ogmagic.dev/api/og?template=gradient-mesh&title={{ page_title|urlencode }}" />
{% endblock %}Or build the URL in your view with urllib.parse.urlencode() for full control. Works with Django, Flask, FastAPI, or any Python framework.
Add OG images to your Rails views using a helper or directly in your layout:
<%# app/helpers/og_image_helper.rb %>
module OgImageHelper
def og_image_url(title:, description: nil, template: "gradient-mesh", domain: nil)
params = { template: template, title: title }
params[:description] = description if description.present?
params[:domain] = domain || request.host if domain != false
"https://ogmagic.dev/api/og?#{params.to_query}"
end
end
<%# app/views/layouts/application.html.erb %>
<% og_url = og_image_url(title: yield(:title) || "My App", description: yield(:description)) %>
<meta property="og:image" content="<%= og_url %>" />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:image" content="<%= og_url %>" />
<%# In any view: %>
<% content_for(:title) { "My Blog Post" } %>
<% content_for(:description) { "A great article about Rails" } %>Works with any Rails version. For API-only apps, build the URL in your serializer or controller.
For any website, add these meta tags to your <head>:
<!-- Open Graph -->
<meta property="og:image" content="https://ogmagic.dev/api/og?title=My+Page&template=gradient-mesh" />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<!-- Twitter Card -->
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:image" content="https://ogmagic.dev/api/og?title=My+Page&template=gradient-mesh" />| Status | Meaning |
|---|---|
200 | Success β PNG image returned |
400 | Bad request β unknown template ID |
429 | Rate limit exceeded |
500 | Server error |
Error responses return JSON with an error field:
{ "error": "Unknown template: foo. Available: gradient-mesh, minimal-dark, ..." }