Skip to main content

Ripple (XRP Ledger) Data

Bitquery provides Ripple / XRP Ledger data dumps in Parquet format, designed for large-scale analytics, historical backfills, and data lake integrations. These datasets can be hosted directly in your own cloud storage (for example, AWS S3) and queried using engines like Snowflake, BigQuery, Athena, Spark, etc.

XRPL is a ledger-object chain rather than an account-and-contract chain. A transaction does not just move a balance — it creates, modifies, or deletes typed objects in the ledger: account roots, trust lines, DEX offers, escrows, checks, NFT offers. Most Bitquery topics mirror that structure, giving you one row per affected ledger object per transaction, with the transaction context attached.

Available Ripple Topics

TopicGrainWhat it holds
transactions_txone row per transactionTransaction envelope: type, fee, sequence, result code, memos, signers
transfers_txone row per value movementUnified view of all value flow — payments, fees, trades, NFT trades, mints
payments_txone row per paymentPayment transactions with full amount / delivered / send-max / deliver-min detail
balancesone row per account per currencyBalance before and after each change, native and issued
account_roots_txone row per account object changeAccount root state: XRP balance, owner count, sequence, domain, transfer rate
ripple_states_txone row per trust line changeTrust line (RippleState) balances between two accounts for an issued currency
offers_txone row per DEX offer changeOrder book offers: taker gets / taker pays, before and after
nftoken_offers_txone row per NFT offer changeNFT buy and sell offers, with the NFToken and the asking price
escrows_txone row per escrow changeEscrow creation, finish, and cancel, with conditions and time locks
checks_txone row per check changeChecks — deferred payment authorizations

Where a topic name ends in _tx, rows carry the transaction that caused the change. Pick transfers_tx when you want a single unified stream of value movement, and the object-level topics when you need XRPL-native state such as trust lines or order books.

Sample Ripple Cloud Dataset

You can explore schemas and validate your tooling using the public Ripple sample datasets:

GitHub reference (schemas & examples)
https://github.com/bitquery/blockchain-cloud-data-dump-sample/tree/main/ripple

Example Parquet file (public S3)

https://bitquery-blockchain-dataset.s3.us-east-1.amazonaws.com/ripple/<topic>/<block_range>.parquet

Sample Parquet downloads (public S3)

Ripple Dataset Directory Structure

bitquery-blockchain-dataset/
└── ripple/
├── account_roots_tx/
│ ├── <start_block>_<end_block>.parquet
│ └── ...
├── balances/
│ ├── <start_block>_<end_block>.parquet
│ └── ...
├── checks_tx/
│ ├── <start_block>_<end_block>.parquet
│ └── ...
├── escrows_tx/
│ ├── <start_block>_<end_block>.parquet
│ └── ...
├── nftoken_offers_tx/
│ ├── <start_block>_<end_block>.parquet
│ └── ...
├── offers_tx/
│ ├── <start_block>_<end_block>.parquet
│ └── ...
├── payments_tx/
│ ├── <start_block>_<end_block>.parquet
│ └── ...
├── ripple_states_tx/
│ ├── <start_block>_<end_block>.parquet
│ └── ...
├── transactions_tx/
│ ├── <start_block>_<end_block>.parquet
│ └── ...
└── transfers_tx/
├── <start_block>_<end_block>.parquet
└── ...

Block Range Naming Convention

Each Parquet file name follows this format:

<start_block>_<end_block>.parquet

Here block is the XRP Ledger ledger index.

Range size varies by topic. Busy topics are written in 50-ledger files, while sparse object types are batched into wider ranges so files do not become tiny. In the samples above, transfers_tx and transactions_tx cover 50 ledgers (93154950_93154999), while checks_tx, escrows_tx, and nftoken_offers_tx cover 200 (93154950_93155149). Discover the files under a prefix rather than assuming a fixed stride.

Density varies enormously. Across the same 50 ledgers the sample files hold about 4,400 transactions, 7,000 transfers, 9,600 balance rows, and 2,600 offers — but Checks are so rare that a 200-ledger file contains a single row.

Common Columns

Most topics share the same transaction-context columns, which makes joining across topics straightforward:

