OwlViz · API docs

Asset & collection intelligence for Solana.

Base URL: https://api.owlviz.dev

Try example mint , agents, or quick start.

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

ParameterDefaultDescription
window15mLookback: 5m, 15m, 1h, 6h, 24h, 7d
intervalwindow-specificBucket size: 1m, 5m, 15m, 1h, 4h, 6h, 24h. Must pair with window (see below).
metricspot_floorspot_floor, reference_floor, or provider_floor
provider—Optional filter: magic_eden, tensor, or orbis. **Required** when metric=provider_floor.
limit500Max populated buckets returned (cap 500).

Default intervals

WindowDefault interval
5m1m
15m5m
1h15m
6h1h
24h4h
7d24h

Allowed `window` / `interval` pairs

  • 5m: 1m, 5m
  • 15m: 5m, 15m
  • 1h: 15m, 1h
  • 6h: 1h, 6h
  • 24h: 1h, 4h, 6h
  • 7d: 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, and emptyBucketCount so 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):

FieldDescription
firstObservationISO timestamp of earliest qualifying row, or null when empty
lastObservationISO timestamp of latest qualifying row, or null when empty
sampleCountCount of qualifying observation rows in the window
densityhigh · medium · low · empty — derived from populated bucket ratio

**Density heuristic** (ratio = populatedBucketCount / expectedBucketCount):

  • empty — sampleCount === 0
  • low — ratio < 0.25
  • medium — ratio < 0.60
  • high — ratio ≥ 0.60

Provider provenance

Each response also includes:

  • **providerLatestFloors** — most recent fresh success floor per provider in the window.
  • **providerRange** — lowLamports / highLamports min–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

json
{
  "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):

bash
curl -sS "https://api.owlviz.dev/v1/collections/${COLLECTION}/market/history?window=24h&metric=spot_floor"

1h spot floor (default interval 15m):

bash
curl -sS "https://api.owlviz.dev/v1/collections/${COLLECTION}/market/history?window=1h&metric=spot_floor"

TypeScript

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.