For the complete documentation index, see llms.txt. This page is also available as Markdown.

How to Build a Live Solana Candlestick Chart with GetBlock Solana Market Data

A step-by-step guide to streaming OHLCV candles from GetBlock Solana Market Data and rendering a live SOL/USDC chart that updates as trades land.

Every price chart starts the same way: you need candles, and Solana does not hand you any. What the chain gives you is raw transactions across dozens of DEX programs, each with its own instruction layout. To draw a single one-minute candle, you have to subscribe to blocks, decode swaps from Raydium, Orca, Meteora, and the rest, normalize them into a common trade shape, group those trades into time buckets, and keep the whole pipeline correct while slots keep arriving. That is weeks of work before you render a single wick.

GetBlock Solana Market Data does that aggregation for you and streams the finished candles over WebSocket, complete with a backfill so your chart is populated the moment it opens.

The GetBlock Solana Market service is a billable service.

In this guide, you'll build a live SOL/USDC candlestick chart that backfills the last hour on startup and then updates in real time as trades land.

What you'll build

A createCandleStream(options, onSeries) function that:

  1. Opens a WebSocket to the Solana Market Data stream and subscribes to the ohlcv topic.

  2. Seeds the chart with the last 60 one-minute candles from the hydrate backfill.

  3. Upserts rows by their stable id, so the candle that is still open is revised in place.

  4. Removes rows that arrive in deletes as they fall out of the window.

  5. Emits a time-sorted candle array that the browser chart redraws whenever it changes.

How it works

Prerequisites

Project Setup

1

Create the project and install dependencies

The stream client needs nothing extra, but you'll serve the chart to a browser, so install a WebSocket server and dotenv.

2

Add your API key

Create a .env file in the project root:

Solana Market Data passes the key as an apiKey query parameter, not an Authorization header. Because it sits in the URL, keep it server-side — never ship it to the browser.

3

Build the candle stream

This is the core of the project. It subscribes, reconciles the change sets into a Map keyed by id, and hands a sorted array to a callback.

Candle rows arrive with the payload nested under params.result, not result. Only the opening acknowledgement uses a top-level result, and it is a plain string — which is exactly what the typeof msg.result === 'string' check catches.

4

Serve the chart to a browser

A small HTTP server serves the page and relays each new series via a local WebSocket. Keeping the API key on this side means the browser never sees it.

Now the page itself, using TradingView's Lightweight Charts from a CDN:

5

Wire it together and run

The entry point starts the server, starts the stream, and pipes one into the other.

Start it:

Then open http://localhost:8080

The live URL page looks like this:

The terminal prints the candle currently being built:

Read that output closely, because it shows the whole model working:

  • The timestamp stays at 12:26:00 — these are all the same candle, revised.

  • The high climbs 104.33 → 104.46 → 104.48 as bigger trades land in the window.

  • The count stays at 60 — hydrate holds the set size, so an old candle is evicted for each new one.

Understanding the response

Each row in inserts, updates, and deletes is one candle. These are the fields the chart uses, plus the ones worth knowing about.

Field
Type
What it tells you

id

number

Stable identifier for the candle. The key for your local state — the open candle keeps it while it changes.

window_start

string

Start of the candle's period. Use this as the chart's time axis.

window_end

string

End of the candle's period.

window_duration

string

Period as an ISO 8601 duration — a 1m window comes back as PT1M.

open

string

First price traded in the window, in the quote token.

high

string

Highest price traded in the window.

low

string

Lowest price traded in the window.

close

string

Latest price traded — still moving while the window is open.

open_usd

string

Same as open, converted to USD. high_usd, low_usd, close_usd follow the same pattern.

base_volume

string

Volume traded, denominated in the base token.

quote_volume

string

Volume traded, denominated in the quote token.

volume_usd

string

Volume traded, expressed in USD.

data_points

string

How many trades went into the candle. A low count means a thin, less reliable candle.

base / quote

string

Mint addresses of the pair, useful when one socket carries several markets.

Numbers arrive as strings so no precision is lost on very small token prices. Convert with Number() only at the point you draw or compare, and keep the string if you need to display the exact value.

Troubleshooting

Symptom
Likely cause
Fix

Socket closes instantly with code 1006 and no reason

The key is rejected during the handshake, and browsers can't surface the 401

Check GETBLOCK_API_KEY is set and that Solana Market Data is activated on that key.

Chart fills with duplicate candles marching sideways

Appending each message instead of upserting by id

Key your state by row.id and overwrite, as candles.js does.

Cannot read properties of undefined (reading 'result')

Treating the subscription acknowledgement as a candle notification

Return early when typeof msg.result === 'string'.

Value is null or a blank chart from setData

Series not sorted by time, or duplicate timestamps

Sort ascending by time and keep one row per id.

window must be one of 1s, 10s, 30s, 1m, …

An unsupported window such as 7m

Use a listed value.

hydrate must be an integer from 1 to 100

hydrate outside the allowed range

Pass a value between 1 and 100.

base must be a base58-encoded 32-byte Solana public key

A malformed or truncated mint address

Copy the full base58 mint for both base and quote.

Flooded with candles for pairs you didn't ask for

base and quote were omitted, subscribing you to every market

Always set both mints.

Candle count never grows beyond hydrate

Not a bug — hydrate is the size of the retained set, not just a backfill

Raise hydrate (max 100), or keep evicted candles in your own store.

Conclusion

You built a live Solana price chart by subscribing to the ohlcv topic on GetBlock Solana Market Data, reconciling the inserts, updates, and deletes change sets into a Map keyed by each candle's stable id, and relaying the sorted series to a browser that redraws it on every change. The same reconciliation pattern works for every other topic in the product — swap ohlcv for trades, vwap, or volume and only the row's shape changes.

Resources

Last updated

Was this helpful?