Programmatic SEO on WordPress: Custom Post Types, ACF and WP All Import, Step by Step

Build programmatic SEO pages on WordPress with a custom post type, ACF fields, WP All Import and Rank Math or Yoast variables. Code, CSV and launch checklist.
OutrankAI SEO platform
Updated
Reading time
9 min read
Programmatic SEO on WordPress: A Practical Guide
In this article
  1. Is WordPress a good choice for programmatic SEO?
  2. Three ways to build programmatic pages in WordPress
  3. The worked example: job description templates
  4. Performance: what changes at 5,000+ pages
  5. Launch checklist for WordPress programmatic pages
  6. Where Outrank fits on a WordPress site
  7. FAQ

Programmatic SEO on WordPress comes down to four parts: a custom post type for your pages, custom fields (ACF or Secure Custom Fields) holding one row of data per page, an importer (WP All Import, or a script using the REST API) that turns a CSV into posts, and one template that renders the fields, with Rank Math or Yoast building each title and meta description from those same fields. WordPress has no cap on the number of posts, so it is the cheapest way to run thousands of programmatic pages, provided your hosting and database are ready for it.

This guide builds a real example end to end: the code to register the post type, the field list, a CSV you can copy, the import settings, the template, the SEO variables, and a launch checklist that keeps Google indexing the pages.

Key takeaways

  • Use a custom post type plus custom fields, not spintax page generators. Real posts with structured data are easy to audit, update and noindex one by one.
  • Set a unique ID column (the slug) in WP All Import so re-imports update existing pages instead of creating duplicates.
  • Rank Math reads custom fields with %customfield(field-name)% and Yoast with %%cf_field-name%%, so every page gets its own title and description from your data.
  • Spintax "uniqueness" is the "synonymizing" Google lists as scaled content abuse. Uniqueness has to come from data.
  • WordPress core sitemaps split at 2,000 URLs per file, and ACF stores two database rows per field per post. Plan caching before you import 10,000 rows.

Is WordPress a good choice for programmatic SEO?

WordPress runs 39.6% of all websites and 58.4% of sites whose CMS is known (W3Techs, October 19, 2026), so the odds are your site already uses it. For programmatic SEO it has three real advantages:

  • No item limit. Webflow's Premium plan stops at 20,000 CMS items; WordPress stops where your server does.
  • Mature import tooling. WP All Import, WP-CLI and the REST API all write posts and custom fields in bulk.
  • SEO plugins that understand custom fields. Titles, descriptions, schema and sitemaps can all be driven by your data.

The trade-offs are performance (thousands of posts with many fields each need caching) and maintenance (plugins, PHP versions, backups). If your team is design-led and the set stays under a few thousand pages, programmatic SEO in Webflow is less work. For the strategy side (finding patterns, sizing demand, what Google penalizes), read the main programmatic SEO guide first.

Three ways to build programmatic pages in WordPress

ApproachHow it worksStrengthsWeaknesses
Custom post type + custom fields + importerEach row becomes a real post in its own post type; a template renders the fieldsEvery page is editable, updatable and indexable on its own; works with any SEO pluginMore setup: post type, fields, template
Page generator plugins (Multiple Pages Generator, Page Generator Pro)A template page with placeholders is multiplied by a spreadsheetFast to test an ideaHarder to audit page by page; spintax features encourage the wrong kind of "uniqueness"
Script via REST API or WP-CLIYour code creates and updates posts and metaFull control, easy to schedule from a data pipelineNeeds a developer

We recommend the first approach for anything you intend to keep. The rest of this guide uses it.

The worked example: job description templates

Our example is an HR software company publishing one page per role: "[Role] job description template". Searchers want a template they can copy, so each page needs a complete, role-specific template (responsibilities, skills, qualifications), plus pay and outlook data from a public source: the U.S. Bureau of Labor Statistics Occupational Outlook Handbook. Every row carries different substance, which is what makes the set worth indexing.

Step 1: Register a custom post type

