Collection market observation history
GET /v1/collections/:address/market/history returns **observation buckets** — how OwlViz-recorded market state changed over time. This is not exchange candle data, Backpack history, or scraped marketplace archives.
Data source: Postgres market_observations rows written at genuine upstream refresh boundaries (Market Watch ticks and cache-bypass refreshes). Lamports are the source of truth.
Query parameters
| Parameter | Default | Description |
|---|---|---|
window | 15m | Lookback: 5m, 15m, 1h, 6h, 24h, 7d |
interval | window-specific | Bucket size: 1m, 5m, 15m, 1h, 4h, 6h, 24h. Must pair with window (see below). |
metric | spot_floor | spot_floor, reference_floor, or provider_floor |
provider | — | Optional filter: magic_eden, tensor, or orbis. **Required** when metric=provider_floor. |
limit | 500 | Max populated buckets returned (cap 500). |
Default intervals
| Window | Default interval |
|---|---|
5m | 1m |
15m | 5m |
1h | 15m |
6h | 1h |
24h | 4h |
7d | 24h |
Allowed `window` / `interval` pairs
5m:1m,5m15m:5m,15m1h:15m,1h6h:1h,6h24h:1h,4h,6h7d:6h,24h
Invalid combinations return 400 with a clear error message.
Metrics
- **
spot_floor** — per timestamp, best (lowest) fresh floor across providers; bucket OHLC over those spot snapshots. - **
reference_floor** — sub-buckets (same size as reference floor computation) rolled into per-interval medians; OHLC over those medians. - **
provider_floor** — single-provider fresh success floors; OHLC in time order.
Only **fresh** observations with status=success and non-null floor_lamports are included (aligned with reference floor rules).
Gaps and sparsity
- Intervals with **no qualifying observations are omitted** from
points. - Response metadata includes
expectedBucketCount,populatedBucketCount, andemptyBucketCountso clients can detect sparse history. - Collections without Market Watch or without recent upstream refreshes may return empty
points— this is expected, not an error.
Coverage metadata
The coverage object summarizes qualifying observations in the requested window (independent of OHLC bucketing):
| Field | Description |
|---|---|
firstObservation | ISO timestamp of earliest qualifying row, or null when empty |
lastObservation | ISO timestamp of latest qualifying row, or null when empty |
sampleCount | Count of qualifying observation rows in the window |
density | high · medium · low · empty — derived from populated bucket ratio |
**Density heuristic** (ratio = populatedBucketCount / expectedBucketCount):
empty—sampleCount === 0low— ratio < 0.25medium— ratio < 0.60high— ratio ≥ 0.60
Provider provenance
Each response also includes:
- **
providerLatestFloors** — most recent fresh success floor per provider in the window. - **
providerRange** —lowLamports/highLamportsmin–max across those latest provider floors (null when no data).
Use these with points[].providers to show multi-marketplace provenance without extra round trips.
Example response
{
"data": {
"collection": "AxFuniPo7RaDgPH6Gizf4GZmLQFc4M5ipckeeZfkrPNn",
"metric": "spot_floor",
"window": "15m",
"interval": "5m",
"provider": null,
"windowStart": "2026-01-15T11:45:00.000Z",
"windowEnd": "2026-01-15T12:00:00.000Z",
"intervalSeconds": 300,
"expectedBucketCount": 3,
"populatedBucketCount": 1,
"emptyBucketCount": 2,
"truncated": false,
"coverage": {
"firstObservation": "2026-01-15T11:55:00.000Z",
"lastObservation": "2026-01-15T11:58:00.000Z",
"sampleCount": 3,
"density": "low"
},
"providerLatestFloors": [
{
"provider": "magic_eden",
"floorLamports": "880000000",
"observedAt": "2026-01-15T11:58:00.000Z"
},
{
"provider": "tensor",
"floorLamports": "900000000",
"observedAt": "2026-01-15T11:55:00.000Z"
}
],
"providerRange": {
"lowLamports": "880000000",
"highLamports": "900000000"
},
"points": [
{
"timestamp": "2026-01-15T11:55:00.000Z",
"openLamports": "900000000",
"highLamports": "950000000",
"lowLamports": "900000000",
"closeLamports": "880000000",
"sampleCount": 3,
"providers": ["magic_eden", "tensor", "orbis"]
}
]
}
}curl
24h spot floor (default interval 4h):
curl -sS "https://api.owlviz.dev/v1/collections/${COLLECTION}/market/history?window=24h&metric=spot_floor"1h spot floor (default interval 15m):
curl -sS "https://api.owlviz.dev/v1/collections/${COLLECTION}/market/history?window=1h&metric=spot_floor"TypeScript
const baseUrl = process.env.OWLVIZ_API_BASE_URL ?? 'https://api.owlviz.dev';
const address = process.env.COLLECTION_ADDRESS!;
const params = new URLSearchParams({
window: '24h',
interval: '4h',
metric: 'spot_floor',
});
const response = await fetch(
`${baseUrl}/v1/collections/${encodeURIComponent(address)}/market/history?${params}`,
{ headers: { Accept: 'application/json' } },
);
if (!response.ok) {
throw new Error(`market/history failed: ${response.status}`);
}
const { data } = (await response.json()) as {
data: {
coverage: { density: string; sampleCount: number };
providerRange: { lowLamports: string | null; highLamports: string | null };
points: Array<{ closeLamports: string }>;
};
};
console.log({
density: data.coverage.density,
samples: data.coverage.sampleCount,
providerRange: data.providerRange,
latestClose: data.points.at(-1)?.closeLamports ?? null,
});See also [docs/api/examples/14-collection-market-history.ts](./examples/14-collection-market-history.ts).
Agents
Compare the **current spot floor** from GET .../market/reference against the **24h OwlViz provider range** from history:
1. Fetch reference: GET /v1/collections/{address}/market/reference?window=15m → spot.floor.lamports.
2. Fetch history: GET /v1/collections/{address}/market/history?window=24h&metric=spot_floor.
3. If providerRange.lowLamports and providerRange.highLamports are set, check whether spot sits inside [low, high]. When spot is below low, note possible undercut vs the observed multi-provider band; when above high, note premium vs recent observations.
4. Use coverage.density before trusting narrow ranges — low or empty means sparse Market Watch data.
Pair with GET .../market/events for discrete change notifications.
Public read; same rate limits as other collection market routes. API keys apply plan RPM and usage metering when present.