Hugo

OG images in
Hugo

Generate with a script before the build, write into front matter.

Where it plugs in

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
The trap

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.

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