Seven weeks ago I published an audit of our own support FAQ, three of our six FAQ answers disagree with the code, and I closed it with this sentence:
The page is notifio.app/help, and the real behaviour is described per site on pages like notifio.app/alerts/kamernet, which are generated from typed data and therefore cannot say "as many as you like" about a field that has a number in it.
That was wrong, and it was wrong in the same way the thing I was auditing was wrong. Typed data is only typed down to the field. The field is a string, and a string holds any sentence you like.
Here is the FAQ on notifio.app/alerts/huurwoningen, which is generated from typed data:
How many Huurwoningen searches can I monitor?
As many as you like. Each city or price band can be its own monitor with its own name and interval.
Two claims, both false. There is a cap, and there is no per-monitor interval. So rather than fix that one answer, I measured the whole site.
The measurement
Notifio's desktop app is in one workspace and the marketing site is a separate Next.js project in another. The number I care about lives in the app:
// app/src/limits.ts export const MAX_SEARCHES = 15; The claim I care about lives on a website that has never imported that file, and cannot. So the only honest way to check is to go and read the rendered output.
curl -s https://notifio.app/sitemap.xml | grep -o '<loc>[^<]*</loc>' Thirty eight URLs. Fetch all of them, strip <script> and <style> blocks, strip the remaining tags, unescape entities, and grep the visible text for unlimited|no limit|as many|any number of.
Results:
- 13 of 38 pages tell a reader there is no limit on how many searches they can run.
- 0 pages mention the number 15.
- 1 further claim exists on /help but only inside a
<script>tag, which I will come back to.
The thirteen:
| Page | What it says |
|---|---|
| /pricing | "Monitor unlimited rental sites" |
| /compare | "Desktop app, unlimited monitors, nothing renews" |
| /compare/rentbird | "Number of searches. Rentbird: up to five on the standard plan. Notifio: unlimited monitors" |
| /compare/rentslam | "Unlimited monitors, Mac and Windows, updates included" |
| /compare/stekkies | "Number of searches. Stekkies: per their plan. Notifio: unlimited" |
| /compare/visualping | "Unlimited monitors, Mac and Windows, updates included" |
| /compare/rentola | "Unlimited monitors, Mac and Windows, updates included" |
| /alerts | "There is no per-site pricing and no limit on monitors" |
| /alerts/kamernet | "There is no limit on how many you add" |
| /alerts/huurwoningen | "As many as you like" |
| /alerts/openrent | "One monitor per search URL, as many as you want" |
| /alerts/spareroom | "You can run as many as you like at once" |
| /guides/how-to-be-first-to-a-rental-listing | "on as many searches as you want, for one payment" |
Thirteen pages, seven lines
The thirteen pages are not thirteen mistakes. They are seven lines of TypeScript in six files:
server/src/app/pricing/PricingClient.tsx:11 "Monitor unlimited rental sites", server/src/app/compare/page.tsx:108 Desktop app, unlimited monitors, nothing renews server/src/app/compare/page.tsx:234 ...on Mac and Windows, unlimited monitors, updates server/src/app/compare/[target]/page.tsx:253 Unlimited monitors, Mac and Windows, updates included. server/src/lib/compare/targets.ts:46 us: "Unlimited monitors" server/src/lib/compare/targets.ts:208 us: "Unlimited" server/src/app/alerts/page.tsx:155 ...no per-site pricing and no limit on monitors. plus four FAQ answers in the site catalogue files and one sentence in a guide. One of those seven lines is the closing call to action on the /compare/[target] route, which renders once per comparison target, so a single line produced five of the thirteen pages. That is the generated-pages trade in one bullet: the leverage that lets fifteen site pages exist is also the leverage that multiplies one wrong sentence.
Meanwhile the cap is enforced twice, in two processes, from the same constant. In the local API:
if (cfg.sites.length >= MAX_SEARCHES) { return res.status(400).json({ error: `You can monitor up to ${MAX_SEARCHES} searches. Remove one to add another.`, }); } And in the renderer, which prints a counter under the list:
const atLimit = sites.length >= MAX_SEARCHES; // ... {sites.length} of {MAX_SEARCHES} So a buyer reads "Monitor unlimited rental sites" on the pricing page, pays, installs, and the app shows them "15 of 15" with a sentence explaining they have reached the limit. Everything about that sequence is our fault and none of it is a bug in any file.
The two that bother me most
Eleven of the thirteen are overclaims in our own marketing voice, which is bad. Two of them are table rows in a comparison against a named competitor:
{ dimension: "Number of searches", them: "Up to five on the standard plan", us: "Unlimited monitors", advantage: "us" }, Those pages exist because comparison pages that cite their sources are more useful than ones that do not, and the whole point of putting a citation under a rival's number is that somebody can check it. The row is still true in its conclusion, since 15 beats 5, and it is false in the only cell we had complete control over. A page that is careful about somebody else's number and careless about its own is not a careful page.
It is not legacy drift
The comfortable story is that the copy was written when the app was unbounded, and the cap arrived later. The git history says otherwise.
limits.ts landed on 27 July 2026. The commit that added the comparison pages, the per-site alert pages and the guides, "Add international pricing, Managed Payments, and content pages", landed on 14 September 2026. The cap was seven weeks old, already enforced in two places and already visible in the app's own UI as a counter, when I sat down and wrote thirteen pages saying there was no cap.
It was not a stale fact. It was marketing language reaching for the strongest version of a true claim. Notifio is a one-off payment with no per-search pricing, which is the real differentiator, and "unlimited" is the three-syllable way to say it. The sentence was never checked against the product because it did not feel like a factual claim while I was typing it.
The fourteenth claim
The /help FAQ answer, "you can add as many monitors as you like, each with its own URL, name, and check interval", did not show up in my visible-text grep. It is still on the page, in two script tags: once in the FAQPage JSON-LD, and once in the serialised payload React uses to hydrate the accordion. The answers are not rendered into the DOM until somebody clicks, which is the rendering bug from the earlier post, still unfixed.
Which produces a genuinely odd outcome. That claim is currently invisible to a reader, fully visible to a crawler, and formatted as a machine-readable question and answer pair, which is the format designed for being quoted.
What I am actually doing about it
Three things, in order:
- Say the number. "Up to 15 searches at once" everywhere the pages currently say unlimited. It is still a stronger claim than a five-search plan, and the reason for the cap is a feature rather than an apology: every search is checked in sequence inside one cycle, so the count is what decides how stale the slowest search gets. I have written about that constant being a latency budget rather than a pricing tier, and the honest version of the sentence is more interesting than the vague one.
- Delete the invented interval. Two pages mention a per-monitor check interval. There is no such setting. The entire persisted settings surface in the app is one boolean.
- Add the test I already know how to write. There is no import path between the two workspaces, so the site cannot read
MAX_SEARCHESat build time. But a test in the website project can read the app's source file as text, pull the integer out of it, and assert that no rendered page string contains "unlimited" next to the word monitors. The repo already has this pattern: a test that reads our own layout file to count the characters a title template spends. A claim about rendered output can only be checked by going and looking at rendered output, and that is as true of a product promise as it is of a meta title.
The general version, for anyone shipping generated pages from typed content: your types protect the shape, and every factual claim you care about is inside a string. The cheapest guard is not a better type, it is a test that greps the built output for the handful of words you have decided you are not allowed to say.
Go and look
By the time you read this the pages may say 15, which would be the good outcome of writing this down. If you want to check my working, the reproducible version is three lines:
curl -s https://notifio.app/sitemap.xml | grep -o '<loc>[^<]*</loc>' \ | sed 's|</*loc>||g' | while read -r u; do curl -s "$u" | sed 's/<script[^>]*>.*<\/script>//g; s/<[^>]*>/ /g' \ | grep -o -i -E '.{60}(unlimited|no limit|as many).{60}' | sed "s|^|$u |" done Count the hits, then count how many of the pages mention the actual number. The cap itself is visible in the product: install from notifio.app/download, add searches, and the list header counts up to it.
