How To Bulk Update WordPress Featured Images With Canva, PowerShell, And WPCode

by Cathy Gray | Oct 1, 2026 | Website

What This Does

This is the process I used to replace 40 featured images on my site in one sitting. You design one template, let Canva build every version, rename the files with a script, and let WordPress match each image to its post. No clicking into 40 posts. If you want the backstory first, here’s how this project started.

It’s worth it once you have 15 or more images to update, or you know you’ll do it again.

What you need:

  • A WordPress site and admin access
  • Canva Pro (Bulk Create is a Pro feature)
  • A Windows computer for the PowerShell step
  • The free WPCode plugin
  • A recent backup of your site. The last step changes posts, so don’t skip this one.

Grab a coffee. The first run takes some setup, but you only build it once.

Note: I’m not a coder by any stretch. I knew what I needed to get done and used Claude AI to help with the scripts and step-by-step instructions. You could do the design side in Photoshop, but I went with Canva for the Bulk Create feature. The same idea works on Shopify too: the design and rename steps stay the same, but matching images to posts is set up differently.

Step 1: Pull Your Post Ritles From WordPress

WordPress has a built-in REST API that lists your posts as plain data. No plugin needed. You just open a link in your browser.

Find your category IDs. Swap in your own domain:

yoursite.com/wp-json/wp/v2/categories?per_page=100&_fields=id,name,count

This shows every category with its ID number and post count. Note the IDs you want.

Pull the titles for those categories. Replace the numbers with your IDs, separated by commas:

yoursite.com/wp-json/wp/v2/posts?categories=21,36,60&per_page=100&_fields=title,categories

If it shows up as one long block of text, check the Pretty print box at the top in Chrome. Copy the titles into a plain list, one per line.

Two things to clean up before moving on:

  • Fix any title formatting you want on the image. I swapped a stray dash for a colon and made “eCommerce” consistent before pasting mine in.
  • If you have over 100 posts, add &page=2 to the end of the link to get the next batch.

Step 2: Build Every Image With Canva Bulk Create

Bulk Create takes one design and a list of text, then makes a copy of the design for each line.

  1. Finish your template first. Set it to 1200 x 628 px, the standard WordPress featured image size. Lock in fonts, colors, and spacing now. Changing the template later means redoing the whole batch.
  2. Add your list to Bulk Create. Paste your titles in with Title as the first line so Canva has a column name.
  3. Connect the text. Link the title text box on your template to the Title column.
  4. Generate. Canva builds one page per title.
  5. Check the long ones. Scroll through and look at your longest titles. Bulk Create drops them in whether they fit or not.
  6. Download all pages as JPG. A quality setting around 80 keeps the files small without blurring the text, which helps page speed.

Using more than one background? I used three colors: slate, caramel, and sage. Don’t shuffle the titles randomly. Assign each title a color based on the order posts show up on your blog feed and each category page, so posts sitting next to each other get different backgrounds. Then do one Bulk Create run per color, and save each run’s list. You’ll need them in the next step.

This only holds while your posts stay in the same order. For new posts, pick a different color from the post published right before it.

Step 3: Rename The Files With PowerShell

Canva names the downloads with numbers, in the same order as your list. Page 1 is title 1, page 2 is title 2. That tells WordPress nothing, so this script renames each file to match its post.

Set up the folder. Unzip each Canva download into its own folder. In each folder, add two files:

  • names.txt: that run’s list, in the same order you pasted into Canva, without the Title line. I added ” – Cathy Gray” to the end of every title so my name is in each image title.
  • rename.ps1: the script below. It’s the same file in every folder.
$names = Get-Content .\names.txt | Where-Object { $_.Trim() -ne "" }
$files = Get-ChildItem -Path .\* -File -Include *.png,*.jpg,*.jpeg |
  Sort-Object { [int][regex]::Match($_.BaseName, '(\d+)(?!.*\d)').Value }

if ($names.Count -ne $files.Count) {
  Write-Host "Stopped: $($files.Count) images but $($names.Count) names. Nothing renamed."
  return
}

for ($i = 0; $i -lt $files.Count; $i++) {
  $clean = ($names[$i] -replace '[\\/:*?"<>|]', '').Trim()
  Rename-Item $files[$i].FullName ($clean + $files[$i].Extension)
  Write-Host "$($files[$i].Name)  ->  $clean$($files[$i].Extension)"
}

Save it the right way. In Notepad, go to File > Save As and set Save as type to All Files. Otherwise Windows saves it as rename.ps1.txt and it won’t run. Turning on file extensions in File Explorer (View > Show > File name extensions) lets you double-check.

Run it. Right-click an empty spot in the folder and choose Open in Terminal. On older Windows, hold Shift, right-click, and choose Open PowerShell window here. Then paste:

powershell -ExecutionPolicy Bypass -File .\rename.ps1

The script prints each old name next to its new name, so check the first couple against the images. It also strips out the characters Windows won’t allow in file names, like ? and :.

The safety check is the important part. If the number of images and names don’t match, it stops without renaming anything. That’s how you avoid 40 images with the wrong names.

Repeat for each folder.