ColumnTypeDescription
blockuint32Ledger index
tx_datedateDate partition of the ledger close time
tx_timedatetimeLedger close time (UTC)
tx_hashstringTransaction hash — the join key across every topic
tx_indexuint32Position of the transaction within the ledger
tx_senderstringAccount that submitted and signed the transaction
tx_typestringXRPL transaction type, e.g. Payment, OfferCreate, TrustSet
operationstringLedger node change type — see below
blockchain_iduint32Bitquery network identifier
prev_txn_idstringHash of the previous transaction that touched this ledger object
prev_ledger_sequint32Ledger index of that previous change
flagsuint32XRPL flag bitfield for the object or transaction

The operation Column

On the object-level topics, operation is the XRPL AffectedNodes change type, and it tells you what happened to the ledger object:

  • CreatedNode – the object came into existence, e.g. an offer was placed or a trust line opened. "Previous" columns are zero.

  • ModifiedNode – the object already existed and changed, e.g. an offer was partially filled or a balance moved.

  • DeletedNode – the object was removed, e.g. an offer was fully consumed or cancelled, or an escrow was finished. The value columns hold the object's final state before removal, not zeros.

A single transaction routinely produces rows across several topics. One OfferCreate that crosses the book can create an offer row, delete counterparty offer rows, and modify two account roots and several trust lines, all sharing one tx_hash.

Correctness Notes

Four things about XRPL will silently produce wrong numbers if you treat the data like an EVM chain.

1. Use delivered_value, Not amount_value

XRPL supports partial payments, where the sender specifies a maximum Amount but the network delivers less. amount_value is the requested ceiling; delivered_value is what actually arrived. This is the exploit that historically drained exchanges that credited deposits from the wrong field.

In the 93154950_93154999 sample, 819 of 2,247 payments are flagged partial, and 346 of them delivered strictly less than the requested amount — sometimes by a factor of 10^15. Summing the wrong column is not a rounding error:

-- native XRP payments only (amount_currency_token_type = '-')
SUM(amount_value) = 13,000,403,476,694 XRP -- 130x the entire XRP supply
SUM(delivered_value) = 482,083 XRP -- correct

Always aggregate delivered_value. The partial column flags affected rows (1 = partial); when partial = 0, the two columns are identical in every row of the sample, so delivered_value is safe to use unconditionally.

2. Drops vs XRP — Units Differ by Topic

XRPL's base unit is the drop, at 1,000,000 drops per XRP. The topics are not uniform:

Topic and columnUnitType
transactions_tx.feedropsstring
account_roots_tx.balance, prev_balancedropsstring
balances.balance, prev_balanceXRPfloat64
transfers_tx.amount_from, amount_toXRPfloat64
payments_tx.*_valueXRPfloat64

Both drop-denominated columns are strings, so they must be cast before arithmetic. Verified against the sample: every one of the 4,394 transactions has transactions_tx.fee exactly 1,000,000× the matching transfers_tx fee row, and all 6,466 joinable account-root rows are exactly 1,000,000× the matching balances row.

CAST(fee AS BIGINT) / 1000000.0 AS fee_xrp

3. Failed Transactions Are Included

transactions_tx contains transactions that were applied to the ledger but did not succeed — they still consume a fee and occupy a sequence number. In the sample, 304 of 4,394 (about 7%) failed, with result codes such as tecPATH_PARTIAL, tecPATH_DRY, tecUNFUNDED_OFFER, and tecINSUF_RESERVE_OFFER.

Filter on success = 1, or equivalently result = 'tesSUCCESS', before counting activity.

4. Issued Currency Codes Are Hex

XRPL supports two currency code formats. Three-character codes such as XRP, POZ, or XPM appear as-is. Longer codes are stored as the 40-character hex the ledger itself carries, so decode them to get a readable ticker:

bytes.fromhex("4D656F7752500000000000000000000000000000").rstrip(b"\x00").decode()
# 'MeowRP'

The same applies in SQL, for example in Athena or Snowflake:

SELECT
rtrim(from_utf8(from_hex(currency_symbol)), chr(0)) AS symbol,
sum(delivered_value) AS volume
FROM ripple_payments
WHERE currency_token_type = 'issued'
GROUP BY 1
ORDER BY 2 DESC

An issued token is only unique as the pair (currency code, issuer) — the same ticker can be issued by many accounts, and anyone may issue one. Filter on the issuer, or use currency_id as a single stable key. Native XRP carries currency_token_type = '-' and currency_address = '-'.

Topic Schemas

Columns listed in Common Columns are omitted below.

transactions_tx

One row per transaction. 4,394 rows in the 50-ledger sample.

