openstocks data

Financial data for tokenized stocks.

OpenStocks is a high-performance financial dataset for tokenized stocks: an instrument master covering every tokenized stock on every chain and issuer, plus the live Robinhood Chain memestocks board with equity quotes. Built for traders, quants and institutions — sub-millisecond server time, ETag-cacheable responses, an additive-only contract, a machine-readable spec and bulk formats.

1,113 assets9,735 deployments16 chains11 issuers

quickstart

Three commands.

Keys are free and stateless. Send yours as the x-api-key header on every request; the base URL is https://openstocks.gg.
1 · get a key
KEY=$(curl -s -X POST https://openstocks.gg/v1/keys | jq -r .key)
2 · the stocks board — ranked by memecoin volume, top 5 coins each
curl -s -H "x-api-key: $KEY" \
  "https://openstocks.gg/v1/stocks?limit=200&coinLimit=5" | jq '.stocks[0]'
3 · every NVIDIA token on every chain
curl -s -H "x-api-key: $KEY" \
  https://openstocks.gg/v1/assets/nvidia | jq '.asset.variants[] | {chain, issuerId, address}'

endpoints

Everything you can ask for.

Every route. Authenticated ones want the key in the x-api-key header; the rest are public.

stocks

The live Robinhood Chain memestocks board: tokenized stocks with memecoins trading against them, the stock token’s on-chain stats and the real equity quote. Served pre-built from memory — about a millisecond of server time.

GET

/v1/stocks

x-api-key

The board in one call — stocks that have memecoins, ranked, each with its top coins embedded, board-wide totals, a weak ETag.

sort
vol · liq · move · new· default vol

How stocks are ranked: the coins’ combined volume in tf · combined tradeable coin liquidity · the #1 coin’s price move in tf · newest coin first. rank is the position under this sort.

tf
5m · 1h · 6h · 24h· default 24h

Window used by sort=vol|move and coinSort=vol|move. Every window is always in the body, so a timeframe toggle is local.

coinSort
vol · liq · cap · move · new· default vol

Order of coins.items within each stock. cap = market-cap leaderboard order (= mcapRank).

coinLimit
0 – 25· default 5

Coins embedded per stock. coins.total is always the full count; 0 = stocks only.

limit
1 – 200· default 50

Stocks per page. The board is ~130–180 stocks, so limit=200 is the whole board in one call.

offset
≥ 0· default 0

Page start. Out-of-range integers are clamped and the effective value echoed.

q
≤ 64 chars

Filters stocks by ticker, name, slug, a coin symbol (any of the stock’s coins) or an exact 0x address (stock or coin). A leading $ is ignored.

format
json · csv · ndjson· default json

Response format — see formats below. JSON carries the envelope; CSV and NDJSON are rows only.

Unknown enum values are rejected with 400 and the allowed values; unknown parameter names and empty values fall back to defaults.

curl -s -H "x-api-key: $KEY" \
  "https://openstocks.gg/v1/stocks?sort=vol&tf=24h&coinLimit=5&limit=200"
GET

/v1/stocks/{stockId}

x-api-key

One stock with all of its memecoins, paged, plus links into the rest of the API. stockId is an assetId slug (nvidia), a ticker (NVDA, $nvda) or the Robinhood Chain token address.

tf
5m · 1h · 6h · 24h· default 24h

Window used by coinSort=vol|move.

coinSort
vol · liq · cap · move · new· default vol

Order of coins.items.

coinLimit
0 – 200· default 100

Coins per page.

coinOffset
≥ 0· default 0

Page start within the coins.

404 only for an unknown id or an asset without an official Robinhood Chain token. A stock that exists but has no coins right now answers 200 with rank null, null token stats and coins.total 0 — a deep link never dead-ends.

curl -s -H "x-api-key: $KEY" \
  "https://openstocks.gg/v1/stocks/nvidia?coinSort=cap&coinLimit=200"
GET

/api/memestocks/logos

no key

Stock logo pack: a ZIP of logos/<TICKER>.png with a manifest.json (ticker, name, address, file, source). Edge-cached for an hour.

tickers
AMC,COST,NVDA

Only these tickers (max 400). Unknown ones are dropped silently; $ prefixes are fine.

all
1

Every official Robinhood Chain stock token (about 3.2 MB).

With no parameters you get the stocks currently on the board. The per-IP rate limit applies.

curl -s -o logos.zip \
  "https://openstocks.gg/api/memestocks/logos?tickers=NVDA,TSLA,SPY"

assets

The instrument master: every tokenized stock on every chain and issuer, with verified contract addresses, token standard, wrapper and redeemability metadata for each deployment.

GET

/v1/assets

x-api-key

List and search assets, each with its full list of cross-chain variants.

q
text

Matches ticker, name, slug, alias, or an exact token address.

category
equity · etf · pre-ipo

Instrument category.

chain
chain id

Assets with a deployment on this chain — solana, ethereum, bnb, base, arbitrum, robinhood-chain, …

issuer
issuer id

Assets from this issuer — backed, ondo, robinhood, dinari, binance, …

limit
1 – 500· default 50

Assets per page.

offset
≥ 0· default 0

Page start.

curl -s -H "x-api-key: $KEY" \
  "https://openstocks.gg/v1/assets?issuer=ondo&chain=bnb&limit=10"
GET

/v1/assets/{assetId}

x-api-key

One asset with every variant. assetId is a slug (nvidia), a ticker (NVDA) or an alias (NVDAx).

curl -s -H "x-api-key: $KEY" https://openstocks.gg/v1/assets/nvidia
GET

/v1/assets/{assetId}/variants

x-api-key

Only the deployments of an asset — one row per chain × issuer.

chain
chain id

Keep variants on this chain.

issuer
issuer id

Keep variants from this issuer.

curl -s -H "x-api-key: $KEY" \
  "https://openstocks.gg/v1/assets/tesla/variants?chain=solana"

reference

Issuers, chains, public counts, keys and the spec.

GET

/v1/issuers

x-api-key

All issuers with jurisdiction, redeemability, KYC model, asset and variant counts, and variants per chain.

curl -s -H "x-api-key: $KEY" https://openstocks.gg/v1/issuers
GET

/v1/issuers/{issuerId}

x-api-key

One issuer plus every asset it issues and where.

curl -s -H "x-api-key: $KEY" https://openstocks.gg/v1/issuers/backed
GET

/v1/chains

x-api-key

All chains with VM, token standard, explorer URL template, asset and variant counts, and variants per issuer.

curl -s -H "x-api-key: $KEY" https://openstocks.gg/v1/chains
GET

/v1/stats

no key

Public counts — assets, variants, chains, issuers — with the registry’s generatedAt and the lists of chain and issuer ids.

curl -s https://openstocks.gg/v1/stats
POST

/v1/keys

no key

Create a free key. Optional JSON body { "label": "my app" }. Answers 201 with the key, shown once. 20 keys per hour per IP.

curl -s -X POST -H "Content-Type: application/json" \
  -d '{"label":"my app"}' https://openstocks.gg/v1/keys
GET

/v1/keys

x-api-key

Usage for the calling key: prefix, label, createdAt, lastUsedAt, requestCount, revoked.

curl -s -H "x-api-key: $KEY" https://openstocks.gg/v1/keys
GET

/v1/openapi.json

no key

The machine-readable OpenAPI description of every endpoint on this page — generate a client, or hand it to an agent.

curl -s https://openstocks.gg/v1/openapi.json | jq '.paths | keys'

data dictionary

Every field, once.

The objects returned by /v1/stocks and /v1/stocks/{stockId}. The contract is additive-only — fields are appended, never renamed, removed or re-typed — so a client written today keeps working. These rules apply everywhere:
null
Every key is always present; null means unknown, never zero.
money
USD. 2 decimals; 4 significant figures below $1.
prices
6 significant figures (memecoins live at 1e-7). Tiny numbers may arrive in exponent form.
percents
Signed, on a 0–100 scale: 4.72 means +4.72%. 2 decimals; 3 significant figures below 1.
timestamps
ISO-8601 UTC strings; they compare lexicographically.
windows
Every window object has exactly the keys "5m", "1h", "6h", "24h" — the same vocabulary as the tf parameter.
addresses
Coin.address is lowercase; Stock.token.address is passed through as the registry publishes it — compare case-insensitively.
text
Coin symbol, name, website and twitter are creator-supplied (sanitised, length-capped). Render as text; key your UI by address.

response

The envelope of GET /v1/stocks. The detail endpoint carries the same asOf, stale, session and echoed params, with a single stock instead of stocks.

asOfstringBoard timestamp. Refreshes about every 2 minutes while requests arrive; treat it as monotonic.
stalebooleantrue when asOf is older than 5 minutes at response time (upstream trouble). Computed on the server — recompute from asOf once you cache a body.
session"pre" | "regular" | "post" | "closed"US equity session, one value for the whole board.
sort · tf · coinSort · coinLimit · limit · offset · qechoThe effective query, echoed — clamped integers and truncated q included. q is null when absent.
totalnumberStocks matching q (equals totals.stocks without q).
totalsobjectBoard-wide, always for the whole board: stocks, coins, coinLiquidityUsd, coinVolumeUsd (window), tokenVolumeUsd (window — the stock tokens’ own on-chain volume), newCoins1h, newCoins24h, hot (coins that flipped another in the last 15 min).
stocksStock[]The page, in rank order.

Stock

One tokenized stock on Robinhood Chain. The same object on the list and the detail endpoint.

ranknumber | nullPosition on the full board under sort/tf, before q and paging. null when the stock is off the board (no coins right now).
assetIdstring | nullOpenStocks slug — joins to /v1/assets/{assetId} for every chain, issuer and variant. Typed nullable; never null today.
tickerstringUnderlying ticker, e.g. NVDA.
namestring | nullCompany or fund name.
category"equity" | "etf" | "index" | "pre-ipo" | nullInstrument category.
logostring | nullPNG URL. Every stock has one today.
tokenStock.tokenThe Robinhood Chain stock token — on-chain price, liquidity, volume and links.
equityStock.equity | nullThe real stock. null until a quote is cached (rare on the list; the norm for an off-board stock on the detail endpoint).
coinsStock.coinsMemecoin totals for the stock plus the embedded top coins.
linksobjectDetail endpoint only: asset, variants and web URLs plus the token’s explorer page (explorer may be null).

Stock.token

The stock token itself, as traded on-chain.

chain"robinhood-chain"Always Robinhood Chain on this endpoint.
addressstringToken contract, as the registry publishes it (checksummed). Compare case-insensitively.
variantIdstring | nullRegistry variant id, e.g. nvidia:robinhood:robinhood-chain.
priceUsdnumber | nullOn-chain price from the token’s own stable/native pools.
liquidityUsdnumber | nullOn-chain liquidity. null when the token’s own pools were not seen this refresh — unknown, not zero.
volumeUsdwindow<number> | nullThe token’s own on-chain volume per window. null when unknown.
changePctwindow<number | null> | nullOn-chain price change per window; individual windows may be null.
availableSharesnumber | nullOn-chain total supply × split multiplier.
linksobjecttrade (Bags), dex (DexScreener), explorer (Blockscout; may be null).

Stock.equity

The real equity quote, session-aware. The whole object is null until a quote is cached.

pricenumber | nullFreshest print — regular hours or pre/after-hours.
asOfstring | nullTimestamp of that print.
source"alpaca-sip" | "alpaca-iex" | "yahoo" | nullQuote source.
regularClosenumber | nullLast regular-session close.
prevClosenumber | nullPrevious session close.
changePctnumber | nullprice vs previous close — the number brokers show.
extendedChangePctnumber | nullLast extended-hours print vs the last regular close. Non-null in pre, post and closed whenever they differ; null in regular hours or when unchanged.
marketCapUsdnumber | nullprice × SEC-reported shares outstanding. null without a share count — all ETFs/indexes and ~30% of equities today.
premiumPctnumber | null(token.priceUsd − equity.price) / equity.price × 100: on-chain premium (+) or discount (−).

Stock.coins

Totals across every memecoin under the stock, plus the embedded top coins.

totalnumberAll coins under this stock. items may be fewer — "See all 17".
liquidityUsdnumberΣ tradeable liquidity of all coins.
volumeUsdwindow<number>Σ coin volume per window — what sort=vol ranks stocks by.
new1hnumberCoins launched in the last hour.
leadChangePctwindow<number | null>The #1 coin’s (by market cap) price change per window — what sort=move ranks stocks by.
youngestAtstring | nullcreatedAt of the newest coin — what sort=new ranks stocks by. null when unknown.
itemsCoin[]Top coinLimit coins under coinSort (for coinSort=vol: by volume in the requested tf).

Coin

One memecoin paired with the stock.

addressstringLowercase — the stable key. A coin can appear under several stocks: key list items by assetId + address.
symbolstring | nullCreator-supplied, ≤ 32 chars.
namestring | nullCreator-supplied, ≤ 64 chars.
logostring | nullFull-size image. About 60% of coins have none — render initials.
logoSmallstring | null128 px variant (~3 KB). Use this in every list.
priceUsdnumber | nullPool price in USD.
marketCapUsdnumber | nullnull when unknown — never 0 as unknown.
liquidityUsdnumberTradeable liquidity: what a balanced pool with the pool’s stock reserve holds — not the coin’s own supply priced at spot.
volumeUsdwindow<number>Volume per window.
changePctwindow<number | null>Price change per window. Any window can be null (no trades in it); ~13% of coins have all four null.
txns24hnumberTransactions in the last 24 hours.
createdAtstring | nullEarliest pool with this stock. null when unknown.
isNewbooleancreatedAt within 1 h of asOf (the site’s NEW badge). Recompute from createdAt as the body ages.
launchpadstring | nullFree-form tag ≤ 24 chars: a brand ("Long", "Pons", "Pair.fund"), an AMM version ("v4") or a template name.
stockSharesInPoolnumber | nullStock shares held across the coin’s pool(s) with this stock (Σ stock-side reserves × split multiplier).
sharesControlledPctnumber | nullThat figure as % of token.availableShares — the site’s "x% shares".
dominancePctnumber | nullMemecoin dominance: this coin’s tradeable liquidity as % of all memecoin liquidity under the stock — the site’s "x% dom"; what other trackers call "share".
mcapRanknumberPosition on this stock’s market-cap leaderboard — independent of coinSort.
mcapRankDeltanumber | nullPositions moved since the previous refresh (+ = up). 0 = unchanged; null = unknown (new coin, or a freshly started server). Show an arrow only when non-null and ≠ 0.
hit{ at: string; over: string } | nullSet when the coin overtook another in the last 15 minutes (the site’s HOT badge): when, and which symbol it passed.
linksobjecttrade (Bags), dex (DexScreener), twitter and website (both may be null; http(s) only).

formats

JSON, CSV, NDJSON.

The same board in the shape your tooling wants. Formats apply to GET /v1/stocks; the detail endpoint is JSON.

json · default

The envelope described in the data dictionary: asOf, stale, session, the echoed query, totals and stocks[]. Weak ETag, Content-Type: application/json.

curl -s -H "x-api-key: $KEY" \
  "https://openstocks.gg/v1/stocks?limit=200&coinLimit=5" -o board.json

csv · format=csv

One row per coin, its stock’s columns repeated on every row; with coinLimit=0, one row per stock. The column order is fixed and part of the contract — columns are only ever appended.

curl -s -H "x-api-key: $KEY" \
  "https://openstocks.gg/v1/stocks?format=csv&limit=200&coinLimit=25" -o board.csv

ndjson · format=ndjson

One Stock object per line, no envelope — stream it, or load it straight into a table. Same fields and rounding as JSON.

curl -s -H "x-api-key: $KEY" \
  "https://openstocks.gg/v1/stocks?format=ndjson&limit=200" | head -1 | jq '{ticker, rank}'

freshness & caching

Poll cheaply.

The board rebuilds about every two minutes. Everything else is designed so that asking again costs almost nothing.
refresh
The board refreshes about every 2 min; equity quotes refresh every 20–30 s while the US market is open, and the ETag covers them. Poll every 30–60 s.
asOf · stale
asOf is the board timestamp — treat it as monotonic and accept a 200 only if its asOf is ≥ the one you hold. stale flips true when asOf is older than 5 minutes at response time; a body you cached says false forever, so recompute staleness from asOf on your side.
etag
Every 200 carries a weak ETag and Cache-Control: private, no-cache. Send If-None-Match; an unchanged board answers 304 with an empty body. During sessions expect mostly 200s, outside them mostly 304s.
304
Only ETag, Cache-Control and Vary survive the edge on a 304 — no RateLimit-* or X-Board-* headers. A 304 means “same body as your ETag”, nothing more, and it still counts toward the rate limit.
headers
X-Board-AsOf mirrors asOf; Server-Timing reports auth, view and build durations in milliseconds.
sizes
gzip, measured: whole board (limit=200&coinLimit=5) ≈ 100 KB · default page ≈ 50 KB · coinLimit=0 ≈ 12 KB (≈ 26 KB for 200 stocks) · detail ≈ 1 KB + ~160 B per coin · 304 ≈ 0.
platform caches
If you handle ETags yourself, disable the platform URL cache for these calls (URLSession with urlCache = nil, fetch(url, { cache: 'no-store' }), OkHttp without a cache) — otherwise a 304 can reach your code as a synthesized 200. Or rely entirely on the platform cache and skip manual ETags; pick one.

rate limits

600 a minute.

Sliding-window limits, decided in memory so the request path never waits on the network. Every response tells you where you stand.
600 / min
per key, sliding window
600 / min
per IP address, a global backstop
20 / hour
key creation, per IP
headers
On 200 and 429: RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset (seconds until the window resets) and RateLimit-Policy (600;w=60). A key with an unlimited override gets only RateLimit-Policy: unlimited, so treat the numeric headers as optional. Not present on 304 or other 4xx.
over the limit
429 {"error":{"_tag":"RateLimitError"}} with Retry-After in seconds. 304s count toward the limit too. A per-key override does not lift the per-IP backstop — users behind carrier-grade NAT share an address, so mobile apps should poll through their own backend.
institutions
Higher limits, dedicated feeds and historical snapshots for institutions — email contact@openstocks.gg.

errors

One shape.

Every error is { "error": { "_tag": "<Name>Error", "message": "…" } } with Cache-Control: no-store and no ETag — never store an error body.
400BadRequestErrorbad enum or non-integer parameter→ fix the request — the message lists the allowed values
401UnauthorizedErrormissing or invalid key→ check the x-api-key header; don’t retry
404NotFoundErrorunknown stockId, assetId or issuerId→ show “not found”
429RateLimitErrorover the per-key or per-IP limit→ wait Retry-After seconds (default 30 if absent), then retry
503ServiceUnavailableErrora cold instance is still loading the board — rare, seconds after a deploy→ wait Retry-After (5 s) and retry; keep showing the cached board

changelog

Additive only.

New fields and formats land here first. Nothing is ever renamed, removed or re-typed.
  1. dominancePct on every coin — its tradeable liquidity as a share of all memecoin liquidity under the stock.

  2. coins.leadChangePct and coins.youngestAt on every stock, so sort=move and sort=new can be reproduced locally from one board. Error bodies are no-store.

  3. Bulk formats on /v1/stocks — format=csv and format=ndjson — and the OpenAPI spec at /v1/openapi.json.

  4. Rate limits: 600 requests per minute per key and per IP, RateLimit-* headers on every response, 429 with Retry-After.

  5. GET /v1/stocks and GET /v1/stocks/{stockId} launch — the Robinhood Chain memestocks board with embedded coins, on-chain token stats and equity quotes.