The dataset
Every field, every null, and the boundary of what is watched — so the data is checkable, not just readable.
Download
One row per (competitor, tier, price state). Regenerated and committed on every export, so any past state is retrievable from git history.
Schema
Field names, types, and null semantics — the same on both files.
| Field | Type | Notes |
|---|---|---|
| competitor | string | The company name. |
| tier | string | The pricing tier name as it appears on the source page. |
| first_observed_at | ISO 8601 timestamp | When this tier state was first observed. |
| last_observed_at | ISO 8601 timestamp | The last observation still holding this same state — one row covers the whole run, not one row per check. |
| monthly_price_usd | number | null | Empty / null means contact sales, never free. A free tier is $0, written explicitly. |
| annual_price_usd | number | null | Same null semantics as monthly_price_usd. |
| billing_unit | "per_seat" | "flat" | "usage" | "unknown" | "unknown" means the source page did not state it, not that none applies. |
| included_seats | number | null | Null when the tier has no seat limit or the page does not state one. |
| is_free | boolean | True only for an explicit $0 tier. |
| is_enterprise | boolean | The source page's own enterprise/custom-pricing tier, not an inference from price. |
| currency | string | Always "USD" — non-USD extractions are recorded internally but excluded from the published dataset. |
| provenance | "live" | "wayback" | "mixed" | See the legend below. |
Provenance legend
- live
- Every observation in this row's run was a direct fetch of the source page.
- wayback
- Every observation in this run was reconstructed from an Internet Archive capture.
- mixed
- The run spans both — a live fetch confirmed a state first seen in a Wayback capture, or vice versa.
Methodology
- Every watched source is checked once daily at 07:00 CT (±30m jitter), plus a weekly synthesis run.
- Pricing is extracted from the raw page HTML by a schema-constrained LLM call. Every numeric price in the output must be findable as a numeral in the page text it was given — a price the model produced but the page never contained fails this grounding check and is never written.
- A candidate change is not published until its new value is observed a second time: live changes confirm the next day, backfilled changes confirm against the following capture. This is the single confirmation rule for both paths.
- Curation policy: a change observed only once, with no persisting second observation, is re-checked by re-extracting both snapshots. Agreement confirms it; disagreement retracts it — and the exclusion is recorded, not silently dropped.
- Resolution differs by source: backfilled history comes from monthly Internet Archive captures, so a change is dated to within a month of when it actually happened, and two changes inside one month can appear as one. Live observations are daily. The provenance column tells you which applies to any given row.
Boundary
Six developer-infrastructure companies were verified by hand as server-rendering their prices in raw HTML: Linear, Notion, Figma, Supabase, Sentry, Postman.
Screened 51 companies; 21 publish comparable plan pricing.
A further 51 developer-infrastructure companies were screened programmatically (bellwether qualify): each candidate's pricing page was fetched, scored for a plausible price-and-tier signal, then run through two independent live extractions. 21 admitted — both extractions agreeing on a plausible tier count (41.2% of the pool) — bringing the watch list to 27 sources. Naming the exclusions is what makes the inclusions credible.
Admitted, by category
- auth (2)
- Auth0, Clerk
- ci-and-build (4)
- Buildkite, Depot, GitHub, GitLab
- data-tooling (1)
- Hasura
- databases (4)
- PlanetScale, Redis Cloud, Turso, Upstash
- email (2)
- Postmark, Resend
- feature-flags (1)
- Statsig
- hosting (4)
- Fly.io, Netlify, Render, Vercel
- observability (2)
- Honeycomb, New Relic
- vector-stores (1)
- Pinecone
Vercel now qualifies. This project's own spec named Vercel the canonical qualification failure: a client-rendered shell with no price in the raw HTML. The screen tests that claim rather than assuming it, and on it no longer held — Vercel's page is server-rendered and extracts as Hobby $0 / Pro $20 / Enterprise contact. That is why it is in the watch list, not an exception to the boundary.
Datadog, Cloudflare, and Fastly publish server-rendered pricing — the screen found real prices in their raw HTML, same as every admitted source. Each is excluded because it prices per product or per unit of usage rather than per plan, so neither extraction attempt reproduced a plan table comparable to the rest of the watch list. They are excluded for comparability, not because they publish nothing.
Excluded (30)
27 reached extraction but did not qualify: extraction failed grounding or schema (16); attempts did not reproduce a plausible tier count (11). 3 more never reached extraction — the pre-filter found essentially no price signal on the page at all.
| Company | Category | Why excluded |
|---|---|---|
| Algolia | search | Attempts did not reproduce a plausible tier countattempt 1 extracted 1 priced tier, attempt 2 extracted 1 priced tier |
| Better Stack | observability | Attempts did not reproduce a plausible tier countattempt 1 extracted 3 priced tiers, attempt 2 extracted 12 priced tiers |
| Bunny | edge-and-cdn | Attempts did not reproduce a plausible tier countattempt 1 extracted 0 priced tiers, attempt 2 extracted 0 priced tiers |
| CircleCI | ci-and-build | Extraction failed grounding or schemaattempt 1 failed: ungrounded: usage_rates.IP ranges per GB of data used.unit_price_usd = 0.27 does not appear in the page text, attempt 2 failed: ungrounded: usage_rates.credits per minute (Linux Medium).unit_price_usd = 0.06 does not appear in the page text; usage_rates.credits per GB (IP ranges).unit_price_usd = 0.27 does not appear in the page text |
| Cloudflare | edge-and-cdn | Extraction failed grounding or schemaattempt 1 failed: ungrounded: usage_rates.vCPU-second.unit_price_usd = 0.00002 does not appear in the page text; usage_rates.GB-second.unit_price_usd = 7e-8 does not appear in the page text, attempt 2 failed: ungrounded: usage_rates.CPU.unit_price_usd = 0.00002 does not appear in the page text; usage_rates.Disk.unit_price_usd = 7e-8 does not appear in the page text |
| Convex | databases | Extraction failed grounding or schemaattempt 1 failed: ungrounded: tiers.Free.monthly_price_usd = 0 does not appear in the page text, attempt 2 failed: ungrounded: tiers.Free.monthly_price_usd = 0 does not appear in the page text |
| Datadog | observability | Attempts did not reproduce a plausible tier countattempt 1 extracted 27 priced tiers, attempt 2 extracted 26 priced tiers |
| Doppler | secrets | Extraction failed grounding or schemaattempt 1 failed: ungrounded: tiers.Developer.monthly_price_usd = 0 does not appear in the page text, attempt 2 failed: ungrounded: tiers.Developer.monthly_price_usd = 0 does not appear in the page text |
| Fastly | edge-and-cdn | Extraction failed grounding or schemaattempt 1 failed: ungrounded: usage_rates.Image Requests (10 Million - 50 Million, per 10,000).unit_price_usd = 0.15 does not appear in the page text; usage_rates.Image Requests (50 Million - 150 Million, per 10,000).unit_price_usd = 0.07 does not appear in the page text; usage_rates.AI Accelerator (20,000 - 2 Million, per 1,000 requests).unit_price_usd = 0.4 does not appear in the page text; usage_rates.Compute Requests (500 Million - 5 Billion, per 1 Million).unit_price_usd = 0.49 does not appear in the page text; usage_rates.Compute Requests (5 Billion - 20 Billion, per 1 Million).unit_price_usd = 0.48 does not appear in the page text; usage_rates.Compute Requests (50 Billion - 125 Billion, per 1 Million).unit_price_usd = 0.4 does not appear in the page text; usage_rates.Compute vCPU Milliseconds (5 Billion - 50 Billion, per 1 Million).unit_price_usd = 0.0495 does not appear in the page text; usage_rates.Compute vCPU Milliseconds (50 Billion - 200 Billion, per 1 Million).unit_price_usd = 0.048 does not appear in the page text; usage_rates.Compute vCPU Milliseconds (200 Billion - 500 Billion, per 1 Million).unit_price_usd = 0.045 does not appear in the page text; usage_rates.Compute vCPU Milliseconds (500 Billion - 1 Trillion, per 1 Million).unit_price_usd = 0.04 does not appear in the page text; usage_rates.Fanout Messages (500 Million - 3 Billion, per 1 Million).unit_price_usd = 0.8 does not appear in the page text; usage_rates.DDoS Protection (500 Million - 3 Billion, per 10,000 requests).unit_price_usd = 0.1 does not appear in the page text; usage_rates.DDoS Protection (3 Billion - 15 Billion, per 10,000 requests).unit_price_usd = 0.04 does not appear in the page text; usage_rates.Key Value Store Class A Operations (100,000 - 1 Million, per 100,000).unit_price_usd = 0.65 does not appear in the page text, attempt 2 failed: ungrounded: usage_rates.Image Requests per 10,000 (10 Million - 50 Million).unit_price_usd = 0.15 does not appear in the page text; usage_rates.Image Requests per 10,000 (50 Million - 150 Million).unit_price_usd = 0.07 does not appear in the page text; usage_rates.AI Accelerator per 1,000 Requests (20,000 - 2 Million).unit_price_usd = 0.4 does not appear in the page text; usage_rates.Compute Requests per 1 Million (500 Million - 5 Billion).unit_price_usd = 0.49 does not appear in the page text; usage_rates.Compute Requests per 1 Million (5 Billion - 20 Billion).unit_price_usd = 0.48 does not appear in the page text; usage_rates.Compute Requests per 1 Million (50 Billion - 125 Billion).unit_price_usd = 0.4 does not appear in the page text; usage_rates.Compute vCPU Milliseconds per 1 Million (5 Billion - 50 Billion).unit_price_usd = 0.0495 does not appear in the page text; usage_rates.Compute vCPU Milliseconds per 1 Million (50 Billion - 200 Billion).unit_price_usd = 0.048 does not appear in the page text; usage_rates.Compute vCPU Milliseconds per 1 Million (200 Billion - 500 Billion).unit_price_usd = 0.045 does not appear in the page text; usage_rates.Compute vCPU Milliseconds per 1 Million (500 Billion - 1 Trillion).unit_price_usd = 0.04 does not appear in the page text; usage_rates.Fanout Messages per 1 Million (500 Million - 3 Billion).unit_price_usd = 0.8 does not appear in the page text; usage_rates.DDoS Protection Requests per 10,000 (500 Million - 3 Billion).unit_price_usd = 0.1 does not appear in the page text; usage_rates.DDoS Protection Requests per 10,000 (3 Billion - 15 Billion).unit_price_usd = 0.04 does not appear in the page text; usage_rates.Key Value Store Class A Operations per 100,000 (100,000 - 1 Million).unit_price_usd = 0.65 does not appear in the page text |
| Grafana Cloud | observability | Extraction failed grounding or schemaattempt 1 failed: ungrounded: usage_rates.per GB Process (Logs).unit_price_usd = 0.05 does not appear in the page text; usage_rates.per GB Write (Logs).unit_price_usd = 0.4 does not appear in the page text; usage_rates.per GB Retain (Logs).unit_price_usd = 0.1 does not appear in the page text; usage_rates.per GB Process (Traces).unit_price_usd = 0.05 does not appear in the page text; usage_rates.per GB Write (Traces).unit_price_usd = 0.4 does not appear in the page text; usage_rates.per GB Retain (Traces).unit_price_usd = 0.1 does not appear in the page text; usage_rates.per GB Process (Profiles).unit_price_usd = 0.05 does not appear in the page text; usage_rates.per GB Write (Profiles).unit_price_usd = 0.4 does not appear in the page text; usage_rates.per GB Retain (Profiles).unit_price_usd = 0.1 does not appear in the page text; usage_rates.per host hour (Kubernetes Monitoring).unit_price_usd = 0.01 does not appear in the page text; usage_rates.per 1k sessions (Frontend Observability).unit_price_usd = 0.75 does not appear in the page text; usage_rates.per host hour (Database Observability).unit_price_usd = 0.07 does not appear in the page text; usage_rates.per virtual user hour (Performance Testing k6).unit_price_usd = 0.15 does not appear in the page text, attempt 2 failed: ungrounded: usage_rates.GB Process (Logs).unit_price_usd = 0.05 does not appear in the page text; usage_rates.GB Write (Logs).unit_price_usd = 0.4 does not appear in the page text; usage_rates.GB Retain (Logs).unit_price_usd = 0.1 does not appear in the page text; usage_rates.GB Process (Traces).unit_price_usd = 0.05 does not appear in the page text; usage_rates.GB Write (Traces).unit_price_usd = 0.4 does not appear in the page text; usage_rates.GB Retain (Traces).unit_price_usd = 0.1 does not appear in the page text; usage_rates.GB Process (Profiles).unit_price_usd = 0.05 does not appear in the page text; usage_rates.GB Write (Profiles).unit_price_usd = 0.4 does not appear in the page text; usage_rates.GB Retain (Profiles).unit_price_usd = 0.1 does not appear in the page text; usage_rates.per host hour (Kubernetes Monitoring).unit_price_usd = 0.01 does not appear in the page text; usage_rates.1k sessions (Frontend Observability).unit_price_usd = 0.75 does not appear in the page text; usage_rates.per host hour (Database Observability).unit_price_usd = 0.07 does not appear in the page text; usage_rates.virtual user hour (Performance Testing k6).unit_price_usd = 0.15 does not appear in the page text |
| Heroku | hosting | Extraction failed grounding or schemaattempt 1 failed: invalid: Failed to parse structured output: Error: Failed to parse structured output as JSON: Expected ',' or '}' after property value in JSON at position 14730 (line 1 column 14731), attempt 2 failed: invalid: Failed to parse structured output: Error: Failed to parse structured output as JSON: Expected ',' or '}' after property value in JSON at position 14608 (line 1 column 14609) |
| Infisical | secrets | Extraction failed grounding or schemaattempt 1 failed: ungrounded: tiers.Pro.annual_price_usd = 264 does not appear in the page text; tiers.Advanced.annual_price_usd = 528 does not appear in the page text, attempt 2 failed: ungrounded: tiers.Pro.annual_price_usd = 240 does not appear in the page text; tiers.Advanced.annual_price_usd = 480 does not appear in the page text |
| LaunchDarkly | feature-flags | Attempts did not reproduce a plausible tier countattempt 1 extracted 1 priced tier, attempt 2 extracted 1 priced tier |
| Lemon Squeezy | payments | Extraction failed grounding or schemaattempt 1 failed: ungrounded: usage_rates.per transaction.unit_price_usd = 0.05 does not appear in the page text, attempt 2 failed: ungrounded: usage_rates.per transaction (ecommerce).unit_price_usd = 0.05 does not appear in the page text |
| Loops | No price signal found in the raw pagefewer than 2 price matches (found 0) | |
| Meilisearch | search | Attempts did not reproduce a plausible tier countattempt 1 extracted 4 priced tiers, attempt 2 extracted 4 priced tiers (low confidence) |
| MongoDB Atlas | databases | Extraction failed grounding or schemaattempt 1 failed: ungrounded: usage_rates.Atlas Data Lake - Virginia, US (us-east-1) - Data Storage per GB per Month.unit_price_usd = 0.048 does not appear in the page text; usage_rates.Atlas Data Lake - Oregon, US (us-west-2) - Data Storage per GB per Month.unit_price_usd = 0.048 does not appear in the page text; usage_rates.Atlas Data Lake - Ireland (eu-west-1) - Data Storage per GB per Month.unit_price_usd = 0.048 does not appear in the page text; usage_rates.Atlas Data Lake - London, England (eu-west-2) - Data Storage per GB per Month.unit_price_usd = 0.0501 does not appear in the page text; usage_rates.Atlas Data Lake - Frankfurt, Germany (eu-central-1) - Data Storage per GB per Month.unit_price_usd = 0.0511 does not appear in the page text; usage_rates.Atlas Data Lake - Mumbai, India (ap-south-1) - Data Storage per GB per Month.unit_price_usd = 0.0522 does not appear in the page text; usage_rates.Atlas Data Lake - Singapore (ap-southeast-1) - Data Storage per GB per Month.unit_price_usd = 0.0522 does not appear in the page text; usage_rates.Atlas Data Lake - Sydney, Australia (ap-southeast-2) - Data Storage per GB per Month.unit_price_usd = 0.0522 does not appear in the page text, attempt 2 failed: ungrounded: tiers.Dedicated (M20).monthly_price_usd = 144 does not appear in the page text; tiers.Dedicated (M30).monthly_price_usd = 388.8 does not appear in the page text; usage_rates.Atlas Data Lake Storage per GB per month (Virginia, Oregon, Ireland).unit_price_usd = 0.048 does not appear in the page text; usage_rates.Atlas Data Lake Storage per GB per month (London).unit_price_usd = 0.0501 does not appear in the page text; usage_rates.Atlas Data Lake Storage per GB per month (Frankfurt).unit_price_usd = 0.0511 does not appear in the page text; usage_rates.Atlas Data Lake Storage per GB per month (Mumbai, Singapore).unit_price_usd = 0.0522 does not appear in the page text; usage_rates.Atlas Data Lake Storage per GB per month (Sydney).unit_price_usd = 0.0522 does not appear in the page text |
| Neon | databases | Attempts did not reproduce a plausible tier countattempt 1 extracted 1 priced tier, attempt 2 extracted 1 priced tier |
| Paddle | payments | Attempts did not reproduce a plausible tier countattempt 1 extracted 0 priced tiers, attempt 2 extracted 0 priced tiers |
| PostHog | observability | Attempts did not reproduce a plausible tier countattempt 1 extracted 2 priced tiers, attempt 2 extracted 1 priced tier |
| Prisma | data-tooling | Extraction failed grounding or schemaattempt 1 failed: ungrounded: usage_rates.Prisma Accelerate operations (Starter plan, per 1,000).unit_price_usd = 0.018 does not appear in the page text; usage_rates.Prisma Accelerate operations (Business plan, per 1,000).unit_price_usd = 0.006 does not appear in the page text; usage_rates.Prisma Accelerate cache tag invalidations (Business plan, per 1,000).unit_price_usd = 0.006 does not appear in the page text, attempt 2 failed: ungrounded: usage_rates.Prisma Accelerate operations (Starter).unit_price_usd = 0.018 does not appear in the page text; usage_rates.Prisma Accelerate operations (Business).unit_price_usd = 0.006 does not appear in the page text; usage_rates.Prisma Accelerate cache tag invalidations (Business).unit_price_usd = 0.006 does not appear in the page text |
| Qdrant | vector-stores | No price signal found in the raw pagefewer than 2 price matches (found 0) |
| Railway | hosting | Extraction failed grounding or schemaattempt 1 failed: ungrounded: tiers.Free Trial.monthly_price_usd = 0 does not appear in the page text; usage_rates.Volumes.unit_price_usd = 6e-8 does not appear in the page text, attempt 2 failed: ungrounded: usage_rates.Volumes.unit_price_usd = 6e-8 does not appear in the page text; tiers.Free Trial.is_free = true but monthly_price_usd = null (free tiers must price at 0) |
| SendGrid | No price signal found in the raw pagefewer than 2 price matches (found 0) | |
| Stripe | payments | Extraction failed grounding or schemaattempt 1 extracted 0 priced tiers, attempt 2 failed: ungrounded: usage_rates.Manually entered card transaction.unit_price_usd = 0.005 does not appear in the page text; usage_rates.International card transaction.unit_price_usd = 0.015 does not appear in the page text; usage_rates.Currency conversion fee.unit_price_usd = 0.01 does not appear in the page text; usage_rates.Klarna transaction.unit_price_usd = 0.0599 does not appear in the page text; usage_rates.ACH Direct Debit transaction.unit_price_usd = 0.008 does not appear in the page text; usage_rates.Stablecoin transaction.unit_price_usd = 0.015 does not appear in the page text; usage_rates.Stablecoin promotional rate.unit_price_usd = 0.008 does not appear in the page text; usage_rates.Payment optimization for custom pricing.unit_price_usd = 0.002 does not appear in the page text; usage_rates.Currency conversion fee minimum.unit_price_usd = 0.02 does not appear in the page text; usage_rates.Instant Payouts.unit_price_usd = 0.015 does not appear in the page text; usage_rates.Currency conversion in balance.unit_price_usd = 0.005 does not appear in the page text; usage_rates.Rate locking.unit_price_usd = 0.0007 does not appear in the page text; usage_rates.In-person international card transaction.unit_price_usd = 0.015 does not appear in the page text; usage_rates.Platform revenue share fee.unit_price_usd = 0.0025 does not appear in the page text; usage_rates.Debit card payout percentage.unit_price_usd = 0.0075 does not appear in the page text; usage_rates.Cross-border payout fee percentage.unit_price_usd = 0.0025 does not appear in the page text; usage_rates.FX fee percentage.unit_price_usd = 0.005 does not appear in the page text; usage_rates.Cross-border transaction.unit_price_usd = 0.01 does not appear in the page text; usage_rates.Billing volume fee.unit_price_usd = 0.007 does not appear in the page text; usage_rates.Invoice generation.unit_price_usd = 0.004 does not appear in the page text; usage_rates.Managed Payments transaction.unit_price_usd = 0.035 does not appear in the page text; usage_rates.Invoicing per paid invoice.unit_price_usd = 0.004 does not appear in the page text |
| Stytch | auth | Extraction failed grounding or schemaattempt 1 failed: ungrounded: usage_rates.Monthly Active Users (beyond 10,000 included).unit_price_usd = 0.02 does not appear in the page text; usage_rates.Device fingerprints (beyond 10,000 included).unit_price_usd = 0.005 does not appear in the page text, attempt 2 failed: ungrounded: usage_rates.Additional monthly active users.unit_price_usd = 0.02 does not appear in the page text; usage_rates.Device fingerprints.unit_price_usd = 0.005 does not appear in the page text |
| Typesense | search | Attempts did not reproduce a plausible tier countattempt 1 extracted 0 priced tiers (low confidence), attempt 2 extracted 0 priced tiers (low confidence) |
| Weaviate | vector-stores | Extraction failed grounding or schemaattempt 1 failed: ungrounded: usage_rates.SNOWFLAKE ARCTIC-EMBED-M-V2.0.unit_price_usd = 0.04 does not appear in the page text, attempt 2 failed: ungrounded: usage_rates.SNOWFLAKE ARCTIC-EMBED-M-V2.0.unit_price_usd = 0.04 does not appear in the page text |
| WorkOS | auth | Attempts did not reproduce a plausible tier countattempt 1 extracted 19 priced tiers, attempt 2 extracted 7 priced tiers |
| Xata | databases | Extraction failed grounding or schemaattempt 1 failed: ungrounded: tiers.Open Source.monthly_price_usd = 0 does not appear in the page text, attempt 2 failed: ungrounded: tiers.Open Source.is_free = true but monthly_price_usd = null (free tiers must price at 0) |
License and citation
Licensed CC BY 4.0 — reuse it anywhere, with attribution.
Citation
Bellwether. Developer-infrastructure pricing archive [dataset]. https://bellwether-nicholas-projects-cdfeb046.vercel.app/data/
BibTeX
@misc{bellwether,
title = {Bellwether: developer-infrastructure pricing archive},
author = {{Bellwether}},
url = {https://bellwether-nicholas-projects-cdfeb046.vercel.app/data/},
note = {CC BY 4.0}
}