TL;DR
- AI assistants get "how much does X cost" questions constantly, and when your pricing page is JS-rendered, vague, or missing numbers, they guess — often wrong.
- Render tier names, prices, currency, and billing cadence as plain HTML text. Never lock pricing behind a toggle, modal, or client-side calculator with no fallback markup.
- Add a visible "Last updated" date near your pricing table. Assistants weight recency when deciding whether a number is still trustworthy.
- Mark up each plan with
Offerschema (price, priceCurrency, availability, URL) so crawlers get a structured confirmation of what the page text already says. - For custom/enterprise tiers, state what "custom" means in words — what drives the price up or down — instead of leaving a blank the model has to fill in itself.
A pricing page that's readable to AI assistants is one where every price, currency, and condition exists as parsable text in the initial HTML response, not just inside JavaScript-rendered widgets. The goal is simple: give ChatGPT, Claude, Perplexity, and Gemini nothing to infer. If they have to guess, they'll either hallucinate a number from a stale training snapshot or cite a review site that scraped you wrong two years ago.
Why pricing pages are a high-risk hallucination surface
Price questions are some of the most common prompts in commercial categories — "what does Notion cost," "is Figma free for students," "how much is a CiteFlow plan." These are also the queries where a wrong answer has immediate consequences: a prospect who hears the wrong number either walks away or shows up expecting a deal you don't offer.
Most pricing pages are built for humans clicking toggles, not for a crawler reading raw HTML. If your monthly/annual switch is a React component that only renders numbers after a click event, and the crawler doesn't execute that JavaScript, it sees an empty shell or a single default number with no context. The assistant then either pulls a number from an old cached crawl, a competitor comparison post, or simply declines and gives a range it guessed at. None of those outcomes help you.
Render every tier as plain text, not just a toggle
The single highest-leverage fix is making sure the full pricing matrix exists in static markup, even if you keep the interactive toggle for human users. Concretely:
- List every tier name, monthly price, annual price, and currency symbol in plain HTML — not inside a
<canvas>, not generated purely by a client-side state change with no server-rendered default. - State the currency explicitly ("$49 USD/month") rather than relying on a symbol alone. A bare "$" is ambiguous across US, Canadian, Australian, and Singapore dollars, and assistants serving international users will sometimes mis-map it.
- Avoid "starting at" language without a concrete number attached to a named tier. "Starting at $X" with no tier label gives the model nothing to anchor to.
- If you use a slider or calculator for usage-based pricing, add a static table of 3-4 example price points below it ("10K requests/mo = $X, 100K = $Y") so there's text to extract even if the slider itself isn't crawlable.
This overlaps with the broader JS-rendering problem covered in SSR vs CSR for AI crawlers territory — pricing pages are simply the highest-stakes instance of that problem, because the cost of a wrong extraction is a lost deal, not just a missed citation.
State a last-updated date near the table
Pricing changes. Assistants know this, and several AI search systems have shown a preference for content that signals recency — a pattern that shows up repeatedly in discussions of content freshness and citation behavior. Put a plain-text "Last updated: [date]" line directly above or below the pricing table, not buried in a footer or changelog. When an assistant is deciding between citing your page or a three-year-old G2 comparison, a visible date increases the odds it trusts yours.
Update that date whenever you touch a number — even a minor one. A stale date next to current prices is worse than no date at all, because it signals the page isn't maintained.
Add Offer schema as confirmation, not replacement
Offer schema (nested inside Product or Service) gives crawlers a structured, unambiguous version of the same data that's already in your visible text. Include price, priceCurrency, url, and availability for each tier as a separate Offer node. If you have tiers, use AggregateOffer with lowPrice and highPrice only if a single number can't represent the page, and still keep each tier's number stated separately in the surrounding text.
Schema should never be the only place a price exists. Crawlers and LLMs that don't fully parse JSON-LD — or that distrust schema not matched by visible text — fall back to the HTML content. Schema is a confirmation layer, not a substitute for plain-text tiers.
Handle custom/enterprise pricing without leaving a blank
"Contact us for pricing" is the single biggest source of AI hallucination on B2B pricing pages, because it leaves a gap the model fills with a guess, a competitor's number, or a flat "pricing not publicly available" answer that undersells your product. Instead:
- State the actual range if you have any flexibility — "Enterprise plans typically range from $X to $Y per month depending on seat count and usage volume."
- If you genuinely can't publish a range, explain the variables driving the price: seats, API volume, SSO/SLA requirements, data residency. This gives the assistant something concrete to paraphrase instead of a dead end.
- Add a line clarifying what's included at the custom tier that isn't in the next tier down, so the assistant can at least describe the differentiation even without a number.
FAQ
Does Offer schema alone fix AI pricing hallucinations?
No. Schema is a structured confirmation of data that must also exist as visible, crawlable HTML text — assistants frequently rely on the rendered page content, and schema that contradicts or exists without matching text is often ignored or distrusted.
What currency format should I use if I sell internationally?
State the currency code or name explicitly alongside the symbol (e.g., "$49 USD/month") rather than relying on a bare symbol, since symbols like "$" are ambiguous across multiple countries' currencies.
Should I ever hide pricing entirely behind "contact sales"?
Avoid it wherever possible; a published range or the specific variables that determine custom pricing give AI assistants something accurate to cite, whereas a blank "contact us" invites a guessed or competitor-sourced answer.
Sources
- General knowledge of JSON-LD
Offer/AggregateOfferschema conventions per Schema.org


