# 64% of SaaS pricing pages give an AI agent a readable price

We requested /pricing at 106 SaaS products with no browser, 1–28 Oct 2026: 68 gave a price an agent can quote. What the rest get wrong and how to fix it.

Source: https://agentdrives.com/guides/saas-pricing-pages-ai-agents-can-read?via=md · Published 2026-10-28

An AI agent without a browser gets a price it can quote from 68 of 106 SaaS pricing pages, with one plain HTTP request for /pricing. At the rest, the page carries no amount in its text, there is no page at that path, the answer is a redirect, or the server turns the request away.

**Key takeaways**

- 62 of the 67 graded pages with prices also name the plan and what each price is per.
- 9 of the 19 pages without a price name a free plan but give no amount.

## What our agent ran into at /pricing

The most common miss was a page with no price in its text: 19 of 106.

**Two in three pricing pages give an agent a price in one request**

|   | Products | Out of | Share |
| --- | --- | --- | --- |
| Price in the page text | 68 | 106 | 64% |
| Page, no price in the text | 19 | 106 | 18% |
| No page at /pricing | 10 | 106 | 9% |
| Redirect or not a page | 5 | 106 | 5% |


## Why 19 pages that load show an agent no price

At the 19 pages that loaded without a price in the text, the amounts came by script, sat in images or were not on the page. A price drawn by script is one that AI fetchers miss.

