Jekyll

OG images in
Jekyll

A script before the build writes `image:` into front matter.

Where it plugs in

GitHub Pages runs Jekyll in safe mode, so custom plugins that call an API at build time are not an option there. A small script that fills in `image:` once, committed with the post, works on GitHub Pages and anywhere else — and jekyll-seo-tag picks the field up as og:image without any template changes.

_scripts/og_images.rb

#!/usr/bin/env ruby
# Usage: IMAGE_API_KEY=sk_live_... ruby _scripts/og_images.rb
require "json"
require "net/http"
require "yaml"

ENDPOINT = URI("https://imageapi.dev/v1/images?wait=true")

Dir.glob("_posts/*.{md,markdown}").each do |path|
  source = File.read(path)
  _, front, body = source.split(/^---\s*$/, 3)
  meta = YAML.safe_load(front, permitted_classes: [Date, Time]) || {}
  next if meta["image"] # already has one

  request = Net::HTTP::Post.new(ENDPOINT, "Content-Type" => "application/json",
                                          "Authorization" => "Bearer #{ENV.fetch("IMAGE_API_KEY")}")
  request.body = { template: "blog-og", modifications: { title: meta["title"], site: "yourblog.com" } }.to_json
  response = Net::HTTP.start(ENDPOINT.host, ENDPOINT.port, use_ssl: true) { |http| http.request(request) }
  response.value

  url = JSON.parse(response.body).fetch("url")
  # `image` is the key jekyll-seo-tag reads for og:image.
  File.write(path, "---#{front.rstrip}\nimage: \"#{url}\"\n---#{body}")
  puts "#{path} -> #{url}"
end
The trap

Check that the front matter does not already have `image:` before generating — otherwise every run creates a new image for every post. Our cache returns the same URL for the same title, but a changed site name or template would cost quota again.

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