Add this to a small site plugin (or your child theme's functions.php), or create the same post type in the ACF interface under ACF → Post Types:

php
add_action( 'init', function () {
    register_post_type( 'job_template', array(
        'label'        => 'Job templates',
        'public'       => true,
        'has_archive'  => 'job-description-templates',
        'rewrite'      => array( 'slug' => 'job-description-templates', 'with_front' => false ),
        'show_in_rest' => true,
        'supports'     => array( 'title', 'editor', 'excerpt', 'thumbnail', 'custom-fields' ),
    ) );
} );

Then go to Settings → Permalinks and click Save once to flush the rewrite rules. Your pages will live at /job-description-templates/data-analyst/, and the archive at /job-description-templates/ becomes the hub page. Add a taxonomy (for example "Department") if you want sub-hubs; our guide on using tags and taxonomies in WordPress covers the navigation side.

Step 2: Define the custom fields

Create a field group in ACF (or Secure Custom Fields, the WordPress.org fork of ACF) and assign it to the Job templates post type:

Field nameTypeWhat goes in itUsed in
role_summaryTextareaTwo-sentence overview of the roleIntro, meta description fallback
responsibilitiesTextarea (one per line)8 to 12 role-specific dutiesThe template itself
skillsTextareaHard and soft skillsTemplate and FAQ
qualificationsTextareaEducation, certificationsTemplate
median_payNumberFrom BLS, with the yearPay block
pay_source_yearText"BLS, May 2024 data" or similarCitation under the pay block
outlookTextProjected growth from BLSOutlook block
seo_titleTextUnder 60 charactersRank Math or Yoast title
seo_descriptionText120 to 155 charactersMeta description
related_rolesRelationship3 to 6 sibling rolesInternal links
robotsSelectindex or noindexHolding back thin rows

Keep anything that ends up in metadata as plain text. One practical note on scale: ACF stores each field as two rows in wp_postmeta (the value and a field-key reference), so 2,000 pages × 11 fields is about 44,000 meta rows. That is fine for WordPress, but it is why caching matters later.

Step 3: Prepare the CSV

One row per page, one column per field, plus a stable slug column that acts as the unique ID:

csv
slug,title,role_summary,responsibilities,skills,qualifications,median_pay,pay_source_year,outlook,seo_title,seo_description,department,related_roles,robots
data-analyst,Data Analyst,"Data analysts turn raw data into reports and recommendations...","Build and maintain dashboards|Clean and validate datasets|...","SQL|Excel|Python|Data visualization","Bachelor's degree in a quantitative field","[BLS median pay]","[BLS data year]","[BLS projected growth]","Data Analyst Job Description Template (Free to Copy)","Copy a ready-to-use data analyst job description: duties, skills, qualifications and current pay data. Edit it for your role in minutes.",Analytics,"business-analyst|data-scientist",index

Before importing, run three checks in your spreadsheet: TRIM() every text column, flag seo_title values over 60 characters and descriptions outside 120 to 155, and mark rows with missing core fields as noindex (or leave them out entirely).

Step 4: Import with WP All Import

  1. All Import → New Import, upload the CSV, and choose Job templates as the post type.
  2. Drag title into the title field. Leave the content editor empty or put the summary there; the template will render the fields.
  3. Map each column to its ACF field in the ACF section (or under Custom Fields for plain post meta). Split the |-separated lists in your template or with the importer's functions.
  4. Map department to the taxonomy, and set the post slug from the slug column.
  5. In Record Matching, set the unique identifier to {slug[1]}. This is the setting that lets you re-run the import later to update pages instead of creating duplicates.
  6. Run it on a staging site first with 20 rows, check the pages, then run the full file.

Custom fields, importing from a URL, updating existing records and scheduled imports sit in WP All Import's paid tiers, so check the current plans before you build around them. If you prefer code, the same import works through the REST API (POST /wp-json/wp/v2/job_template with an application password, after registering the meta keys with show_in_rest) or with WP-CLI's wp post create and wp post meta update.

Step 5: Build the template

In a classic theme, create single-job_template.php and render the fields, skipping any section whose field is empty:

php
<?php get_header(); ?>
<main class="job-template">
  <h1><?php the_title(); ?> job description template</h1>
  <p class="summary"><?php echo esc_html( get_field( 'role_summary' ) ); ?></p>

  <?php if ( $items = get_field( 'responsibilities' ) ) : ?>
    <h2>Responsibilities</h2>
    <ul>
      <?php foreach ( explode( '|', $items ) as $item ) : ?>
        <li><?php echo esc_html( trim( $item ) ); ?></li>
      <?php endforeach; ?>
    </ul>
  <?php endif; ?>

  <?php if ( $pay = get_field( 'median_pay' ) ) : ?>
    <h2>How much does a <?php the_title(); ?> earn?</h2>
    <p>Median pay: $<?php echo number_format( (float) $pay ); ?> a year
      (<?php echo esc_html( get_field( 'pay_source_year' ) ); ?>).</p>
  <?php endif; ?>
</main>
<?php get_footer(); ?>

In a block theme, open Appearance → Editor → Templates → Add New Template → Single item: Job template. Since WordPress 6.5, the Block Bindings API can connect Paragraph, Heading, Image and Button blocks to registered post meta, so simple fields need no PHP; for lists and conditional sections, ACF Blocks or a small custom block is cleaner.

Whichever route you take, the template should put the thing the searcher came for (here, the copyable template) above the fold, then supporting data, then an FAQ and links to related roles.

Step 6: Titles, descriptions and schema from your fields

  • Rank Math: go to Rank Math SEO → Titles & Meta → Job templates and set the single title to %customfield(seo_title)% and the description to %customfield(seo_description)%. Make sure the post type is set to index.
  • Yoast SEO: go to Yoast SEO → Settings → Content types → Job templates and use %%cf_seo_title%% and %%cf_seo_description%%.
  • Robots per page: read the robots field in your template's <head> (through the SEO plugin's filters or a small wp_head hook) and output noindex, follow for rows that aren't ready.
  • Schema: both plugins can attach a schema type per post type. Use the type that describes the page; don't mark up content that isn't visible.
  • Hub pages: the post type archive (/job-description-templates/) and taxonomy archives (/department/analytics/) list every page with pagination. Add a short intro to each so they aren't bare lists.
  • Sibling links: render related_roles as "Related job descriptions" on every page. Pages reachable only through the sitemap get crawled slowly. More on this in why internal links matter.
  • Sitemaps: WordPress core's wp-sitemap.xml splits post types into files of up to 2,000 URLs; Yoast and Rank Math replace it with their own sitemap index. Either way, confirm the new post type appears, then submit the index in Search Console. Our sitemap guide shows how.

Performance: what changes at 5,000+ pages

A few thousand posts won't trouble a decent host. Tens of thousands with heavy templates will, unless you plan for it:

  • Page caching (your host's cache, or a plugin like WP Rocket or LiteSpeed Cache) so programmatic pages are served as static HTML.
  • Persistent object caching (Redis or Memcached) so repeated get_field() and meta lookups don't hit the database every time.
  • Cheap related-post queries. Use a taxonomy or a stored relationship field, not a meta_query that scans postmeta on every page view.
  • Bulk operations from the command line. WP-CLI handles updates and deletions of thousands of posts without timeouts in the admin.
  • A staging site for every re-import, with a database backup before you run it on production.

Launch checklist for WordPress programmatic pages

Interactive checklist

WordPress programmatic SEO launch checklist

0/17

Tick these off before and after you import the full dataset.

Before the import

0/6

Template and SEO

0/5

Launch and monitor

0/6

Your progress is saved in this browser, so you can come back to it later.

If the pilot pages land in "Crawled - currently not indexed", don't import the rest yet. Make the template more useful first; our guide to fixing crawled but not indexed pages explains what to change.

Where Outrank fits on a WordPress site

Template pages answer lookup queries. The long-tail questions around them ("how to write a job description for a remote role", "what to include in a data analyst job posting") need articles, and that is the part Outrank automates: it picks keywords your site can rank for, writes one long article a day with images, internal links and an FAQ, and publishes it through the WordPress plugin, the REST API or WordPress.com. It doesn't generate custom post type pages from your CSV.

WordPress is the most common CMS among Outrank customers: 1,195 WordPress sites published Outrank articles in the 90 days to October 2026, 59,031 of those articles through the plugin alone (Outrank platform data). In our study of 2,119 sites, 83% of sites connected through the WordPress REST API and 74% of those on the plugin grew their Google clicks. Connection steps are on the integrations page.

FAQ

Can you do programmatic SEO in WordPress without coding?

Yes. ACF can register the custom post type and fields from its interface, WP All Import maps a CSV to those fields, and Rank Math or Yoast build titles and descriptions from them. A block theme can render simple fields with block bindings. You only need code for complex templates or scripted imports.

What is the best plugin for programmatic SEO in WordPress?

For durable projects, the combination of ACF (or Secure Custom Fields), WP All Import and Rank Math or Yoast is the most flexible, because every page is a real post you can edit and update. Page generator plugins such as Multiple Pages Generator are quicker for testing an idea but harder to audit at scale.

How many programmatic pages can WordPress handle?

WordPress has no built-in limit on posts. Sites with tens of thousands of posts run fine on good hosting with page caching, a persistent object cache and lean templates. The practical limits are database size, import speed and how many of those pages Google decides to index.

Will Google penalize programmatic pages on WordPress?

Not because of WordPress or automation. Google acts on pages that offer little value at scale, such as near-identical city pages or spintax text, under its scaled content and doorway policies. Pages built from genuinely different data, linked into a clear hierarchy, are treated like any other content.

How do I update thousands of programmatic pages later?

Re-run the import with the same unique identifier (for example the slug) and choose which fields to update, so existing posts change instead of duplicating. For frequent updates, schedule the import from a URL or script it with WP-CLI or the REST API.

Share this article

About the author

Outrank

Outrank researches, writes and publishes SEO articles like this one, then builds the backlinks that help them rank. Yes, Outrank writes its own blog posts.

Your blog could look like this

Outrank finds the keywords, writes articles like this one, adds images and internal links, and publishes them to your site. 30 articles a month, on autopilot.