Step 4: Upload To The Media Library

Upload all the renamed files to your media library. WordPress swaps the spaces in file names for hyphens. That’s normal and fine for SEO, and the image title keeps your post title, which is what the next step matches on.

Check what actually uploaded. Nine of mine didn’t upload the first time, and most of them had an apostrophe in the name: What’s, I’d, Doesn’t, It’s. Compare the media library to your lists, then upload the missing ones one at a time. If one still fails, remove the apostrophe from the file name and try again.

While you’re comparing, look for duplicates. I ended up with one image uploaded twice and deleted the extra.

This is also a good time to add alt text, while all the images are in one place.

Step 5: Match Images To Posts With WPCode

This is where the time savings happen. A one-time snippet compares each post title to each image title and sets the match as the featured image.

It only looks at images with “Cathy Gray” in the title, and it ignores capitals, punctuation, spacing, and the ” – Cathy Gray” ending when it compares. So a file that lost its ? or apostrophe still matches its post. If you didn’t add an ending, delete the `if (stripos…` check so it looks at every image. If you used a different ending, change Cathy Gray and cathygray in the code to yours.

Take a backup first. Applying this replaces any existing featured images on those posts.

Add the snippet in WPCode:

  1. Go to Code Snippets > + Add Snippet.
  2. Hover over Add Your Custom Code (New Snippet) and click Use snippet.
  3. Give it a name.
  4. Set Code Type to PHP Snippet.
  5. Paste in the code below. Don’t add your own <?php line. If the box already has one at the top, leave it and paste below it.
  6. Change the category IDs on the category__in line to yours.
  7. Under Insertion, keep Auto Insert and set Location to Admin Only.
  8. Switch the toggle to Active and click Save Snippet.
add_action('admin_init', function () {
  if (!current_user_can('manage_options') || empty($_GET['cwc_thumbs'])) return;
  $apply = $_GET['cwc_thumbs'] === 'apply';

  $norm = function ($s) {
    $s = strtolower(html_entity_decode($s, ENT_QUOTES, 'UTF-8'));
    $s = preg_replace('/[^a-z0-9]/', '', $s);
    return preg_replace('/cathygray$/', '', $s);
  };

  $media = [];
  foreach (get_posts(['post_type' => 'attachment', 'post_mime_type' => 'image',
    'posts_per_page' => -1, 'post_status' => 'inherit']) as $a) {
    if (stripos($a->post_title, 'Cathy Gray') !== false) $media[$norm($a->post_title)] = $a->ID;
  }

  $posts = get_posts(['post_type' => 'post', 'posts_per_page' => -1,
    'category__in' => [21, 36, 60, 61, 82, 83, 84, 95, 97]]);

  $out = '';
  foreach ($posts as $p) {
    $key = $norm($p->post_title);
    if (isset($media[$key])) {
      if ($apply) set_post_thumbnail($p->ID, $media[$key]);
      $out .= "MATCH: {$p->post_title}\n";
    } else {
      $out .= "NO MATCH: {$p->post_title}\n";
    }
  }
  wp_die('<pre>' . esc_html(($apply ? "APPLIED\n\n" : "PREVIEW ONLY: nothing changed\n\n") . $out) . '</pre>');
});

Preview first. While logged in, open:

yoursite.com/wp-admin/?cwc_thumbs=preview

Every post shows as MATCH or NO MATCH, and nothing changes yet. Sort out any NO MATCH before going further.

Apply it. When everything says MATCH, open:

yoursite.com/wp-admin/?cwc_thumbs=apply

The page says APPLIED at the top, and every matched post has its new featured image.

Spot-check. Open two or three posts and make sure the right thumbnail is showing in the right color.

Turn it off. Go back to WPCode and switch the snippet to Inactive. I kept mine saved as “Bulk set featured images by title (run once)” for the next batch.

If Something Doesn’t Match

ProblemFix
Some images didn’t uploadUsually an apostrophe in the file name. Upload them one at a time, and remove the apostrophe if it still fails.
An image shows up twiceCompare the media library to your lists and delete the extra copy.
Script says “Stopped: X images but Y names”There’s a blank or missing line in names.txt, or an extra image in the folder. Fix it and run it again. Nothing was renamed.
The script won’t runMake sure it saved as rename.ps1, not rename.ps1.txt.
Preview shows NO MATCHCompare the post title and image title. A changed word or typo breaks the match. Fix the image title in the media library and preview again.
The REST API link won’t load in a toolSome sites block automated requests. Open the link in your own browser instead.

Work Smarter Next Time

The first run takes some setup. The second run is mostly design, because the script and snippet are already waiting. Update the category IDs, drop in a new names.txt, and you’re done before your coffee gets cold.

Curious why I bothered automating this in the first place? Here’s the story behind it.

About

Cathy Gray is an eCommerce and digital marketing professional based in Atlanta, GA. Off the clock, she's somewhere in the Southeast with a paddle, a trail, or a tent. The Full Story

What I Do

Full Stack Marketing
Portfolio

View Posts By Categories

Contact

Looking for a marketer who drives more business and grow sales?