wsky1

Data & API access

Every public bottle page has a machine-readable JSON twin, and AI agents get a dedicated /llms.txt — pull hammer prices, medians, and history programmatically, no account or API key required.

Every public price page on wsky1 has a JSON twin: append .json to the URL and you get the same data the page renders, as structured JSON. There's no API key, no auth, and no sign-up — the data is public and served straight from the edge. AI agents and LLMs also get a dedicated /llms.txt.

This page documents the two JSON endpoints, their exact response shapes, and how to read the values.

The two JSON endpoints

Endpoint Keyed by Returns
/prices/<slug>.json the human-readable bottle slug a computed price summary + the 12 most recent observations
/catalog/<key>.json the internal canonical key the raw catalog row + up to 36 raw price points

Both respond with Content-Type: application/json and Cache-Control: public, max-age=3600 (one hour), so hammer the edge freely — repeat requests are served from cache. Each page also advertises its twin as an alternate link (application/json) in the HTML <head>, so tools that follow alternates find it automatically.

/prices/<slug>.json — the bottle summary

The slug is the same one in the page URL — for example /prices/lagavulin-16-year-old:

curl https://wsky1.com/prices/lagavulin-16-year-old.json
{
  "bottle": {
    "displayName": "Lagavulin 16 Year Old",
    "distillery": "Lagavulin",
    "canonicalKey": "lagavulin|16|||||",
    "ageYears": 16,
    "bottlingYear": null,
    "vintageYear": null,
    "region": "Islay",
    "abv": 430,
    "sizeMl": 700
  },
  "priceSummary": {
    "latestGBP": 92,
    "medianGBP": 88,
    "lowGBP": 71,
    "highGBP": 140,
    "currency": "GBP",
    "dataPoints": 24
  },
  "recentHistory": [
    { "observedAt": "2026-06-15T00:00:00.000Z", "priceCents": 9200, "source": "swa", "lots": 3 }
  ]
}

bottle — identity fields for the matched canonical bottle:

  • displayName, distillery, region — human-readable strings (region may be null).
  • ageYears, bottlingYear, vintageYear — integers or null when the release has no such statement.
  • abvABV × 10: 430 means 43.0% ABV. Divide by 10.
  • sizeMl — bottle size in millilitres (defaults to 700).
  • canonicalKey — wsky1's internal identity string (lowercase, pipe-separated). Treat it as an opaque identifier — it's what you pass to /catalog/<key>.json below.

priceSummary — computed over the 24 most recent hammer observations:

  • latestGBP — the newest observed hammer price, in whole pounds.
  • medianGBP, lowGBP, highGBP — median / min / max across those observations.
  • currency — always "GBP".
  • dataPoints — how many observations the summary is built from (024). All the *GBP fields are null when dataPoints is 0.

recentHistory — the 12 most recent observations, newest first:

  • observedAt — ISO-8601 timestamp of the observation.
  • priceCents — the hammer price in integer pennies (9200 = £92.00).
  • source — which feed the observation came from (see Where prices come from for the source codes).
  • lots — how many auction lots that observation aggregates.

A bottle we know but have no price for still resolves — you get the bottle block with a dataPoints: 0 summary. An unknown slug returns { "error": "not found" } with HTTP 404.

/catalog/<key>.json — the raw catalog record

Keyed by the canonical key rather than a slug. The | separators must be URL-encoded as %7C:

curl 'https://wsky1.com/catalog/lagavulin%7C16%7C%7C%7C%7C%7C.json'
{
  "bottle": {
    "id": "cat_...",
    "distillery": "Lagavulin",
    "expression": "16 Year Old",
    "displayName": "Lagavulin 16 Year Old",
    "ageYears": 16,
    "abv": 430,
    "sizeMl": 700,
    "bottler": null,
    "caskType": null,
    "edition": null,
    "region": "Islay",
    "canonicalKey": "lagavulin|16|||||",
    "referencePriceCents": 8800
  },
  "priceHistory": [
    { "observedAt": "2026-06-15T00:00:00.000Z", "priceGBP": 92, "source": "swa" }
  ]
}
  • bottle — the full catalog row (all columns, including expression, bottler, caskNumber, caskType, edition, referencePriceCents, and internal bookkeeping fields). Same unit conventions as above — abv is ×10, referencePriceCents is integer pennies.
  • priceHistory — up to the 36 most recent raw observations, newest first. Here the price is pre-converted: priceGBP is whole pounds (no cents field), alongside observedAt and source.

Use /catalog/<key>.json when you want the raw price series and the complete bottle record; use /prices/<slug>.json when you want the headline numbers already summarised.

Units & conventions

These hold across both endpoints:

  • Currency is GBP, always. Every money value is British pounds sterling.
  • Hammer prices, excluding buyer's premium. Nothing here includes the buyer's premium auction houses charge on top (typically 24–28%). To estimate what you'd actually pay, add it yourself. See Where prices come from.
  • Cents vs. pounds. Fields ending Cents (priceCents, referencePriceCents) are integer pennies; fields ending GBP (latestGBP, priceGBP, …) are whole pounds.
  • abv is ABV × 10 — divide by 10 for the percentage.
  • observedAt is an ISO-8601 timestamp; timestamps are date-level, so intraday time is 00:00:00.
  • source codes map to auction houses (swa = Scotch Whisky Auctions, and so on) — the full list is in Where prices come from. The synthetic market aggregate is a per-distillery estimate, not a real house.

For AI agents & LLMs

wsky1 publishes two plain-text context files for LLM crawlers, and they're explicitly allowed:

  • /llms.txt — a concise site summary: what wsky1 is, an authority statement with citable example claims, pricing, data sources, valuation method, refresh cadence, and the key public pages.
  • /llms-full.txt — the extended version: full methodology, coverage numbers, an FAQ, and per-page descriptions.

Both are served as text/plain. If you're building an agent that cites whisky prices, read /llms-full.txt for the methodology and pull live numbers from the JSON twins above.

What's not public

  • /api/* endpoints are internal. They power the site's own UI (search, the demo, valuation) and are not a supported public API — don't build against them; they can change without notice. The public, stable data surface is the two .json twins on this page.
  • /app/* is the signed-in app and requires authentication — your own portfolio, cost basis, and gain/loss are never exposed through the public JSON.

Notes & limits

  • No key, but be polite. There's no auth, but the one-hour edge cache means you rarely need to re-fetch — cache on your side and respect it.
  • Indicative, not licensed. The data is aggregated from public auction results, not licensed from the houses. Treat every figure as indicative — it's for informational purposes only and is not investment advice.
  • Freshness tracks the daily scrape cadence — see live counts and the last-scrape status on the live stats page.

Related