Astro

OG images in
Astro

Generate images at build time, for the whole collection at once.

Where it plugs in

Astro builds statically by default, so images are created once during the build. The first run costs one render per post; every rebuild after that gets the finished URLs back from cache for free.

src/pages/blog/[...slug].astro

---
import { getCollection } from 'astro:content';
import Layout from '../../layouts/Base.astro';

export async function getStaticPaths() {
  const posts = await getCollection('blog');
  return posts.map((post) => ({ params: { slug: post.slug }, props: { post } }));
}

const { post } = Astro.props;

const res = await fetch('https://imageapi.dev/v1/images?wait=true', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${import.meta.env.IMAGE_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    template: 'blog-og',
    modifications: {
      title: post.data.title,
      author: post.data.author,
      date: post.data.pubDate.toISOString(),
      site: 'yourblog.com',
    },
  }),
});

const { url: ogImage } = await res.json();
---

<Layout title={post.data.title} ogImage={ogImage}>
  <slot />
</Layout>
The trap

A large collection fires many requests at once. If you see a 503 `sync_overloaded`, drop `?wait=true` and collect the URLs by ID afterwards — or simply retry, because the job is never lost.

What comes back

With ?wait=true the finished image arrives on the same connection — usually in about a second.

{
    "id": "img_01m08s3ez1zcv28h335g9923bw",
    "status": "completed",
    "url": "https://cdn.imageapi.dev/renders/img_01m08s3ez1zcv28h335g9923bw.png",
    "duration_ms": 912
}

What it costs

A hundred images a month free, then $9 for two thousand. The same post rendered twice returns the existing URL and uses no quota — so rebuilding your site costs nothing.

Other frameworks