aidb.digital · engineering overview
AIDb

The IMDb of AI

A cited, continuously re-researched index of the AI industry — every company, model, product, investor and the people who move between them. Built as a read-mostly static site over Postgres, written entirely by Claude research agents.

960
companies
501
people
6,686
prerendered pages
0
database reads on a cache hit
01 · What’s in the index

The index, by entity

Every figure below is read live from Postgres at render time — the site’s own copy is generated from these counts rather than typed in, so it can’t drift from the database.

Row counts, current as of this build
EntityRecordsNotes
Companies96028 deep profiles, 932 master-list entries
Open roles4,761Synced daily from company boards
People501Cross-linked filmographies
Positions620The person ↔ company edge; powers talent flow
Briefs303Daily newsroom, five per run
Tools100Re-rated weekly
Models86Linked to their originating lab
Products41Shipping surfaces per company
Citations338Source-of-record for factual claims
Funding rounds27Valuation and investor edges
Categories21Sortable leaderboards
Investors19Portfolio views

The interesting table isn’t companies — it’s roles. Because a position carries a person, a company and a time range, the same 620 rows that render a filmography also render the talent-flow graph: who left which lab for which, and when.

02 · How records are written

Nobody types the data in

Every profile is produced by a research agent and re-produced on a schedule. The pipeline is the same whether a cron triggers it or an admin presses Refresh on a profile.

  1. Research

    claude-sonnet-4-6 with live web search re-researches the entity against primary and major secondary sources.

  2. Parse strictly

    The run must return JSON matching the entity schema. A malformed response fails the run rather than writing a partial record.

  3. Label confidence

    Every number lands as reported or estimated. The distinction is stored, not editorial — it renders on the profile.

  4. Store citations

    Claims are written to citations with domain and date, so a reader can check any figure against its source.

  5. Publish and invalidate

    The run is recorded in an admin-only changelog, then revalidateTag() drops the cached data and rebuilds only the pages that showed it.

Freshness is visible

Each profile carries a badge: green under 30 days, amber under 90, red beyond. Staleness is shown rather than hidden, so the index doesn’t quietly rot.

Two models, by job

claude-sonnet-4-6 does deep research and debate. claude-haiku-4-5 handles the high-volume work — the newsroom, tool ratings, job classification and the reader-facing chat.

Scheduled agent runs
JobCadenceWrites
Newsroomdaily · 13:00 UTCFive briefs, grounded in a stored source set
Job syncdaily · 06:30 UTCRefreshes open roles from company boards
Job agentweekly · Wed 07:30Classifies and enriches new postings
Tool ratingsweekly · Mon 14:00Re-rates the 100-tool catalog
03 · Serving it

A database-backed site that doesn’t read the database

AIDb is read-mostly: roughly 1,900 crawlable entity pages against a handful of writes a day. The rendering strategy follows that shape rather than fighting it.

Prerendered, not revalidated on a clock

All 6,686 pages are built at deploy with revalidate = false. Nothing expires on a timer — pages rebuild only when a write says they should.

Invalidated on write

The agent and the crons call revalidatePath() and revalidateTag() together, dropping the rendered page and the cached query behind it in one step.

Cached at the query layer too

Reads are wrapped in unstable_cache with entity tags, so even a page-cache miss re-renders from cached rows instead of hitting Postgres.

Personalisation stays client-side

The admin check runs in the browser against a cookie-only endpoint. Reading it on the server would make every route dynamic — a single cookies() call in the root layout is enough to do that.

04 · The bill that made this necessary

Why the architecture looks like this

Neon bills compute by wake-time, not by query count. The database was awake 620 of the 621 hours in a billing period — it never once reached its five-minute idle timer, and logged no suspend events at all across five straight days.

Before
620 h awake — 99.8% of the period
Cause
~1,900 crawlable pages × render-per-request

The root cause wasn’t traffic volume. A cookies() read in the root layout forced every route to render dynamically, which quietly turned every per-page caching declaration in the codebase into a placebo. Ordinary crawler traffic across ~1,900 sitemap URLs then meant fresh SQL every few seconds, around the clock.

The counter-intuitive part: lengthening the cache window doesn’t fix this. At 1,900 pages, even a 24-hour window still means a regeneration roughly every 45 seconds once a crawler is sweeping — comfortably enough to hold the compute open forever. The fix has to remove the timer, not extend it.

The content surface now serves entirely from cache: every entity page returns a cache hit with no function invocation and no database read behind it.

05 · Stack

What it’s built on

Layer by layer, and the reason for each
LayerChoiceWhy
FrameworkNext.js 16.2 · React 19App Router; static export is the point
DatabaseNeon PostgresScale-to-zero suits a read-mostly index
AccessDrizzle · neon-httpStateless one-shot reads, no idle pool
AgentsAnthropic SDKSonnet for research, Haiku for volume
HostingVercelEdge cache plus scheduled agent runs