Solana Indexer Schema conventions
How the Solana Historical Data Indexer schema is put together: shared columns, raw base units, partition keys, retention windows, and the padding and naming gotchas that bite first.
// updated 2026-10-21
The Solana database holds 21 tables and 557 columns of decoded DEX activity. This page covers what is true across all of them; the per-table column lists live in the Table reference.
Read this page before your first query. Four conventions - fixed-width padding, raw base units, per-table partition keys, and per-table retention - explain most of the surprises people hit in week one.
Shared columns
Almost every table carries the same transaction envelope. Use it for joins, time-series aggregation, and wallet-level work.
| Column | Type | What it is |
|---|---|---|
block_time | DateTime | UTC timestamp of the block |
block_date_utc | Date | UTC date, and on most tables the partition key |
slot | UInt32 / UInt64 | Solana slot number |
tx_idx | UInt16 / UInt32 | Transaction index within the block |
signature | String / FixedString(128) | Transaction signature, base58 |
fee_payer | String | Wallet that paid the fee |
fee | UInt64 | Fee actually paid, in lamports |
provided_gas_fee | UInt64 | Compute-unit price the transaction offered |
provided_gas_limit | UInt64 | Compute-unit limit requested |
consumed_gas | UInt64 | Compute units actually burned |
parent_program | String | Program that invoked this instruction through CPI |
(slot, tx_idx) identifies a transaction inside a block, and together with signature it is what you join on across tables.
parent_program is the aggregator fingerprint. A swap routed through Jupiter, a bot, or any other program carries that program's address here, while a direct call to the DEX does not. It is the cheapest way to split organic flow from routed flow.
Four gotchas
1. Fixed-width columns are null-padded
Older tables store mints, wallets, and signatures as FixedString(48) or FixedString(128). ClickHouse pads the unused bytes with \0, so a raw value will not compare equal to a plain base58 string, and grouping by it produces keys with invisible trailing bytes.
Strip the padding on both sides of any comparison or join:
SELECT replaceAll(toString(mint), '\0', '') AS mint_clean,
count() AS launches
FROM pumpfun_token_creation
WHERE block_time >= now() - INTERVAL 1 DAY
GROUP BY mint_clean
Newer tables use plain String and need no cleanup. The Table reference shows the exact type per column.
2. Every amount is a raw base unit
Token and SOL amounts are integers in the smallest unit - lamports for SOL, and the mint's own base unit for SPL tokens. Nothing is pre-divided by decimals, and nothing is converted to USD. Divide by 1e9 for SOL; for a token, apply that mint's decimals.
Fee-rate columns follow the same rule: lp_fee_basis_points and protocol_fee_basis_points are basis points, not fractions.
3. The partition key is not the same on every table
Most tables partition on block_date_utc, but not all:
| Partition key | Tables |
|---|---|
block_date_utc | most swap and migration tables |
block_date | meteora_swaps |
(block_date_utc, failed) | pumpfun_v2_swaps |
toYYYYMM(block_time) | pumpfun_amm_admin_set_coin_creator |
toYYYYMMDD(block_time) | pumpfun_creator_fee_distributions |
toYYYYMMDD(toDateTime(entry_timestamp)) | tx_timestamps |
| none | pumpfun_token_creation, pumpfun_all_swaps, raydium_launchpad_token_creation, solana_blocks, max_caps |
Filter on the partition column, and do it in PREWHERE. A swap query with no partition filter reads the whole table - on pumpswap_all_swaps that is about 1.74 TB and 5.16 billion rows in the published snapshot. PREWHERE block_date_utc >= today() - 7 cuts it to the parts you need before any other column is read.
4. Some columns only carry data from a certain date
Columns added after a table went live are marked (populated since ...) in the Table reference. The column exists on every row, but rows older than that date hold the type default - an empty string or a zero, not a NULL. Filter on the date range as well as the column value, or old rows will quietly look like real zeros.
Transaction metadata added to the schema
The published schema includes transaction context on Pump.fun launches and v2 swaps, Raydium swaps and Launchpad events, and Meteora swaps. Depending on the table, this includes fee_payer, blockhash_prefix, compute-budget instruction indexes, num_signatures, transaction_version, lookup_tables, and top_level_transfers_json. Check the exact column list and its populated-since date before joining older records.
Meteora Dynamic Bonding Curve swaps also expose curve_complete, base_reserve_after, and quote_reserve_after. Raydium Launchpad token creation includes amm_fee_on, transfer_fee_basis_points, and transfer_fee_max; its swap and creation tables include instruction_type.
Retention
Retention is per table. Nothing is a single global window.
| Window | Tables |
|---|---|
| 14 days | tx_timestamps, jito_tips |
| 31 days | token_transfers, sol_top_ups |
| 90 days | meteora_swaps, raydium_all_swaps, raydium_cpmm_swaps |
| 1 year | pumpswap_all_swaps, pfamm_migrations, meteora_dynamic_bonding_swaps, all raydium_launchpad_* |
| Full history | pumpfun_all_swaps, pumpfun_v2_swaps, pumpfun_token_creation, pumpfun_creator_fee_distributions, pumpfun_amm_admin_set_coin_creator, solana_blocks, max_caps |
Need a longer window than the table keeps? Extended retention and one-off exports are arranged per customer - start in the portal or ask Telegram support before you build a pipeline that assumes the data will still be there.
Coverage and latency
The deepest table, pumpfun_token_creation, starts 17 January 2024. Every table's own window is listed in the Table reference.
~15 seconds between block confirmation and the row being queryable. Confirmed transactions only - never processed-level data that can revert.
Storage engine
| Setting | Value |
|---|---|
| Database | ClickHouse, default database |
| Engine | MergeTree family |
| Partitioning | daily on the table's date column (see above) |
| Sort order | (slot, tx_idx) on transaction tables |
| Nanosecond timing | separate tx_timestamps table, joined on (slot, tx_idx) |
See also
All 21 tables, column by column.
Working SQL you can paste.
Sub-block timing via tx_timestamps.