Generate on publish, store the URL in post meta.
In WordPress you do not want to hit the API on every page view. Hook into the post save, generate once, and store the URL in a meta field that your theme or SEO plugin reads later.
functions.php
add_action('save_post_post', function (int $post_id, WP_Post $post): void {
if (wp_is_post_revision($post_id) || $post->post_status !== 'publish') {
return;
}
$response = wp_remote_post('https://imageapi.dev/v1/images?wait=true', [
'timeout' => 15,
'headers' => [
'Authorization' => 'Bearer ' . IMAGE_API_KEY,
'Content-Type' => 'application/json',
],
'body' => wp_json_encode([
'template' => 'blog-og',
'modifications' => [
'title' => get_the_title($post),
'author' => get_the_author_meta('display_name', $post->post_author),
'site' => wp_parse_url(home_url(), PHP_URL_HOST),
],
]),
]);
if (is_wp_error($response)) {
return;
}
$body = json_decode(wp_remote_retrieve_body($response), true);
if (! empty($body['url'])) {
update_post_meta($post_id, '_og_image', esc_url_raw($body['url']));
}
}, 10, 2);
// Print it in <head> if you are not using an SEO plugin.
add_action('wp_head', function (): void {
if (! is_singular('post')) {
return;
}
$image = get_post_meta(get_the_ID(), '_og_image', true);
if ($image) {
printf('<meta property="og:image" content="%s">' . "\n", esc_attr($image));
echo '<meta name="twitter:card" content="summary_large_image">' . "\n";
}
});
The `save_post` hook also fires on autosaves and on every update. The `post_status` and `wp_is_post_revision` guards are what stop you from generating a dozen images while writing a single post.
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.