heromarket.com/api/v1
Hero Market API v1
Public read-only JSON API for Hero Market. CORS-open, no key required. Anyone can curl these endpoints from a terminal, a notebook, or the browser console. The Hero Market embed widget and any third-party analytics tool consume this same surface.
Conventions
- Base URL:
https://heromarket.com/api/v1 - All responses are JSON with the envelope
{ data, meta }. List responses setmeta.next_offsetwhen more pages exist. - Rate limit: 60 requests per minute per IP. The current remaining count is in
meta.rate_limit_remaining. When exceeded, the API returns HTTP 429 with aRetry-Afterheader. - Cache: responses are cacheable for 10 seconds on the Vercel edge (30 seconds for market detail, prices, and accuracy).
- CORS:
Access-Control-Allow-Origin: *. Hit the API directly from any web page. - Authentication: anonymous only for v1. The
Authorizationheader is parsed but ignored. Higher rate limits via API keys are on the roadmap.
Quick start
# List the 5 most-traded active markets curl -s 'https://heromarket.com/api/v1/markets?limit=5' # Pull one market in full curl -s 'https://heromarket.com/api/v1/markets/olipop-jan-2026' # Top 25 traders by Brier calibration this month, insiders only curl -s 'https://heromarket.com/api/v1/leaderboard?scope=insider&period=month&metric=brier'
Endpoints
- GET
/api/v1/marketsList markets. Default sort is 24-hour volume descending. Returns summary fields per market.
Query params
Name Type Description status string active (default), closed, resolved, all. Filter by market status. category string Filter by category slug, e.g. functional_beverage. limit integer 1 to 100. Default 50. offset integer Pagination cursor. Pass meta.next_offset from the previous response. Example response
{ "data": [ { "slug": "olipop-jan-2026", "title": "Will OLIPOP launch a new flavor in January 2026?", "category": "functional_beverage", "status": "active", "price_yes": 0.62, "price_no": 0.38, "closes_at": "2026-01-31T23:59:59Z", "resolves_by": "2026-02-07T23:59:59Z", "resolved_at": null, "created_at": "2026-05-01T12:00:00Z" } ], "meta": { "version": "1", "generated_at": "2026-05-16T03:00:00Z", "rate_limit_remaining": 59, "next_offset": 50, "total": 87 } } - GET
/api/v1/markets/{slug}Single market detail. Includes description, resolution criteria, pools, total volume, comment count, holder count.
Example response
{ "data": { "slug": "olipop-jan-2026", "title": "Will OLIPOP launch a new flavor in January 2026?", "category": "functional_beverage", "status": "active", "description": "OLIPOP announces a new SKU on or before Jan 31, 2026.", "resolution_criteria": "OLIPOP publicly announces a new SKU on their site, press release, or official social on or before Jan 31, 2026.", "resolution_source": "https://drinkolipop.com/blogs/news", "price_yes": 0.62, "price_no": 0.38, "pool_yes": 482.5, "pool_no": 783.2, "total_volume": 14250.0, "comment_count": 23, "holder_count": 41, "closes_at": "2026-01-31T23:59:59Z", "resolves_by": "2026-02-07T23:59:59Z", "resolved_at": null, "created_at": "2026-05-01T12:00:00Z" }, "meta": { "version": "1", "generated_at": "...", "rate_limit_remaining": 58 } } - GET
/api/v1/markets/{slug}/pricesPrice history, downsampled to ~250 points. Returns an array of (t, price_yes) pairs.
Query params
Name Type Description period string 1h, 6h, 1d (default), 1w, all. Example response
{ "data": [ { "t": "2026-05-16T01:30:00Z", "price_yes": 0.58 }, { "t": "2026-05-16T02:30:00Z", "price_yes": 0.61 }, { "t": "2026-05-16T03:00:00Z", "price_yes": 0.62 } ], "meta": { "version": "1", "generated_at": "...", "rate_limit_remaining": 57 } } - GET
/api/v1/markets/{slug}/tradesRecent trades for the market, newest first. Handles only.
Query params
Name Type Description limit integer 1 to 500. Default 100. Example response
{ "data": [ { "created_at": "2026-05-16T02:58:12Z", "user_handle": "freshfridge", "side": "yes", "shares": 12.5, "cost": 7.75, "price_yes": 0.62 } ], "meta": { "version": "1", "generated_at": "...", "rate_limit_remaining": 56 } } - GET
/api/v1/leaderboardSliceable leaderboard. Same axes as heromarket.com/leaderboard.
Query params
Name Type Description scope string all (default), insider, consumer, category. category string Required when scope=category. period string today, week, month, all (default). metric string profit (default), brier, winrate, volume. limit integer 1 to 100. Default 25. Example response
{ "data": [ { "handle": "freshfridge", "display_name": "Fresh Fridge", "tier": "insider", "profit": 1240.5, "volume": null, "brier": 0.17, "win_rate": 0.71, "markets_resolved": 42, "wins": 30, "losses": 12 } ], "meta": { "version": "1", "generated_at": "...", "rate_limit_remaining": 55 } } - GET
/api/v1/categoriesActive categories with the count of active markets in each.
Example response
{ "data": [ { "slug": "functional_beverage", "name": "Functional Beverage", "market_count": 20 } ], "meta": { "version": "1", "generated_at": "...", "rate_limit_remaining": 54 } } - GET
/api/v1/accuracyPlatform calibration numbers: average Brier, top-decile Brier, and accuracy decay by trade horizon.
Example response
{ "data": { "platform_brier": 0.21, "top_decile_brier": 0.12, "markets_resolved": 384, "by_horizon": [ { "horizon_days_max": 7, "brier": 0.16, "markets_resolved": 110 }, { "horizon_days_max": 30, "brier": 0.21, "markets_resolved": 180 }, { "horizon_days_max": 90, "brier": 0.27, "markets_resolved": 94 } ] }, "meta": { "version": "1", "generated_at": "...", "rate_limit_remaining": 53 } }
Errors
Errors return a non-2xx HTTP status and a JSON body of the shape:
{ "error": "rate_limited", "retry_after_seconds": 42 }400 invalid_*: malformed query param.404 not_found: slug does not resolve.429 rate_limited: slow down; seeRetry-After.500 internal_error: something on our side. Tell us.