Pick an AI app builder off the price on its homepage and you can be a long way into a project before you learn that iOS is on a different plan. Rork leads with $0 and $20 a month. Neither of those tiers produces an iOS app: "iOS apps with SwiftUI", along with iPad, Apple Watch and Vision Pro, first appears on Rork Max at $200 a month. That was read off rork.com/pricing on 2026-08-17.
That gap is the whole reason a comparison page is worth publishing. It is also somebody else's number, and it moves when they feel like moving it.
Every cell carries the string that proves it
A cell in our comparison file is four things: the value we print, the URL it was read off, the date it was read, and a probe, a literal string that has to still be on that page. A nightly job re-fetches all seven source pages and checks all 28 strings.
The first sweep reported 20 of the 38 claims in the sibling claim file as stale, within hours of their being written. Every one of those figures was correct. They looked like this:
Free $0 … 300K tokens daily limit … Pro $25 per month billed monthly … Teams $30 per month
That is a plan grid, quoted as the separated strings that carry it, because no single run of text on the page says it. A substring test can never match that, so those claims were not being checked. They were failing permanently, and a check that is permanently red is one that everybody learns to skip.
So the ellipsis now means what the person typing it meant by it. Every fragment has to still be there, each one literally:
export function probeHolds(text, probe) {
const fragments = String(probe)
.split(/\s*(?:…|\.\.\.)\s*/)
.map((f) => normalise(f))
.filter(Boolean);
if (!fragments.length) return false;
return fragments.every((f) => text.includes(f));
}Four figures instead of one, so it asserts strictly more than the substring it replaced. Order is not enforced, because the order a table sits in the file the server sends is not the order a person reads it. normalise runs over both sides, so a curly apostrophe substituted by somebody's CMS cannot raise an alarm about a price.

Product page for Rork, a chat-to-app builder, with features and evaluation criteria — Feature checklist and verdict criteria for evaluating an app builder tool.
Six of the seven pricing pages do not exist until a browser runs them
A plain fetch of these plan grids comes back with a few hundred characters of shell and no prices in it. Picking an easier source instead would mean the vendor's choice of front end framework decides where we read their prices from, so a cell can declare that it needs a real browser profile.
Timing then became the problem. bolt.new/pricing was read at 7,157 characters in one sweep and three claims came back stale; a read minutes later returned 2,115 characters that plainly contain all three probe strings. Same URL, same profile, same day. The difference was how far a client rendered page had got when the clock ran out.
The job now waits for the network to go quiet rather than for a fixed number of seconds, and a browser read source whose probe missed gets read once more, on its own, before anything is called stale. A plain fetch source gets no retry: a page that arrives whole in one response does not change between two requests a second apart, and a retry there only doubles the load on somebody's server for nothing.
| Builder | Free tier | Cheapest paid plan | Cheapest plan that reaches iOS | Read by |
|---|---|---|---|---|
| Rork | $0, design mode only | $20/mo (Rork Pro) | $200/mo (Rork Max) | browser |
| Vibecode | Not published | $20 /month (Plus) | $20 /month (Plus) | browser |
| FlutterFlow | $0, build and test | $39/month (Basic) | $39/month (Basic) | browser |
| Expo + EAS | $0 /month, 15 Android and 15 iOS builds | $19 /month plus usage (Starter) | $0 /month (Free) | plain fetch |
| Replit Mobile | Free (Starter) | $20 per month (Core) | Not published | browser |
| Bolt.new | $0, 300K tokens daily limit | $25 per month (Pro) | Not published | browser |
| Lovable | $0 / month | $25 / month (Pro) | Not published | browser |
All 28 cells checked 2026-08-17, each against that vendor's own pricing page.
The date only moves if all 28 confirm
Freshness is the strongest citation signal a page like this controls, which makes a date that moved while a figure underneath it went wrong the most expensive thing it can print. So the stamp is all or nothing: any stale claim, or any page that returned under 400 characters and therefore told us nothing, and the run exits non-zero and writes no dates.
"Not published" is a value in that file rather than a blank. The structural check rejects a cell that says Not published and still carries a probe, and rejects a cell that prints a figure and has no probe for it. Four of the 28 currently say it, three of those in the iOS column.
This is how we built StoreReady: https://storeready.kynth.studio/?utm_source=kynth-devto&utm_medium=social&utm_campaign=kynth
One shipped product, taken apart, once a month. What it does, what it cost to build, what the pipeline behind it looks like, and what the numbers did — read off the repository and the live site, not written from memory. Join the list.
← All writing