GRAPHQLOne endpoint. Two root fields.
Candles for a Solana pool with only the fields you select, plus the venue list. Introspection is on, so you will size this schema yourself within a minute of arriving. There are no mutations, no subscriptions and no nested graph, and it is better that you hear that here.
- Root fields
- 2
- Candle fields
- 15
- Timeframes
- 8
- Candles
- 146M+
Validator to your transport.
- feed
- connecting
- frames this session
- 0
- events decoded
- read live on /products
- last venue
- —
Drop a field. Watch the payload shrink.
A real query against the public endpoint, run while this page was rendered. Every control is a link, so changing the pool, the timeframe or the selection set re-runs it server-side, no client JavaScript is involved, and the byte count on the request line is the measured size of the data block that came back.
/graphql200132 ms11545 B of data60
counted forward from start
query Candles($pair: String!, $tf: String!, $start: String, $n: Int) {
candles(pairAddress: $pair, timeframe: $tf, start: $start, limit: $n) {
timestamp
open
high
low
close
volumeSol
tradeCount
}
}
// variables
{
"pair": "Czfq3xZZDmsdGdUyrNLtRhGc47cXcZtLG4crryfu44zE",
"tf": "1m",
"start": "2026-09-16T12:29:00+00:00",
"n": 60
}{
"data": {
"candles": [
{
"timestamp": "2026-09-16T12:29:00+00:00",
"open": 0.010260701065015901,
"high": 0.010263231357866562,
"low": 0.010253268167993376,
"close": 0.010255008123196315,
"volumeSol": 284781834553,
"tradeCount": 31
},
{
"timestamp": "2026-09-16T12:30:00+00:00",
"open": 0.010255008123196315,
"high": 0.010276911582199936,
"low": 0.010255008123196315,
"close": 0.010268684612366104,
"volumeSol": 1514782469410,
"tradeCount": 72
}
]
}
}The entire schema, printed.
Not an excerpt. This is what __schema returns today, transcribed field for field.
schema {
query: QueryRoot
}
type QueryRoot {
candles(
pairAddress: String!
timeframe: String!
start: String
end: String
limit: Int
): [GqlCandle!]!
protocols: [String!]!
}
type GqlCandle {
timestamp: String!
pairAddress: String!
tokenAddress: String!
baseMint: String!
timeframe: String!
open: Float!
high: Float!
low: Float!
close: Float!
volumeSol: Float!
volumeToken: Float!
tradeCount: Int!
buyCount: Int!
sellCount: Int!
uniqueTraders: Int!
}
# No Mutation type. No Subscription type.
# __schema returns null for both.- POST
/graphqlQueries and introspection: the entire GraphQL surface - GET
/graphqlA stub page that links back to POST. Not a GraphiQL IDE - GET
/api/v1/candles/latestREST only: GraphQL has no newest-first field - GET
/api/v1/protocolsREST equivalent of the protocols field
pumpfunpumpswapraydium_ammraydium_clmmraydium_cpmmorcameteora_damm_v2meteora_dbcmeteora_dlmmmeteora_pools10 venue ids. No arguments, no sub-selection, no pagination. Asking for it returns all of it. That is the second of the two root fields, in its entirety.
timestampString!ISO-8601 with a +00:00 offset, not a Z suffix
pairAddressString!The pool you asked for, echoed back
tokenAddressString!The token side of the pool
baseMintString!The mint the price is denominated in
timeframeString!Echoed back; one of the eight
openFloat!Base mint per token, already decimal-adjusted
highFloat!Highest trade in the bucket
lowFloat!Lowest trade in the bucket
closeFloat!Last trade in the bucket
volumeSolFloat!Lamports despite the name: divide by 1e9
volumeTokenFloat!Base units: divide by 10^decimals
tradeCountInt!Trades in the bucket
buyCountInt!Direction resolved per venue
sellCountInt!Direction resolved per venue
uniqueTradersInt!Distinct wallets in the bucket
Four things that will cost you an afternoon.
limit returns the oldest rowsThere is no latest field. The resolver counts forward from start, which defaults to the beginning of the pool’s history, so limit: 5 on its own hands you the five oldest candles in the index, and end does not anchor it the other way. Pass a recent start; the console above does, and shows you the value it used.
errors arrive with a 200An unknown field or a bad timeframe comes back as HTTP 200 with data: null and an errors array. A status code will not tell you the query failed. A missing or rejected key is a 401, because that is the transport answering rather than the resolver. So branch on the body, not on res.ok.
unitsvolumeSol is lamports, not SOL: divide by 1e9. volumeToken is base units. Prices are already decimal-adjusted, so no decimals lookup is needed, and timestamp carries a +00:00 offset rather than a Z.
nothing streams hereIntrospection returns null for both the mutation and the subscription root, so GraphQL is a request/response surface over candle history. For real-time WebSocket streaming or decoded swaps, use the other transports. And GET /graphql serves a stub page linking back to POST, not a GraphiQL IDE. Point your own client at the endpoint and introspect it.
Need newest-first, or live?
REST has the newest-first candle endpoint this schema lacks, the same candles stream over WebSocket, and every decoded swap has its own feed.
One query, the same decoded corpus.
- 01
Validators
Transactions execute on the Solana cluster.
- 02
Yellowstone Geyser
Transaction updates stream off the node.
- 03
Dexploit engine
Decoded across 10 protocols into one record.
- 04
GraphQL resolver
You select the fields; only those are assembled and returned.
Rate limits, stated per second.
- Rate
- 20 RPS
- History
- Full history
- Support
- Community
- Rate
- 100 RPS
- Connections
- 10 concurrent
- History
- Full history
- Support
- Rate
- 500 RPS
- Connections
- 50 concurrent
- History
- Full history
- Support
- Priority
Limits are per second. The plans have no monthly request quota, so none is quoted here.