Skip to content

Market state API

Public read endpoints for Candle’s view of a market: its lifecycle, a curve quote, launch verification, the trade page’s feed buckets, and agent records. None 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",
"candleLaunched": true,
"launchpad": "candle",
"venue": "candle-curve",
"trade": { "endpoint": "POST /api/v1/trade/agent/quote", "routable": true },
"decimals": 6,
"quoteDecimals": 9,
"metadata": { "...": "..." },
"migration": { "status": "not_started", "reserveRaw": "...", "thresholdRaw": "...", "progressBps": 4120 }
}
}
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.
feeBpsThe venue’s own trading fee in basis points, for where the token trades now. On the curve it is the curve’s fee as read on chain (100 by default). After graduation it is the Meteora DAMM v2 pool’s base fee: 50, or 150 or 250 for an experimental fast curve launched with an extra creator fee of 1% or 2%. 0 is a placeholder, not free trading: it is reported for a legacy Meteora launch (whose pool fee Candle does not set), a completed token with no pool yet, and external tokens. It does not include Candle’s own platform fee.
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.
candleLaunched, launchpadWhether Candle launched this mint, and the launchpad that did (candle for a Candle curve).
venueWhere a trade routes now: candle-curve, jupiter, dex, hood-dex, or null while none is available.
trade{ endpoint, routable, reason? }. Stored eligibility for the agent quote endpoint, never a quote or an authorization check. reason says why a market is not routable (for example solana_graduating).
decimals, quoteDecimalsThe token’s decimals and its quote asset’s decimals, for converting amounts to raw base units.
metadataThe token’s name, symbol, image and socials as Candle holds them, or null.
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.
cancelledHood Recovery: no pool will open. Holders redeem from the curve. Overrides migration retries.

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. While a Solana curve is still trading, the block carries live graduation progress read from the pool: reserveRaw (quote raised so far), thresholdRaw (the graduation threshold), both in the quote asset’s raw units, and progressBps (capped at 10000). Watch these rather than the curve address’s balance: the quote sits as wrapped SOL in the pool’s vault.

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 are separate flags. On Solana they can differ; on Hood a graduated curve closes both sides until the pool is seeded (CandleCurve reverts PoolMigrated for any trade outside Trading):

lifecycleSolanaHood
tradingBuys and sells openBuys and sells open
completedBoth closed during migrationBoth closed until the Uniswap pool is seeded (migration.status completed)
migratedBoth open on the graduated venueBoth open on the graduated venue
recoveryn/aBoth closed; recovery is a claim flow, not a trade

When the mint is in Candle’s feed index or Jupiter can name it, the same endpoint returns HTTP 200 with external: true plus discovery flags (jupiterOk, externalTradeable, paperDiscoveryOk, organic0LiveOk). That shape means Jupiter-routable (Solana) or Hood DEX reachable, not a Candle-launched curve, but tradeable on the agent rail when tier allows.

MARKET_NOT_FOUND (404) carries error.routing.reason (chain_mismatch, token_unknown, and others) and error.discovery. On chain_mismatch the mint is indexed on the other chain: discovery.indexed is true and discovery.chain names the chain to retry with. On token_unknown nobody knows the mint (discovery.indexed: false).

Errors use the structured envelope described in the Headless launch API: an invalid chain is VALIDATION_FAILED (400), an unknown market is MARKET_NOT_FOUND (404), and a dependency failure is QUOTE_UNAVAILABLE (503, retryable).

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 and sells closed while graduating”.

This quote covers Candle curves only. A mint that is not a Candle market returns MARKET_NOT_FOUND (404) with a message pointing to POST /api/v1/trade/agent/quote, which quotes external tokens. Other errors: RATE_LIMITED (429), QUOTE_PAIR_UNKNOWN for an unrecognized quote asset, and QUOTE_UNAVAILABLE (503, retryable) when a dependency fails.

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|finalstretch&chain=solana|hood

A thin public read onto the same feed buckets the trade page tabs use, optionally filtered to one chain. It is served live from the feed engine (no cache); while the engine starts it answers 503 FEED_UNAVAILABLE with Retry-After. bucket is required; chain is optional.

The feed also takes where, sort, fields, limit, since_seq and discovery=paper, and the response carries matched, scanned, cursor, warming, organicScoreGuidance, and on a since_seq read reset and removed. bucket=finalstretch (pump.fun tokens at 50% or more of their bonding curve) has no delta: it always answers the whole list with reset: true, and adds asOf, stale, feed and exact. bucket=bluechip is deprecated and will be removed. See Reading the feed for the query syntax.

{
"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,
"tier": "pro"
}
}

tier is the account’s plan today: max, pro, believer or free.

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

GET /api/v1/markets/hood/:mint/mark

A Hood token’s mark: the market provider’s indexed price (priceUsd), plus sellUsd and buyUsd adjusted for the deepest pool’s fees (feeBps, lpFeeBps, hookFeeBps), the pool, and asOf. It is a price for carrying positions, not a quote for a trade. Public, limited to 600 requests per minute per client, cached 10 seconds per mint. A token with no usable price is MARKET_NOT_FOUND (404); a provider failure is QUOTE_UNAVAILABLE (503, retryable).

GET /api/v1/markets/agents/:profileId/copy-score

Whether a published agent’s edge is one a follower could capture, computed from that agent’s own fills. The response carries score with profileId, name, wallet, the assessment, and the inputs it used (a null input was not measured). An agent whose record is not published answers 404 MARKET_NOT_FOUND, the same as an unknown one. Public, 60 requests per minute per client, cached 60 seconds.

GET /api/v1/markets/cohorts/:cohort?limit=10

Agents worth watching, curated from published records and ordered by how followable they are. cohort is steady, high_conviction or early_finder; limit is 1 to 50 (default 10). The response carries cohort, members (each with profileId, name and address), and truncated. Only agents whose owner published their record are eligible. Public, 60 requests per minute per client, cached 2 minutes.

A market recorded in Recovery reports lifecycle: "recovery", both trade flags false, trade.reason: "market_recovery", and migration.status: "cancelled" even if seeding previously exhausted retries. Its recovery block includes curve, quoteToken, reserveSnapshotRaw, eligibleSupplyRaw, surrenderedRaw, method: "claimRecovery(uint256)", and requiresApprovalTo (the curve address). All amounts are raw integer strings, read together at one chain block. The zero quote-token address means native ETH; otherwise the payout is that ERC-20 quote asset.

For a raw token amount, payout is amount * reserveSnapshotRaw / eligibleSupplyRaw, rounded down in quote base units. Approve that amount to the curve on the launched token, then call claimRecovery(amount) on the curve from the same holder wallet. The claim burns the surrendered tokens. Claim order does not change the fixed rate; outstanding claims do not expire. The trade and quote errors use the existing reason market_recovery and retryable: false. No pool will open, and retrying a trade cannot help.