Vercel and MERJ studied crawler traffic and wrote: "The results consistently show that none of the major AI crawlers currently render JavaScript." ([vercel.com](https://vercel.com/blog/the-rise-of-the-ai-crawler)) Anthropic's web fetch tool for the Claude API documents the same limit: "The web fetch tool currently does not support websites dynamically rendered with JavaScript." ([platform.claude.com](https://platform.claude.com/docs/en/agents-and-tools/tool-use/web-fetch-tool)) Search is different. Google says that once its resources allow, Googlebot renders the page in Chromium and "executes the JavaScript." ([developers.google.com](https://developers.google.com/search/docs/crawling-indexing/javascript/javascript-seo-basics))

## Turned away before the page loads: 401, 403, 429 and 503

Fewer than 5 of 106 requests got a 401, 403, 429 or 503 instead of the pricing page. Each code has a fixed meaning in the HTTP standards:

- **401**: the request lacks valid authentication credentials ([rfc-editor.org](https://www.rfc-editor.org/rfc/rfc9110.html)).
- **403**: "the server understood the request but refuses to fulfill it." ([rfc-editor.org](https://www.rfc-editor.org/rfc/rfc9110.html))
- **429**: "the user has sent too many requests in a given amount of time ("rate limiting")." ([rfc-editor.org](https://www.rfc-editor.org/rfc/rfc6585.html))
- **503**: "the server is currently unable to handle the request" ([rfc-editor.org](https://www.rfc-editor.org/rfc/rfc9110.html)).

Cloudflare's error page reference lists its Managed challenge page, its WAF block page and its IP or country block page with status 403, and its rate limiting block page with 429 ([developers.cloudflare.com](https://developers.cloudflare.com/rules/custom-errors/reference/error-page-types/)). Akamai's standard deny action serves an HTTP 403 Forbidden response unless a custom deny action is set ([techdocs.akamai.com](https://techdocs.akamai.com/application-security/reference/api-concepts)). Cloudflare's Bot Fight Mode issues computationally expensive challenges to clients it detects as bots ([developers.cloudflare.com](https://developers.cloudflare.com/bots/get-started/bot-fight-mode/)).

To tell a challenge from a plain refusal, look at the response headers: Cloudflare adds a header that marks every response that served a Challenge Page instead of the expected content, for all Challenge Page types ([developers.cloudflare.com](https://developers.cloudflare.com/cloudflare-challenges/challenge-types/challenge-pages/detect-response/)).

Some of these rules stay off until someone turns them on. Vercel's Bot Protection Managed Ruleset is off by default; in Challenge mode it serves a JavaScript challenge to traffic unlikely to be a browser ([vercel.com](https://vercel.com/docs/vercel-firewall/vercel-waf/managed-rulesets)). Vercel's AI Bots Managed Ruleset is inactive by default, and set to Deny it blocks all traffic identified as coming from AI bots ([vercel.com](https://vercel.com/docs/vercel-firewall/vercel-waf/managed-rulesets)). Cloudflare says new customers who are not publishers get no blocks or disallows added by default when they onboard a domain ([blog.cloudflare.com](https://blog.cloudflare.com/bot-preference-sync/)). If your /pricing answers a plain request with one of these codes, check which rule in your own settings produced it.

## What agent builders should expect from a direct fetch

Plan a fallback: one direct fetch of /pricing returned a quotable price at 68 of 106 products.

User-initiated agents fetch on a person's request. OpenAI says ChatGPT may visit a page with the ChatGPT-User agent when a user asks a question, and that "robots.txt rules may not apply." ([developers.openai.com](https://developers.openai.com/api/docs/bots)) Anthropic says Claude may access websites using a Claude-User agent when people ask it questions ([support.claude.com](https://support.claude.com/en/articles/8896518-does-anthropic-crawl-data-from-the-web-and-how-can-site-owners-block-the-crawler)). Perplexity says its Perplexity-User fetcher "generally ignores robots.txt rules." ([docs.perplexity.ai](https://docs.perplexity.ai/guides/bots)) A firewall can still answer such a fetch with a 403 or 429 ([developers.cloudflare.com](https://developers.cloudflare.com/rules/custom-errors/reference/error-page-types/)).

What we advise for each outcome:

- **No page at /pricing, or a redirect:** follow the home page's link to pricing.
- **401, 403, 429 or 503:** record the code and stop. A 429 means rate limiting, so don't retry in a loop ([rfc-editor.org](https://www.rfc-editor.org/rfc/rfc6585.html)). A 403 means the server refused the request, not that the product has no prices ([rfc-editor.org](https://www.rfc-editor.org/rfc/rfc9110.html)).
- **A page with no price in the text:** hand off to a browser tool, as Anthropic suggests for pages rendered with JavaScript, or report "no price on the page" ([platform.claude.com](https://platform.claude.com/docs/en/agents-and-tools/tool-use/web-fetch-tool)). Never guess a price.
- **Identify your agent** with a published user-agent string. Perplexity publishes its full one: "Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; Perplexity-User/1.0; +https://perplexity.ai/perplexity-user)" ([docs.perplexity.ai](https://docs.perplexity.ai/guides/bots)).

## A readable price names the plan and what it is per

62 of the 67 graded pages with readable prices named the plan and the unit each price is per.

An agent comparing tools needs the amount, the currency, the plan name and the unit side by side. Without the unit, it can compare a monthly price with a yearly one. Buffer's pricing.md writes all of it in one line: "Essentials — from $6/month per channel" ([buffer.com](https://buffer.com/pricing.md)).

If you add structured data, schema.org has a field for each part. On an Offer, priceSpecification holds detailed price specifications, including the unit price ([schema.org](https://schema.org/Offer)). billingDuration states how long a price is billed for ([schema.org](https://schema.org/UnitPriceSpecification)). referenceQuantity gives the quantity a price applies to, such as one seat ([schema.org](https://schema.org/UnitPriceSpecification)). unitCode takes a UN/CEFACT 3-character code or a URL ([schema.org](https://schema.org/UnitPriceSpecification)). The currency goes in priceCurrency, "instead of including ambiguous symbols such as '$' in the value." ([schema.org](https://schema.org/price)) Google uses offers.price and ignores offers.priceSpecification when both are given, so put the plain price on the Offer as well ([developers.google.com](https://developers.google.com/search/docs/appearance/structured-data/product-snippet)).

## A free plan needs a number too

9 of the 19 pages without a readable price named a free plan but gave no amount. Write $0 with its period, so the free plan becomes a price an agent can quote. Resend writes its free plan as "$0/mo" ([resend.com](https://resend.com/pricing.md)). In structured data, Google's product snippet documentation shows a price of 0 as the way to mark a product available without payment ([developers.google.com](https://developers.google.com/search/docs/appearance/structured-data/product-snippet)).

## What to change on your pricing page

Serve pricing at /pricing, return it to a plain request without a 401, 403, 429 or 503, and write every plan's name, amount, currency and unit in the HTML text.

1. **The path exists.** 10 of 106 products had no page at /pricing.
2. **A plain request gets a page.** Request /pricing without a browser. A 401, 403, 429 or 503 turns agents away. On Cloudflare, check the response headers for its challenge marker ([developers.cloudflare.com](https://developers.cloudflare.com/cloudflare-challenges/challenge-types/challenge-pages/detect-response/)).
3. **The price is in the text.** Not in a script, not in an image. 19 of 106 pages had no price in the text.
4. **Plan name, unit and currency beside each amount**, the way Buffer writes "from $6/month per channel" ([buffer.com](https://buffer.com/pricing.md)).
5. **$0 for a free plan**, with its period, as Resend writes "$0/mo" ([resend.com](https://resend.com/pricing.md)).
6. **Optional: Offer markup** that matches the visible prices, with the currency in priceCurrency, and billingDuration and referenceQuantity for the unit ([schema.org](https://schema.org/price), [schema.org](https://schema.org/UnitPriceSpecification)).

For every step an agent takes, read [Make your SaaS agent-ready](/guides/make-your-saas-agent-ready). Each pricing answer, product by product: [Can an AI agent read your prices?](/findings/readable-prices)

## How we measured

Between 1 and 28 Oct 2026 we sent one plain HTTP request for /pricing at 106 of 181 products we drove, with no browser and no script run, and graded the returned text for prices, plan names and units. Every count on this page is of those 106 pricing pages. We counted a request as turned away when it got a 401, 403, 429 or 503. We graded plan name and unit at 67 of 68 pages with prices. One request shows what an agent gets on its first try at the usual address, not what a browser or a later retry would get. Where a count is between 1 and 4, we show it as fewer than 5. Full method at [/method](/method); more studies in [Research](/research).

## Questions owners and agent builders ask

**Can an AI agent without a browser read the prices on my /pricing page?**
It could at 68 of 106 products whose /pricing we requested: the prices were in the text of one plain request. Another 19 loaded without a price an agent could read. To check yours, request /pricing without a browser and search the returned HTML for the amount.

**Why does my pricing page look fine in a browser but show an agent no price?**
Your browser runs the page's scripts, and an agent reading raw HTML does not. At 19 of 106 products, the amounts came by script, sat in images or were not on the page. Googlebot renders JavaScript in Chromium ([developers.google.com](https://developers.google.com/search/docs/crawling-indexing/javascript/javascript-seo-basics)). Vercel and MERJ found that none of the major AI crawlers render JavaScript ([vercel.com](https://vercel.com/blog/the-rise-of-the-ai-crawler)), so a page can show its prices to Google and none to an AI crawler.

**Does bot protection or rate limiting turn agents away, and with which status codes?**
Fewer than 5 of 106 requests got a 401, 403, 429 or 503 before the page loaded. Cloudflare's challenge, WAF and country block pages return 403, and its rate limiting block returns 429 ([developers.cloudflare.com](https://developers.cloudflare.com/rules/custom-errors/reference/error-page-types/)). Akamai's standard deny action serves 403 ([techdocs.akamai.com](https://techdocs.akamai.com/application-security/reference/api-concepts)).

**Does it matter if my pricing isn't at /pricing?**
For an agent that tries the usual address first, yes. 10 of 106 products had no page there, and at 5 more the request ended in a redirect or an answer that isn't a page.

**Do I need a pricing.md or schema markup, or is plain HTML enough?**
Plain HTML is enough for one plain request: at the 68 products where we got a price, it was in the HTML text. A pricing.md file, as Buffer and Resend publish, and Offer markup are additions, not replacements. If you add markup, Google reads offers.price and ignores priceSpecification when both are present ([developers.google.com](https://developers.google.com/search/docs/appearance/structured-data/product-snippet)).

**As an agent developer, how often will a direct fetch of /pricing fail?**
In our test it returned a quotable price at 68 of 106 products. The most common miss was a page with no price in the text, at 19. For those, plan a browser fallback.