Skip to content

Market state API

Two public read endpoints return Candle’s own view of a market’s lifecycle and of the trade page’s feed buckets. Neither requires authentication. They are convenience reads: the on-chain registry and contract state remain authoritative for market identity, as described in Terminals and indexers.

GET /api/v1/markets/:chain/:mint

chain is solana or hood. The response is one chain-agnostic shape regardless of whether the mint is a Solana bonding curve or a Hood CandleCurve, cached about 5 seconds server-side:

{
"success": true,
"market": {
"chain": "solana",
"mint": "...",
"lifecycle": "trading",
"buysOpen": true,
"sellsOpen": true,
"curveAddress": "...",
"poolAddress": null,
"quoteMint": "...",
"feeBps": 100,
"graduationVenue": "meteora-damm-v2",
"tier": "open",
"crossingModel": "full-fill-surplus"
}
}
FieldNotes
lifecycletrading, completed, migrated, or recovery.
buysOpen, sellsOpenWhether each side is currently executable. The two are independent; see the table below.
curveAddressThe bonding curve (Solana pool or Hood CandleCurve) address.
poolAddressThe post-migration venue pool on Solana (Meteora DAMM v2); null before migration and always null on Hood, where v3 and v4 pool discovery is on-chain. See Hood market integration.
quoteMintThe quote asset’s mint or token address.
feeBpsCurve trading fee in basis points.
graduationVenuemeteora-damm-v2 on Solana; uniswap-v3 or uniswap-v4 on Hood.
tieropen or exclusive, when known.
crossingModelfull-fill-surplus on Solana, capped-refund on Hood.
migrationGraduation progress. See below.
"migration": { "status": "in_progress", "attempts": 3, "nextAttemptAt": 1754400120000 }
statusMeaning
not_startedThe curve is still trading.
in_progressThe curve is done and the graduated venue is being seeded.
completedTrading has moved to the graduated venue. Carries migratedAt.
delayedAutomated seeding exhausted its retries and a human has been alerted.

migratedAt is stamped when a token first completes migration, so markets that graduated before this release report completed without one. On Hood the block also carries the keeper’s attempts, nextAttemptAt, and gaveUpAt where they apply.

The target is that a completed curve is live on its graduated venue within 10 minutes: the graduation keepers sweep every 2 minutes and retry with backoff. delayed is the machine-readable form of “this one needs a human”, not a normal step in the sequence.

buysOpen and sellsOpen do not always move together:

lifecycleSolanaHood
tradingBuys and sells openBuys and sells open
completedBoth closed during migrationBuys closed, sells still open until migration completes
migratedBoth open on the graduated venueBoth open on the graduated venue
recoveryn/aBoth closed; recovery is a claim flow, not a trade

Errors use the structured envelope described in the Headless launch API: an invalid chain is VALIDATION_FAILED (400) and an unknown market is MARKET_NOT_FOUND (404).

GET /api/v1/markets/:chain/:mint/quote?side=buy|sell&amountIn=<base units>&slippageBps=50

Prices one trade through the exact same integer math the curve runs on-chain, so terminals do not have to re-implement crossing edge cases. Public, rate-limited to 120 requests per minute per client, cached about 5 seconds.

{
"success": true,
"chain": "hood",
"mint": "0x...",
"side": "buy",
"amountIn": "5000000000000000000",
"crossingModel": "capped-refund",
"quote": {
"amountOut": "...",
"fee": "...",
"minAmountOut": "...",
"crossesGraduation": true,
"refund": "...",
"quoteConsumed": "..."
}
}

Every amount is a decimal string in the smallest unit of the quote asset (buys) or the base token (sells).

crossesGraduation applies to buys only, and what it implies differs by chain, which is why crossingModel travels with it. On Hood a crossing buy is capped at the threshold and the remainder refunded, so refund and quoteConsumed tell you how much was actually spent. On Solana a crossing buy fills in full and the surplus stays in the pool, so the refund fields never appear.

minAmountOut applies your slippageBps to the capped fill rather than the uncapped one, so a quote is always satisfiable on-chain, even across graduation.

A curve that cannot execute the requested side returns MARKET_NOT_TRADABLE (409), and the message names where trading continues: the Uniswap pool, the Jupiter or Meteora route, or “buys closed while graduating, sells remain open”.

GET /api/v1/verify/:chain/:mint

The authoritative answer to “is this mint a genuine Candle launch”, so users and terminals can reject look-alike mints. Public, cached about 60 seconds.

{
"success": true,
"candleLaunched": true,
"chain": "hood",
"mint": "0x...",
"tier": "open",
"quoteMint": "0x...",
"graduated": false,
"pool": null,
"createdAt": 1754400000000,
"creator": "0x...",
"viaAgentKey": true,
"provenance": { "curve": "0x...", "factory": "0x...", "configHash": "0x...", "dexVersion": "v4" }
}

An unknown mint, or one launched with non-production visibility, returns 200 with { "candleLaunched": false } rather than a 404, so a caller branches on one response shape either way.

Hood responses carry the provenance block above, re-verifiable against the registry. Solana responses instead carry attribution (the bonding-curve program and the attribution signer), so an indexer can re-derive the same conclusion on-chain using the procedure in Solana market identification.

viaAgentKey is true when the launch came through an agent API key, which is the same signal behind the agent badge in the Candle UI.

GET /api/v1/markets/feed?bucket=new|graduated|onfire|bluechip&chain=solana|hood

A thin public read onto the same feed buckets the trade page tabs use, optionally filtered to one chain, cached about 10 seconds server-side. bucket is required; chain is optional.

{
"success": true,
"bucket": "new",
"tokens": [
{
"chain": "solana",
"address": "...",
"name": "Trend Coin",
"symbol": "TREND",
"image": "https://...",
"isAgent": true
}
]
}

Each row carries chain, address, name, symbol, image, price, volume, and market-cap statistics, and isAgent, which is true for a Candle-origin launch created through an agent API key. An unknown bucket or chain is VALIDATION_FAILED (400).

GET /api/v1/users/:idOrWallet/agent

A public read for any account: whether it has agent features enabled, its address and username, and its launch counts, total versus those attributed to an agent key.

{
"success": true,
"agent": {
"enabled": true,
"address": "...",
"username": "trendbot",
"launches": 12,
"launchesViaApi": 9
}
}

Unlike the market endpoints, this route’s errors are plain: { "error": "User not found" } with status 404.