Programmatic SEO in WordPress means generating hundreds or thousands of targeted pages from a single template plus a structured dataset — all inside the CMS you already run. The working stack is Advanced Custom Fields (ACF) for the data slots, WP All Import or Multi-Page Generator (MPG) to load the rows, and one PHP template that renders them. This guide walks the whole build, from spreadsheet to indexed pages, and shows where these projects usually break.
If you're new to the concept, start with what programmatic SEO is and browse real programmatic SEO examples before you touch WordPress. The strategy decisions matter far more than the plugin choice.
What you need: dataset, template, generation plugin
Every programmatic build has exactly three ingredients. Get these right and the WordPress mechanics are easy.
- A dataset — one row per page, one column per variable. A CSV or Google Sheet with columns like
city,avg_price,provider_count,top_neighbourhood. This is the hard part and the part that decides whether you rank. See building database-driven pages for how to model this properly. - A page template — the shared HTML/PHP layout with placeholders where the row data drops in. One template can power 10,000 pages.
- A generation plugin — the tool that marries rows to the template and publishes them: WP All Import, MPG, or a custom loop.
Here's how the two main plugin paths compare.
| Factor | ACF + WP All Import | Multi-Page Generator (MPG) |
|---|---|---|
| Best for | Messy data, scheduled re-imports | Clean data, fast one-off generation |
| Data source | CSV, XML, Google Sheet, API | CSV, Google Sheet |
| Creates real posts | Yes (custom post type) | Virtual pages by default |
| Per-row meta title/desc | Via ACF + SEO plugin | Built in |
| Spintax / variation | Manual | Built in |
| Re-sync on data change | Excellent | Good |
| Learning curve | Medium | Low |
A common, robust setup is ACF for the fields, a custom post type to hold them, and WP All Import to load the CSV. MPG is the faster route when your data is already clean and you don't need each page to be a full WordPress post.
Setting up custom post types and ACF fields
Don't dump programmatic pages into your normal Posts or Pages. Register a custom post type (CPT) so the generated content stays isolated — separate template, separate sitemap rules, one-click bulk cleanup if a batch flops.
Register a CPT called, say, location:
register_post_type('location', [
'label' => 'Locations',
'public' => true,
'has_archive' => true,
'rewrite' => ['slug' => 'seo/plumbers'],
'supports' => ['title', 'editor', 'custom-fields'],
]);
Use the WordPress Custom Post Types handbook or a plugin like CPT UI if you'd rather not write code.
Next, create an ACF field group and attach it to that post type. Add one field per data point in your spreadsheet: avg_price (number), provider_count (number), top_neighbourhood (text), intro_paragraph (textarea). These field names must match the column mapping you'll set up during import. The ACF documentation covers field types in detail.
A quick rule: if a data point should appear on the page or influence its meta tags, it needs an ACF field. The more real fields you carry, the easier it is to make each page genuinely unique — which is the whole game in avoiding thin content.
Importing data with WP All Import (CSV or Google Sheets)
This is where WP All Import earns its place. Install it, choose your source, and map columns to fields.
Step by step:
- New Import → upload your CSV, or paste a published Google Sheet CSV URL (File → Share → Publish to web → CSV). The Google Sheet route lets non-technical teammates edit data in a spreadsheet and re-sync to the live site.
- Set the import target to New Items → your
locationpost type. - Map the title — drag your
citycolumn into the post title, e.g.Plumbers in {city}. - Map ACF fields — in the Custom Fields / ACF section, drag each column to its matching field.
{avg_price}→avg_price, and so on. WP All Import's drag-and-drop uses{column_name}tokens. - Set a unique identifier — this is critical. Point WP All Import at a stable unique column (an ID, or the slug). On re-import it updates existing posts instead of creating duplicates.
- Run the import. For large sets, WP All Import processes in chunks; a 5,000-row import typically completes in a few minutes on decent hosting.
Schedule re-imports (daily/weekly) so live pages reflect your source data. This turns your spreadsheet into a living CMS. For the wider tooling picture, see our roundup of programmatic SEO tools.
Building the template with dynamic fields
One template renders every imported row. Create single-location.php in your theme (or a child theme) and pull ACF values with get_field().
<h1>Plumbers in <?php the_title(); ?></h1>
<p><?php the_field('intro_paragraph'); ?></p>
<ul>
<li>Average callout: ₹<?php the_field('avg_price'); ?></li>
<li>Verified providers: <?php the_field('provider_count'); ?></li>
<li>Busiest area: <?php the_field('top_neighbourhood'); ?></li>
</ul>
The template is also where you win or lose on quality. A page that only swaps the city name is thin content. Instead, surface 2-3 real, differentiating data points per page — pricing, counts, a location-specific paragraph — plus dynamic internal links to related rows. Our guide to template pages that actually rank goes deeper on this pattern.
Wire up meta tags too: if you use an SEO plugin (Rank Math, Yoast), set its title/description templates to read ACF fields so every page gets a unique, keyword-aligned title. This directly feeds your programmatic SEO keyword targeting.
Generating pages at scale with Multi-Page Generator
If your data is clean and you want speed over full post objects, Multi-Page Generator (MPG) is the lighter path. You attach a dataset (CSV or Google Sheet) to a single WordPress page marked as a template, then use {{column}} shortcodes anywhere in that page — content, meta title, meta description, even the URL slug.
MPG generates URLs like /plumbers/mumbai/, /plumbers/pune/ from one template page. Its built-in spintax lets you vary sentence structure per row ({fast|quick|rapid}) so pages don't read identically — useful, but no substitute for real per-row data.
When to pick MPG over WP All Import:
- You want generation live in minutes, not an import job.
- You don't need each page to be a queryable WordPress post.
- Your dataset is already tidy and stable.
When to stick with ACF + WP All Import:
- You need real posts for archives, related-post loops, or WooCommerce.
- Your data is messy and needs field-level cleanup on import.
- You want scheduled re-syncs from an external source.
For a strategy-level view of pushing volume responsibly, read scaled content creation and AI content for SEO — automation multiplies both quality and mistakes.
Sitemaps, indexing and avoiding thin-content flags
Generating pages is half the job. Getting them crawled, indexed and kept is the other half, and it's where most WordPress programmatic projects quietly fail.
Sitemaps. Your SEO plugin auto-generates an XML sitemap, but a 20,000-URL dump in one file is a red flag. Split into files of 50,000 URLs max (Google's limit), ideally far smaller and grouped by post type. See Google's guidance on splitting large sitemaps. Submit the sitemap index in Search Console.
Drip-publish, don't dump. Publishing 15,000 pages overnight looks unnatural and burns crawl budget. Release in batches of a few hundred to a couple thousand, watch indexing, then continue. WP All Import can set posts to draft, so you stage publishing.
Beat thin-content flags. Google's scaled content abuse policy targets pages made mainly to manipulate rankings with little value. Protect yourself with a per-page checklist:
| Do | Don't |
|---|---|
| Include 2-3 unique data points per page | Swap only the city/product name |
| Write a unique meta title and description | Reuse one meta template verbatim |
| Add dynamic internal links to related rows | Leave pages orphaned |
Set noindex on pages with missing data |
Publish empty-field pages |
| Prune rows that never earn impressions | Keep dead pages forever |
That last point matters. Conditionally noindex any page where key ACF fields are empty — a stub page with no data is a liability. After 60-90 days, prune rows that earn zero impressions in Search Console.
Track from day one. Programmatic sets move as a group, so a template or indexing problem hits thousands of pages at once. Monitor rankings and indexed-page counts continuously — a tool like DeployFlare's rank tracker lets you segment by page group and catch a batch-wide drop before it spreads. Watch the Search Console Pages report for the ratio of submitted-to-indexed URLs; if indexing stalls below ~50%, your content is likely too thin or your internal linking too sparse.
Build it in this order — clean data, isolated post type, honest per-page value, staged publishing, constant monitoring — and programmatic SEO on WordPress behaves like any other content that earns its rankings. For the full strategic picture, circle back to the programmatic SEO pillar guide.