An async filter that returns the image URL inside the template.
Eleventy supports async filters, so you can fetch the image URL directly in the template. The built-in cache in `eleventy-fetch` means the next build does not touch the API at all.
.eleventy.js
const EleventyFetch = require('@11ty/eleventy-fetch');
module.exports = function (eleventyConfig) {
eleventyConfig.addAsyncFilter('ogImage', async (title, author) => {
const response = await EleventyFetch('https://imageapi.dev/v1/images?wait=true', {
duration: '1y', // same post = same image
type: 'json',
fetchOptions: {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.IMAGE_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
template: 'blog-og',
modifications: { title, author, site: 'yourblog.com' },
}),
},
});
return response.url;
});
};
// In the template:
// <meta property="og:image" content="{{ title | ogImage(author) }}">
`eleventy-fetch` caches by URL, not by request body. With POST, add the title to the cache key (a query parameter works) — otherwise every post gets the same image.
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
}
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.