Machine-Gated Catalog Reads (Post-Launch)
GET /catalog and GET /advertisers are free for all clients
at launch. Once the catalog reaches a traction threshold, machine reads via
Accept: application/json will be MPP-gated. Human reads remain
free permanently.
Review note (2026-10-31)
Still Planned. The traction threshold has not been met, so catalog reads are free and ungated today. The only MPP-gated reads are GET /ads/{ad_id}/rank-status ($1.00) and bid placement. Site copy must not describe catalog reads as paid until this ADR is implemented.
Context
The catalog's public read endpoints (GET /catalog,
GET /advertisers) are the viral surface of madcatalog. Humans
sharing links, embedding catalog data in blog posts, and referencing advertiser
profiles on social media all drive advertiser acquisition at zero marginal cost.
Charging for reads at launch would kill this flywheel before it starts.
However, the machine economy creates a distinct second audience for catalog reads: autonomous agents running competitive intelligence pipelines, monitoring advertiser rankings, and making bid decisions programmatically. These agents have operational budgets. They are not the viral audience — they are data consumers.
The challenge is timing: charging too early creates a cold-start problem (agents won't configure payment for an unproven catalog). Charging too late leaves revenue on the table once catalog value is established. The traction threshold (~20+ active advertisers) is the natural inflection point.
Decision
Machine reads of GET /catalog and GET /advertisers
will be MPP-gated post-launch using Accept-header detection, identical to the
mechanism designed for newsletter endpoints:
Accept: text/html→ free for all clients, alwaysAccept: application/json+ valid MPP credential → serve JSON, charge $0.02–$0.05 per readAccept: application/json, no credential →402 Payment Required+ MPP challenge
The 402 response and x-payment-info extension are already
specified in api/openapi.yml to define the contract. Implementation in
the Lambda is gated on reaching the traction threshold, not on a calendar date.
Trigger condition: enable machine-gating when the catalog sustains 20+ active advertisers for 30 consecutive days.
Consequences
Positive
- Creates a recurring revenue stream with zero marginal cost once implemented — catalog reads are pure Lambda compute
- Differentiates human and machine audiences cleanly; the human viral loop is never compromised
- Consistent with the machine-economy thesis: machines pay for machine-grade data access
- The contract is already in the OpenAPI spec; implementation is a Lambda config change, not a design change
- At $0.03/read and 1,000 machine reads/day, annual revenue is ~$11,000 with no new product work
Negative
- Traction threshold is a judgment call — enabling too early risks chilling machine adoption of the catalog as a data source
- Agents that embedded free catalog reads will need to be reconfigured with MPP credentials when gating activates
- Per-read pricing at $0.02–$0.05 may be too low to be worth the implementation and ops overhead; revisit if catalog read volume is lower than expected
- Requires Lambda changes, Accept-header detection logic, and MPP verification for a path that is currently auth-free
Alternatives Considered
| Option | Why not chosen |
|---|---|
| Gate catalog reads at launch | Cold-start problem: agents won't configure payment for an unproven catalog; kills viral adoption before it starts |
| Keep catalog reads free permanently | Leaves a natural monetization surface unused; inconsistent with the thesis that machines pay for machine-grade data; the newsletter already prices this pattern |
| API key subscription instead of MPP per-read | Requires a separate subscription management flow; per-read MPP is simpler, consistent with the rest of the API, and requires no out-of-band key issuance |
Charge for GET /advertisers/{slug} but not GET /catalog |
Inconsistent; both are structured machine-readable data surfaces; gate together or not at all |