ColumnTypeDescription
feestringFee burned, in drops
resultstringXRPL result code, e.g. tesSUCCESS, tecPATH_DRY
successuint81 when result = 'tesSUCCESS'
sequenceuint32Sender's account sequence number
last_ledger_sequenceuint32Last ledger the transaction was valid for
account_txn_idstringOptional chained-transaction identifier
source_taguint32Sender-side routing tag
memosstringJSON array of memos, each with data_hex, decoded data, format, type
tx_signersstringJSON array of signers for multi-signed transactions

transfers_tx

One row per value movement — the unified stream. 7,041 rows in the 93155850_93155899 sample.

ColumnTypeDescription
senderstringAccount the value left; empty on mints
receiverstringAccount the value arrived at; empty on fees
directionstringTransfer classification — see below
amount_fromfloat64Amount debited from sender, in XRP for native
amount_tofloat64Amount credited to receiver
currency_from_*mixedid, symbol, name, address, tokenType of the sent asset
currency_to_*mixedSame set for the received asset
tx_hash_binbinaryTransaction hash as raw bytes — cheaper to join and filter on
tx_type_raw, transaction_typestringRaw and normalized transaction type
tx_sender_raw, transaction_senderstringRaw and normalized submitting account

One transaction produces several transfer rows. direction tells you what each row represents:

  • payment – a direct value transfer between two accounts, XRP or issued token

  • fee – the XRP burned to pay for the transaction. receiver is empty because the fee is destroyed, not paid to a validator

  • trade – a leg of a DEX order-book or AMM execution, where the sent and received currencies differ

  • nft_trade – an NFToken changing hands via NFTokenAcceptOffer

  • mint – an NFToken being created. sender is empty and amount_from is 0

  • other – ledger effects that are not a completed value movement, such as TrustSet or OfferCancel bookkeeping

In the sample, fee rows alone are half the file (3,535 of 7,041), so filter to direction = 'payment' for economic volume and to direction = 'fee' for network fee revenue.

payments_tx

One row per Payment transaction. 2,247 rows in the sample.

ColumnTypeDescription
sender, receiverstringPayment source and destination
amount_valuefloat64Requested amount — a ceiling, not what arrived
delivered_valuefloat64Amount actually delivered — use this
send_max_valuefloat64Maximum the sender was willing to spend
deliver_min_valuefloat64Minimum the sender would accept delivering
partialuint81 when the partial-payment flag was set
amount_*, delivered_*, send_max_*, deliver_min_*mixedEach carries its own currency_id, currency_address, currency_name, currency_symbol, currency_token_type, and issuer
taguint32Destination tag — identifies the end user at an exchange
invoicestringOptional invoice identifier

balances

One row per account per currency per change. 9,640 rows in the sample.

ColumnTypeDescription
accountstringAccount whose balance changed
balancefloat64Balance after the change, in XRP for native
prev_balancefloat64Balance before the change
issuerstringIssuer of the currency; empty for native XRP
currency_*mixedid, address, name, symbol, token_type

The per-row delta is balance - prev_balance. 252 rows in the sample have balance = prev_balance, so a change row does not guarantee a net movement.

account_roots_tx

One row per AccountRoot object change — the account's own XRP balance and settings. 6,742 rows in the sample.

ColumnTypeDescription
accountstringThe account
balancestringXRP balance after the change, in drops
prev_balancestringXRP balance before the change, in drops
owner_countuint32Number of ledger objects the account owns, which sets its reserve
sequenceuint32Account sequence number
domainstringOptional domain the account claims, hex encoded. Rare — 11 of 6,742 rows
transfer_rateuint32Fee an issuer charges on transfers of its token

Nearly all rows are ModifiedNode; CreatedNode marks account funding (16 in the sample).

ripple_states_tx

One row per RippleState (trust line) change. Trust lines hold every issued-token balance on XRPL. 1,857 rows in the sample.

ColumnTypeDescription
low_accountstringThe numerically lower of the two accounts
high_accountstringThe numerically higher of the two accounts
balancefloat64Trust line balance after the change
pre_balancefloat64Trust line balance before the change
currency_*mixedid, address, name, symbol, token_type

The balance is signed, from the low account's perspective. A positive balance means the low account holds the asset; a negative balance means the high account does. In the sample, 909 rows are negative, 562 positive, and 386 zero. Take the absolute value, and use the sign to decide which side holds the token — do not sum raw balances across trust lines.

offers_tx

