How to Choose Template Variables for Programmatic SEO
Choose template variables, required source fields, and page-specific evidence that make programmatic SEO pages meaningfully different.
A template can contain twenty variables and still produce twenty versions of the same page.
That is the trap in programmatic SEO data modeling. Once you learn that {{city}}, {{service}}, {{product}}, or {{integration}} can be inserted into a URL, title, H1, and body, it is tempting to treat every variable as proof that the page is different.
It is not.
Some variables identify the page. Some control whether the page should exist. Some change the answer a user receives. Some are merely presentation. If you do not distinguish those roles before generation, the spreadsheet looks richly structured while the rendered pages remain interchangeable.
The goal is not to maximize the number of columns. It is to define a source contract that lets every planned page answer three questions:
- What is this page about?
- What facts make its answer specific?
- What should happen if one of those facts is missing?
Template variable, source field, and page-specific evidence are different things
These terms are easy to collapse because they often share the same column name.
A template variable is a placeholder in a rendering pattern. In pSEO Guard’s Page Matrix tool, for example, {{city}} inserts the city value and {{city|slug}} formats it for a URL.
A source field is a named value in your underlying data model. It may be raw data, a normalized value, a relationship key, or a derived fact. A source field does not have to appear in the final copy.
Page-specific evidence is the source data that materially changes what the page can truthfully tell the user.
Consider a hypothetical local-service page:
| field | role | should it render? | does it prove independent value? |
|---|---|---|---|
city |
page identifier | Usually | Not by itself |
service |
intent / page identifier | Usually | Not by itself |
service_available |
eligibility gate | Usually not | Yes, because it decides whether the page should exist |
response_window |
answer-changing fact | Often | Potentially |
local_constraint |
answer-changing evidence | Often | Yes when it changes the service decision |
parent_url |
relationship | Usually not in prose | No, but it makes the architecture explicit |
hero_adjective |
presentation | Maybe | No |
This distinction matters because a variable can be useful without being evidence. city is essential to a city page. It determines identity, search framing, and often the URL. But if the only change across pages is Austin → Dallas, the page family has not demonstrated that the answer itself changes.
Changing the name is not the same as changing the answer
Imagine this template:
Need payroll software in {{industry}}?
Our platform helps {{industry}} teams automate payroll, reporting, and compliance.
Book a demo for {{industry}} today.
Now feed it 100 industries.
The output contains 100 industry names, 100 titles, and perhaps 100 unique URLs. The underlying answer is still the same. If you could replace every industry with [INDUSTRY] and the pages became indistinguishable, the variables are doing identification work, not evidence work.
This is why word count does not rescue a weak field model. A long template with the entity name repeated 30 times is still one answer copied across 100 rows.
Google does not publish a safe percentage of “unique content.” Its canonicalization guidance explains that pages with the same or very similar primary content can be grouped, and its spam policies focus on scaled, unoriginal content that provides little or no value rather than a numeric uniqueness threshold.
The practical test is simpler: what would a user learn, decide, compare, or do differently because this field changed?
Give every field a role before you give it a placeholder
Before building the template, classify each source field.
A useful field contract includes:
| property | question |
|---|---|
name |
What stable name will templates and audits use? |
role |
Identifier, eligibility, evidence, relationship, metadata, or presentation? |
required |
Can this page truthfully exist if the value is missing? |
type |
Text, number, boolean, date, URL, enum, relationship key? |
source |
Where does the value come from? |
freshness |
How old can it be before the claim becomes unreliable? |
missing_value_action |
Block generation, Review, omit a section, or use a verified fallback? |
used_by |
URL, title, H1, body, structured data, internal logic, or QA only? |
This turns a spreadsheet column into an operational decision.
For example, service_available might be a required boolean used only by generation logic. If it is false, there should be no page.
testimonial might be optional: when it is empty, the testimonial block disappears and the rest of the page remains valid. local_constraint might be required for a page family whose entire promise is to explain what changes locally.
Required and optional are therefore not universal properties of a field. They are properties of the page model.
Make a field required when the page cannot be valid without it
A required field should have consequences. If it is blank, generation should stop or the row should become explicitly reviewable.
Fields are usually required when they determine one of these things:
Page identity. A city landing page cannot be a city landing page if city is missing.
Stable URL construction. If service_slug is part of /services/{{service_slug}}/{{city_slug}}/, a blank value can create a malformed or colliding path.
Eligibility. If a product is not available for an integration, region, plan, or customer type, the availability field must decide whether the combination can generate.
The core answer. If a comparison page promises pricing differences but price is missing, publishing a generic comparison is not a graceful fallback. It breaks the page promise.
Critical relationships. If your model requires every child page to belong to a hub, the relationship key needs an explicit policy when absent.
The last case may be a Review rather than an automatic block, depending on the architecture. The important thing is that missing data changes the row’s state. It does not silently become an empty string.
Keep optional fields genuinely optional
Optional means the page remains truthful and useful when the field is absent.
Good optional fields often enrich a page without carrying its central claim:
- a secondary image
- an extra example
- a testimonial
- an optional FAQ
- a supporting statistic that is not needed for the core answer
Design the template so the entire dependent section disappears cleanly when the field is empty.
Do not “solve” optionality by filling missing values with generic copy such as:
Information for {{city}} is coming soon.
or:
Contact us for details.
repeated across hundreds of pages.
That preserves the HTML shape, which is a strangely popular thing for spreadsheets to care about, but it does not preserve the page’s value.
Decide missing-value behavior before generation
For every field used by the template, define one of four actions.
1. Block the row
Use this when the value is required for page identity, URL construction, eligibility, or the core answer.
Example: service_available is unknown for a page that claims the service is available.
2. Send the row to Review
Use this when the absence may be intentional but needs a human decision.
Example: parent_url is blank for a family that normally has hubs, but some top-level pages may legitimately have no parent.
3. Omit the dependent section
Use this for genuinely optional enhancements.
Example: there is no testimonial for this product. Remove the testimonial block; do not manufacture one and do not leave a heading with an empty card below it.
4. Use a verified fallback
A fallback is safe only when it is semantically true for the row.
For example, a product-level support policy may be inherited by every variant if the source system explicitly says the policy applies to all variants. A generic sentence that merely sounds plausible is not a fallback.
Write this behavior into the data contract. Do not leave it to the template author to improvise after encountering the first blank cell.
Test whether a field actually changes the user’s answer
A useful way to judge a proposed variable is to run five tests.
The removal test
Remove the field from the rendered page. Does the page lose a fact the user needed, or does it merely lose a noun or adjective?
If removing city changes only the heading, it is an identifier. If removing local_constraint removes the reason the service works differently in that city, it is evidence.
The substitution test
Swap the value with another row’s value. Would the resulting claim become false or misleading?
If response_window=2 hours can be swapped with response_window=3 days without anyone noticing, either the field is not actually used or the page promise is too vague.
The decision test
Does the field change what the user should do?
Compatibility, eligibility, price, limits, requirements, availability, and operating constraints often pass this test because they can change a buying or implementation decision.
The provenance test
Can a reviewer tell where the value came from and when it was last checked?
A field that drives a strong claim but has no traceable source is a future QA problem wearing the costume of data.
The sibling test
Compare two rendered pages after masking the identifying nouns. Do the remaining facts and conclusions differ?
If not, the source model is probably producing a set of renamed pages rather than a set of different answers.
Keep variables out of fields they should not control
A source field can be useful in the body without belonging everywhere.
Do not automatically place every identifier in:
- URL
- title
- H1
- meta description
- body
- anchor text
- structured data
Use a field only where it changes that element’s meaning.
This is especially important for URLs. Stable identifiers such as product, service, integration, or city slugs often belong in the path. Volatile evidence such as price, stock, response time, rating, or an “updated” year usually does not. Those values can change while the identity of the page stays the same.
Similarly, parent_url is a relationship, not decorative copy. source_record_id may never render at all but can be invaluable when someone asks why a claim appeared on 300 pages.
Prevent placeholder leakage by failing visibly
A placeholder that reaches a public page is rarely a writing problem. It is a data-contract failure.
Typical leakage looks like:
/services/{{service}}/austin/
or:
Available in with a {{response_window}} response time.
The dangerous behavior is to remove the unknown value silently. That can turn a broken template into output that looks superficially finished.
pSEO Guard’s Page Matrix Generator deliberately keeps an unknown {{column}} placeholder in the rendered page so the audit can report it. When a referenced source column exists but the row value is blank, the matrix records that missing value instead of pretending the substitution succeeded.
That is the right failure mode for any pSEO system: broken evidence should remain visible until someone makes a decision about it.
Preserve source evidence for future QA
A generated page should not be the only remaining record of its source data.
Keep the raw fields beside the rendered output, including useful provenance such as:
- source record ID
- source URL or system
- last updated time
- field owner
- original value
- normalized value
- eligibility state
- evidence fields used by the page
- template or page-family ID
Why keep both raw and rendered values?
Because six months later, “the page says 24-hour support” is not enough. You need to know whether that claim came from a CRM flag, a product database, a manually maintained spreadsheet, or a template default. Without the source, you cannot tell whether the fix belongs in one page or in the system that created all of them.
pSEO Guard’s matrix export preserves original data as source_* columns beside rendered fields and audit decisions. The export schema documentation describes that provenance model.
If you are importing a page plan into the Audit rather than generating it in the matrix tool, use the Field Mapping guide to align page-facing fields such as url, title, h1, target_keyword, parent_url, and content_text with the supported audit schema.
Common aliases are recognized, but an unfamiliar header is not magically inferred.
Build the field model before you build the page family
A practical order of work is:
- Define the page intent and eligibility rule.
- List the source fields needed to answer that intent.
- Mark each field as identifier, gate, evidence, relationship, metadata, or presentation.
- Decide required, optional, Review, and fallback behavior.
- Record provenance for claims that matter.
- Only then create template variables.
- Build a small page matrix and compare sibling pages.
- Run the pSEO Audit with body content when you need body-level checks to be measured.
This order feels slower than opening a template and typing {{city}} everywhere. It is faster than discovering after 500 URLs exist that the only field distinguishing them was the noun in the H1.
A good template variable is not valuable because it makes the page look different. It is valuable because it connects a specific page to data that makes the answer different, traceable, and worth reviewing before you scale it.
Define the data contract before you write the template.
Use the supported pSEO Guard field names and aliases so URLs, intent, hierarchy, and body evidence remain measurable in the audit.