Cross-source pricing
Cambridge TCG has a shape for comparing price signals from multiple upstream markets. Today, the collected warehouse contains CardRush observations only. TCGplayer is blocked under the access and use terms currently available to us; Cardmarket's public catalog and price files are a planned reader. This page separates what the architecture can hold from what has actually arrived.
Where this lives in code. The aggregation pipeline ispackages/data-ingest/src/<source>/(one module per upstream). The wholesale writer atapps/wholesale/src/lib/ingest/<source>.tspersists rows toprice_archivewith(card_id, snapshot_date, source, condition)uniqueness. The cross-source view endpoint is/api/v1/prices/[sku]/sourceson wholesale (bearer-gated). The auditpnpm --filter @cambridge-tcg/admin cross-source-divergenceflags outliers across sources for the same card+date.
The four properties every source declares
Every upstream module exports a typed SourceMeta object declaring four things that propagate to every byte that leaves the platform:
| Property | What it means | Where it surfaces |
|---|---|---|
access | How we may reach the source (public-api / public-file / oauth2 / scrape / paid-feed / partner / blocked). | /api/v1/sources response. |
license | The redistribution tier. cc0, cc-by, cc-by-nc, mit, partner-redistributable, internal-only, proprietary. | _meta.source_license array on every response that touches that source's data. |
freshness | The platform's intent on staleness (catalog 24h, price_current 5min, price_historical immutable, market_signal 1min). | _meta.freshness_seconds on every response. |
redistribute | Whether we may re-export raw upstream values verbatim. true requires evidence that an open license covers the upstream data itself, not merely the API client code. | price_archive.source_redistribute per row; propagated through _meta downstream. |
One observed source, two explicit absences
| Source | Region | Currency | License tier | Cadence | Conditions captured |
|---|---|---|---|---|---|
| CardRush | Japan | JPY | internal-only | Collected warehouse observations; daily snapshot intent | NM (status A-) |
| TCGplayer | US | USD | proprietary | Blocked; no collection cadence | No observations collected |
| Cardmarket | Europe | EUR | proprietary | Public daily files; reader not wired | No observations collected |
FX normalisation
The price archive can carry a source's native amount plus a GBP-normalised value computed at write time. Current collected rows are CardRush JPY observations; TCGplayer and Cardmarket have not landed. When conversion occurs, the rate is captured per-row in fx_rate_to_gbp with a fx_rate_source declaration (live / cached / fallback), so an archived conversion retains the rate provenance recorded at capture time.
Substrate honesty applied to FX: the row is the platform's view of what the source charged in GBP at the moment of capture. It is not marked up; we don't transform a TCGplayer market signal into a Cambridge retail price. Cross-source comparison happens at the substrate level.
The headline number per source
Some pricing sources publish several fields. A permitted, implemented reader must declare which field becomes the prominent value and retain the rest with provenance. Only CardRush has an active choice today.
| Source | Headline field | Rationale |
|---|---|---|
| CardRush | A-condition retail JPY | One value per card; no spread offered upstream. |
| TCGplayer | None | Source blocked. Cambridge has no recorded permission for this multi-source pricing use and does not collect its price fields. |
| Cardmarket | Not selected | The public-file reader and its field-level rights record are not wired yet. |
Divergence interpretation
When two permitted sources eventually price the same card on the same date and disagree meaningfully (max/min ratio > 1.5×), the platform is designed to preserve the disagreement rather than hide it in an average. With one observed source today, this is an architecture rule, not a claim of current cross-source coverage. Two kinds of divergence will matter:
- Genuine regional asymmetry. A JP-exclusive printing may trade differently between Japan, Europe, and the US. Different markets can carry different scarcities and prices.
- Upstream anomaly. One source has stale data; an FX rate was applied wrong; a listing on one platform got mispriced. The audit at
pnpm --filter @cambridge-tcg/admin cross-source-divergenceflags outliers (> 5×) for operator review.
The platform's job is to surface the disagreement honestly, not decide which source “wins”. Substrate honesty applied to cross-source aggregation: when sources disagree, the response carries every source's row, not an opaque consensus.
License boundary downstream
Public price responses declare their sources and known source-rights tier in the response envelope's _meta.sources and _meta.source_license arrays. That declaration is a boundary signal, not a substitute for the upstream terms or field-level lineage:
- Open-license tiers apply only when evidence shows the license covers the upstream data in question. A software-client license does not license the content returned by that client.
- proprietary / policy-governedmeans public access did not establish an open redistribution license. The source's stated use policy remains the boundary.
- blocked proprietary sources (including CardRush and unapproved marketplace feeds) are not exposed anonymously, through a session, or through a bearer key. Authentication and transformation do not create upstream rights.
- NOASSERTION marks a mixed export where Cambridge cannot truthfully assign one license to every upstream-derived field.
Federation
The TCGplayer reverse-lookup door at /api/v1/federation/identify/by-upstream is dormant and returns a blocked status. Stored upstream identifiers are not made CC0 merely because Cambridge holds a mapping. Reopening requires written approval covering publication of those identifiers.
Where it began
The substrate widened from one source (CardRush) to many across two kingdoms in May 2026: kingdom-066 (the-cardrush-alignment) added the source column to price_archive; kingdom-080 (the-tcgplayer-alignment) widened the unique key to include condition and added the extra JSONB column plus generalised FX provenance. Adding another source reuses that storage shape, but it is not merely mechanical: access, use, and redistribution rights must be established before its reader is activated.