One row per DEX offer object change. 2,572 rows in the sample.

ColumnTypeDescription
accountstringOffer owner
taker_gets_valuefloat64What the taker receives, after the change
taker_pays_valuefloat64What the taker pays, after the change
pre_taker_gets_valuefloat64Same, before the change
pre_taker_pays_valuefloat64Same, before the change
taker_gets_currency_*mixedCurrency the taker receives
taker_pays_currency_*mixedCurrency the taker pays
book_directory, book_nodestringOrder book placement
expiration, sequenceuint32Offer expiry and owner sequence

Compare the pre_* and post columns to size a fill. On CreatedNode rows the pre_* values are zero; on DeletedNode rows the post columns retain the offer's final state rather than zeroing out, so a deletion is a cancel or a complete fill depending on whether the remaining value went to zero.

nftoken_offers_tx

One row per NFT offer object change. 202 rows in the 200-ledger sample.

ColumnTypeDescription
from_accountstringOffer creator
destination_accountstringRestricted counterparty, when the offer targets one account
nftoken_sell_offerstringSell offer identifier, set on sell-side rows
nftoken_buy_offerstringBuy offer identifier, set on buy-side rows
nftoken_currency_*mixedThe NFToken being offered, with token_type of nft
nftoken_valuefloat64NFToken quantity, normally 1
currency_*mixedCurrency of the asking price — XRP in every sample row
valuefloat64Asking price
book_directory, book_nodestringOffer book placement
expiration, sequenceuint32Offer expiry and owner sequence

Check which of nftoken_sell_offer / nftoken_buy_offer is populated to tell the two sides apart — 68 sell and 59 buy in the sample.

escrows_tx

One row per Escrow object change. 12 rows in the 200-ledger sample.

ColumnTypeDescription
accountstringEscrow creator
destinationstringEscrow beneficiary
amountfloat64Escrowed amount
conditionstringCrypto-condition that must be fulfilled to release
finish_afteruint32Earliest release time, Ripple epoch seconds
cancel_afteruint32Time after which the escrow can be cancelled back
source_tag, destination_taguint32Routing tags
currency_*mixedEscrowed currency

CreatedNode rows are EscrowCreate; DeletedNode rows are EscrowFinish or EscrowCancel — read tx_type to distinguish them.

Time fields use the Ripple epoch, which starts at 2000-01-01T00:00:00Z. Add 946,684,800 to convert to Unix time.

checks_tx

One row per Check object change. Checks are rare — the 200-ledger sample contains one row.

ColumnTypeDescription
accountstringCheck writer
destinationstringCheck recipient
send_maxfloat64Maximum amount the check can be cashed for
expirationuint32Expiry, Ripple epoch seconds
invoice_idstringOptional invoice identifier
source_tag, destination_taguint32Routing tags
sequenceuint32Owner sequence number
currency_*mixedCheck currency

Joining Topics

Every topic carries tx_hash, so it is the natural join key. To attach fee and success to value movement:

SELECT
p.sender,
p.receiver,
p.delivered_value,
CAST(t.fee AS BIGINT) / 1000000.0 AS fee_xrp
FROM ripple_payments p
JOIN ripple_transactions t USING (tx_hash)
WHERE t.success = 1

Because one transaction fans out to many object rows, joining two object-level topics on tx_hash alone produces a cross product. Aggregate one side first, or add block and the object identity columns to the join.

Reading Files in Python

import pandas as pd

BASE = "https://bitquery-blockchain-dataset.s3.us-east-1.amazonaws.com/ripple/"
RANGE = "93154950_93154999"

tx = pd.read_parquet(f"{BASE}transactions_tx/{RANGE}.parquet")
pay = pd.read_parquet(f"{BASE}payments_tx/{RANGE}.parquet")

# fees are drops-as-string
tx["fee_xrp"] = tx.fee.astype("int64") / 1_000_000
print("fees burned:", tx.fee_xrp.sum(), "XRP over", len(tx), "transactions")
print("failed:", (tx.success == 0).sum())

# delivered_value, never amount_value
xrp = pay[pay.amount_currency_token_type == "-"]
print("XRP delivered:", xrp.delivered_value.sum())
print("if you used amount_value:", xrp.amount_value.sum()) # ~27,000,000x too high

Real-Time vs Batch Data Access

Cloud data dumps are optimized for batch analytics and historical workloads.

If you require low-latency or streaming Ripple data, Bitquery also provides: