Generate with a script before the build, write into front matter.
Hugo cannot make HTTP requests at build time in any way you would want to rely on. Generating with a script, writing the URL into front matter and committing it is both simpler and faster.
scripts/og.sh
#!/usr/bin/env bash
set -euo pipefail
# For every post without an og_image field: generate one and write it back.
for file in content/posts/*.md; do
grep -q '^og_image:' "$file" && continue
title=$(grep -m1 '^title:' "$file" | sed 's/^title:[[:space:]]*//; s/^"//; s/"$//')
url=$(curl -sS -X POST "https://imageapi.dev/v1/images?wait=true" \
-H "Authorization: Bearer $IMAGE_API_KEY" \
-H "Content-Type: application/json" \
-d "$(jq -n --arg t "$title" '{template:"blog-og",modifications:{title:$t,site:"yourblog.com"}}')" \
| jq -r .url)
# Append to the front matter, right below the first '---' line.
sed -i "0,/^---$/{s|^---$|---\nog_image: \"$url\"|}" "$file"
echo "$file -> $url"
done
Run this before `hugo`, not in CI on every build — otherwise you generate images nobody commits, and pay for them again on the next deploy.
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.