Why Startups Are 12.8x More Invisible Than Incumbents
By Alex Bouchard · 8 August 2026
No individual Y Combinator company is named in this piece, by policy. Two established public companies are named descriptively, permitted under house style for the public major-sites dataset, and neither is presented as a failure. This is about a population-level pattern, not any one company's choices.
The symptom
A company in Y Combinator's Fall 2025 batch is 12.8 times as likely to be an empty shell to AI crawlers as an established SaaS company is.
Among 145 YC Fall 2025 companies that returned a clean, unblocked page — 37 of them, roughly one in four (25.5%), serve raw HTML with fewer than 150 words of actual text. No JavaScript execution, no product description, no pricing, nothing to cite. Among 450 established SaaS companies checked the same way — spanning public giants down to well-known late-stage private companies — only 9, roughly one in 50 (2.0%), are in the same state.
That gap isn't noise. It shows up at every threshold we checked:
| YC Fall 2025 (n=145) | Established SaaS (n=450) | Gap | |
|---|---|---|---|
| Empty shell (CSR_SHELL, <150 words) | 25.5% | 2.0% | 12.8x |
| Thin (SSR_THIN, 150–399 words) | 9.7% | 2.4% | 4.0x |
| Not fully readable (shell, thin, or bot-richer) | 35.2% | 4.9% | 7.2x |
| Fully readable (SSR_FULL, 400+ words) | 64.8% | 95.1% | — |
| Median visible words, all companies | 630 | 1,241 | incumbents ~2x |
One in three companies in a Fall 2025 YC batch — not the empty-shell cases alone, but everyone below the "fully readable" line — hands a non-rendering AI crawler less raw text than a single paragraph. Among established SaaS companies, that's closer to one in twenty.
The evidence (verify it yourself)
The check behind every number above is the same one behind the hero case file: fetch the page, strip <script> and <style> tags exactly the way a non-rendering crawler's parser would, and count what's left. Run it against your own domain in ten seconds:
# Fetch raw HTML with JavaScript disabled (the condition every non-rendering # AI crawler is permanently in), strip script/style tags, count real words. # (sed can't do this reliably — its . doesn't match newlines, so multi-line # script blocks leak tokens into the count. python3 mirrors the audit's own # parser and handles this correctly.) curl -sL https://yourdomain.com \ | python3 -c "import re,sys; h=sys.stdin.read(); h=re.sub(r'<(script|style)(\s[^>]*)?>.*?</\1>','',h,flags=re.S|re.I); h=re.sub(r'<[^>]+>',' ',h); print(len(h.split()))"
Under 150 and you're a CSR_SHELL by this audit's own threshold — the same bucket 37 of 145 YC companies and 9 of 450 SaaS companies landed in.
The gap isn't confined to unknown startups. Two companies in the established-SaaS dataset — both public, both with mature engineering organizations — land in that same CSR_SHELL bucket:
curl -sL https://www.duolingo.com | ... # → 1 visible word in raw HTML (11.2 KB of markup)
curl -sL https://www.palantir.com | ... # → 4 visible words in raw HTML (464 KB of markup,
# a Next.js hydration shell — has_next_data: true)Both returned a clean 200 on the real homepage — this isn't a bot challenge page, it's what's actually there. This reflects each site's homepage only, as fetched on the measurement date (2026-08-08) — other pages on either site may render differently server-side. The point isn't that Duolingo or Palantir did anything wrong; it's that the mechanism is universal. Company size doesn't immunize you from shipping a client-rendered shell. It just makes it far less likely you will — 2.0% likely instead of 25.5%.
And when an established company does land in CSR_SHELL, the shell is just as empty as a startup's: median 8 visible words among the 9 SaaS companies in that bucket, versus median 9 among the 37 YC companies. Same failure mode, same severity, wildly different frequency.
Why this happens (it's not a maturity gap in disguise)
It's tempting to read "12.8x" as "startups build worse" or "startups don't care." The data doesn't support that story, and the mechanism explains why.
The default changed, and startups adopt current defaults. A company starting today reaches for whatever the ecosystem currently recommends — Vite, Create React App descendants, a plain SPA template — and those defaults render entirely client-side unless you deliberately opt into server rendering. That's not a corner being cut; it's the tool doing exactly what it says on the tin. The gap only becomes a liability for readers that skip JavaScript execution entirely, and until AI crawlers arrived, nothing did — Google has rendered JavaScript for years, so a purely client-rendered SPA ranked fine. The technical debt was invisible and free. It only became visible and costly in the last two years, and a young company hasn't necessarily had a reason to notice yet.
Nobody's dedicated to noticing, this early. At seed stage, engineering hours go to product-market fit — the thing YC batches are explicitly optimized for. Server-side rendering adds real complexity (hydration, data-fetching patterns, deploy pipeline changes) that is a rational thing to defer when you have ten users and need forty. There usually isn't a dedicated SEO or growth engineering function yet to flag it — that role tends to arrive with headcount, later.
Incumbents didn't get here by trying harder at AI-readability — they already built the infrastructure for a different reason. Companies that scaled during the pre-AI-search era needed Google's organic traffic to survive, and Google's ranking algorithm has rewarded fast, crawlable, often server-rendered pages for over a decade. The investment SaaS companies made in technical SEO, Core Web Vitals, and semantic markup — for entirely different, older reasons — happens to double as AI-crawler readability today. It's also why the median established-SaaS page carries almost twice the raw text of the median YC page (1,241 words vs. 630): years of content built to rank, not just to convert, produces more extractable prose as a byproduct.
None of this is a verdict on any individual company's engineering discipline. It's what you'd expect from company age interacting with when the relevant readability requirement appeared. Blame the timeline, not the team.
The fix (same mechanism, shorter version)
The fix doesn't change by company stage: server-render your content-bearing pages so the words exist in the raw HTML response, before any JavaScript runs. Next.js Server Components, Remix, Astro, SvelteKit, or a static-prerender step (react-snap, Prerender.io) in front of an existing SPA all get you there. The test is the one-liner from the evidence section above — the answer to "what does this product do" has to survive a curl, not just a browser tab.
If you're pre-seed or seed and this is the first time server rendering has come up: that's the expected order of operations, not a red flag. It's cheaper to fix now, with one marketing site and no legacy routing, than after ten more landing pages have shipped on the same client-rendered pattern. For the full walkthrough — code, migration options, and what "extractable" structure looks like once the text exists — see the companion case file on this exact fix: Invisible Without JavaScript (/cases/invisible-without-javascript).
What it costs to be the invisible 25%
This matters more for an early-stage company than for an incumbent, because of who's asking. Forrester's most recent buyer survey found that 94% of B2B buyers used AI at some point during their most recent purchase process — up five points year over year — making AI-assisted research close to the default path a prospect now takes before they ever talk to sales. [Forrester, Jan 22, 2026] If that buyer's AI assistant reads your homepage and gets a near-empty shell, you don't rank behind your incumbent competitor in that conversation — you're absent from it, while a company three years older and one server-rendering decision ahead of you is present. For a company that needs every early customer, that's not a ranking problem. It's not being in the room.
How you'll know it's fixed
Run the curl check above against your own domain. If the word count clears 150 — better, clears 400 — you're out of the bucket entirely. Or run the free scan, which applies the same SSR_FULL / SSR_THIN / CSR_SHELL classification used throughout this piece, across 12 crawler user-agents (11 AI-vendor crawlers plus bingbot as a non-AI control), and reports the visible-word count directly.
We'll re-verify you for free. If this prompted a fix, email us and we'll re-run the audit and note the change. Going from CSR_SHELL to SSR_FULL before your next fundraising conversation is a good story, and it costs a day or two of engineering time, not a quarter.
Method: 147 Y Combinator Fall 2025 companies and 514 established SaaS companies (the SaaS-1000 dataset — public giants through well-known late-stage private companies) were each fetched with a baseline browser user-agent — a cold request for timing, then a warm request whose body every classification below is computed from. Of the 514 SaaS domains, 8 were unreachable outright (connection/DNS failures, no baseline response at all) and were dropped first, leaving 506 with a baseline. Domains where that baseline request didn't return a clean 200 (2 of 147 YC; 51 of 506 reachable SaaS domains — mostly WAF/bot-management challenges on the probe's datacenter IP, not evidence about real visitors) were excluded from the readability comparison, leaving 145 YC and 455 SaaS domains. Two further exclusion rules were then applied to both populations. Five SaaS domains — amdocs.com, ooma.com, agilysys.com, radware.com, and paylocity.com — returned a WAF challenge or interstitial rather than the site's own page under the baseline browser user-agent: empty or challenge titles and bodies under 2 KB, with radware.com's title literally "Radware Captcha Page" and paylocity.com returning 683 bytes from Cloudflare. Measuring those pages would have measured the challenge, not the homepage, so they are excluded from both the numerator and the denominator. Two more — wandb.ai and bubble.io — fall under 150 words on the baseline fetch, but their largest AI-crawler response is at least twice the size of that baseline; because crawlers receive more, not less, they are reported separately rather than counted as invisible. Together those rules give 9 of 450 rather than the 16 a baseline-only count would show. Both rules were applied to the YC population as well, where no domain matches either. Raw HTML was stripped of <script> and <style> tags and word-counted; classification is SSR_FULL at 400+ words, SSR_THIN at 150–399, CSR_SHELL under 150 — the same thresholds used in the hero case file. This reading is baseline-derived: it records what the site returned to an ordinary browser request, so it does not depend on our probe successfully impersonating a crawler, and unlike access findings elsewhere in this project it needs no server-log confirmation before publishing. It is not a claim that a site serves the same page to every identity. The bot-richer rule above exists precisely because some do not, and where a crawler demonstrably received more, the row was excluded rather than counted against the company. Full dataset and method: github.com/abouchard11/geo-crawl-audit.