{"title":"XRPL.to API","version":"1.20","baseUrl":"https://api.xrpl.to/v1","baseUrlNotes":"https://api.xrpl.to/v1 is the canonical base. https://api.xrpl.to/api serves the identical API (internal alias) — use /v1 in new integrations.","openapi":"https://api.xrpl.to/v1/openapi.json","openapiNotes":"Full OpenAPI 3.1 spec (270+ paths with parameter/response schemas) — use it for codegen, Postman, and LLM tooling.","humanDocs":"https://xrpl.to/docs","quickStart":{"example":"curl \"https://api.xrpl.to/v1/tokens?limit=10\"","authenticatedExample":"curl -H \"X-Api-Key: YOUR_KEY\" \"https://api.xrpl.to/v1/tokens?limit=10\"","websocket":"wss://api.xrpl.to/ws/sync/","notes":"Quote URLs in shells (zsh globs on \"?\"). No key needed to start — see rateLimits.anonymous. Get a key: POST /v1/keys (wallet signature) or via https://xrpl.to/docs."},"authentication":{"apiKey":{"description":"Pass your API key via header","header":"X-Api-Key: YOUR_API_KEY","gettingAKey":"Create one with POST /v1/keys (requires a wallet signature — see the keys section) or through the dashboard at https://xrpl.to/dashboard. Free tier: 1 active key, 1M credits/mo.","notes":"Most read endpoints work without a key (anonymous tier). API keys provide higher rate limits and credit allocations."},"walletSignature":{"description":"Required for key management and account operations","headers":{"X-Wallet":"Your XRPL r-address","X-Signature":"Hex signature of message \"{wallet}:{timestamp}\"","X-Timestamp":"Unix milliseconds timestamp (5 min validity window)","X-Public-Key":"Hex public key (always required for /user and /watchlist signed routes; optional for /keys routes after first on-ledger transaction)"}},"userAgent":"Requests with generic programmatic User-Agent strings (e.g. Python-urllib) may be challenged by the CDN/WAF with a non-JSON 403 before reaching the API. Always send a descriptive User-Agent identifying your app (e.g. \"myapp/1.0\")."},"usagePolicy":{"appliesTo":"Every caller — no key, Free, and every paid tier alike.","attribution":"REQUIRED. Any app, website, bot, or dashboard that displays xrpl.to data must show a visible, clickable credit to xrpl.to on the same screen as that data — not buried in an about page. Use: <a href=\"https://xrpl.to\" target=\"_blank\" rel=\"noopener\">Data by xrpl.to</a>","redistribution":"Storing our data and re-serving it from your own API, dataset, or public dashboard requires written permission first. Email hello@xrpl.to — the answer is usually yes, we just need to know who is doing it.","indexers":"Use the paginated list endpoints instead of looping one item at a time, cache what you fetch, and honour Retry-After on a 429 — a repeated 429 means back off, not retry. Get a key rather than running anonymous so we can raise your limits instead of blocking you.","partner":"The invitation-only Partner tier (100 req/sec, 20M credits/mo, direct support) is free for integrations that credit us properly. Email hello@xrpl.to or DM @xrplto on X.","contact":"hello@xrpl.to"},"rateLimits":{"description":"Four independent layers apply to every request; the strictest one wins. All 429 responses carry Retry-After plus X-RateLimit-Limit/-Remaining/-Reset headers and a JSON body with retryAfter.","layers":{"ipBurst":"All clients: 100 requests per 2s per IP (DDoS backstop). 429 with Retry-After: 2.","perSecond":"Sustained rate enforced over a 10s window, plus a hard 1-second burst cap. Anonymous: 2/sec burst, 30/min sustained, per IP. Free: 10/sec sustained with 20/sec bursts (shared per IP across keys). Keyed tiers per key — developer 50/sec (burst 100), partner 100/sec (burst 200), business 200/sec (burst 300), professional 500/sec (burst 750).","perMinute":"Anonymous and free: 30 req/min and 600 req/min per IP respectively.","perEndpoint":"Expensive endpoints have their own request windows, matched by path prefix (so /tokens and /tokens/slugs share one bucket). Base limits are per 60s and scale by tier multiplier: anonymous 0.25x, free 1x, developer 3x, partner 5x, business 10x, professional 15x, enterprise uncapped. Examples at anonymous tier: /tokens 5/min, /search 7/min, /amm 7/min, /account/tx 5/min. Some endpoints pin per-tier caps directly instead of the multiplier (unpinned tiers use the standard multiplier): /dex/quote 1/min anonymous, 3/min free, 45/min partner (10 credits/call); /mentions/x-search 1/min anonymous, 2/min free (10 credits/call); /account-tx-explain 2/min free. The 429 body names the endpoint bucket and its window."},"daily":"Anonymous: 333 requests/day per IP. Free: 33K/day per IP. Keyed tiers per key: developer 400K/day, partner 1M/day, business 4M/day, professional 8M/day. Successful responses include X-RateLimit-Daily-Remaining where a daily cap applies.","headers":"Successful responses include X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset (seconds until the window resets). For anonymous clients these reflect the per-minute window; for keyed clients the per-second window. Credit-metered keys also get X-Credits-Used / X-Credits-Remaining.","anonymous":"2 req/sec burst, 30 req/min, 333/day (no key) + 0.25x endpoint windows","free":"10 req/sec (burst 20, 600/min), 33K req/day per IP, 1M credits/mo (free API key)","developer":"50 req/sec (burst 100), 5 tx/sec, 400K req/day, 10M credits/mo, 3x endpoint windows","partner":"100 req/sec (burst 200), 10 tx/sec, 1M req/day, 20M credits/mo, 5x endpoint windows (invitation-only, for integration partners)","business":"200 req/sec (burst 300), 50 tx/sec, 4M req/day, 100M credits/mo, 10x endpoint windows","professional":"500 req/sec (burst 750), 100 tx/sec, 8M req/day, 200M credits/mo, 15x endpoint windows","notes":"Each endpoint has a credit cost (0-10 credits per call). Check /keys/costs for details. WebSocket messages cost 0.1 credits each. Scam-blocklist reads (/scams/*, /nft/scam) are never rate-limited or capped."},"tokenIdFormat":{"description":"All {id} parameters accept any of these formats:","formats":{"md5":"32-char hex hash","slug":"issuer-currency (e.g. rhcyBrowwApgNonehKBj8Po5z4gTyRknaU-VGB)","issuer_currency":"issuer_currencyHex (40-char hex currency, underscore separator)","mpt":"mpt-<48-char mptIssuanceID> (MPT slug form)","xrp":"Native XRP: use the literal \"XRP\" (or its canonical md5 84e5efeb89c4eae8f68188982dc290d8). Works everywhere a token {id} is accepted, e.g. /token/XRP, /stats/rates?token1=XRP&token2={md5}, /ohlc/XRP."}},"conventions":{"took":"Server-side processing time: integer MILLISECONDS as a JSON number, on every endpoint. Excludes network transfer.","countAndTotal":"One meaning everywhere: count = rows in THIS response; total (also pagination.total / meta.total) = rows matching the query pre-pagination. Exception: on single-resource + metrics responses (/stats, /stats/rates, /token/{id}), the envelope's total is the count of all indexed tokens — a platform stat, not pagination. A few older endpoints use length; its meaning is stated per endpoint.","clamping":"Out-of-range numeric params are clamped, not rejected (limit above max -> max, negative offset -> 0, non-numeric -> default); the effective value is echoed in the response. Invalid enum values fall back to the documented default (e.g. an unknown range/interval/sort). An unknown tag returns 200 with 0 rows and empty tagName — check tagName to distinguish a typo from genuinely no data.","numericStrings":"Very-high-precision amounts (amount, mintSupply, supply, usd, exch on some endpoints, sparkline prices) are serialized as STRINGS on purpose — XRPL token amounts exceed IEEE-754 float precision. Parse with a decimal library if you need exact math.","timestamps":"Unix milliseconds (13 digits) unless a field is explicitly documented otherwise.","attribution":"Successful responses on the tiers that pay nothing (anonymous, free, partner) carry an `attribution` string right after `success`, restating the requirement in usagePolicy. It is informational — safe to ignore in code, but not optional to comply with. Paid tiers omit it and owe the same attribution."},"tokenFields":{"identity":{"md5":"canonical token id — md5(issuer + \"_\" + currency)","issuer":"issuer r-address (\"XRPL\" for native XRP)","currency":"3-char code or 40-char hex currency","name":"display ticker/name","user":"issuer display name","slug":"URL slug (issuer-currency)","tokenType":"trustline|lp|mpt|xls14|demurrage|nonstandard","origin":"launchpad/source platform (FirstLedger, XPMarket, LedgerMeme, ...)","tags":"category tags (see /tags)","dateon":"listing time (unix ms)"},"price":{"exch":"current price in XRP (string, precision-preserving)","usd":"current price in USD (string)","dom":"market dominance % of total XRPL token marketcap","pro5m":"% price change, last 5 minutes","pro1h":"% 1h","pro24h":"% 24h","pro7d":"% 7d","pro30d":"% 30d","maxMin24h":"[max, min] price over 24h — MAX FIRST","athDistance":"% below all-time high"},"market":{"marketcap":"live marketcap in XRP (number; price x circulating supply, computed server-side — do not recompute)","vol24hxrp":"24h volume in XRP","vol24htx":"24h trade count","vol24h":"24h token-side volume","mintSupply":"total issued supply: amount plus tokens locked in escrow (string)","amount":"issued supply held on trust lines, without escrow (string)","supply":"circulating supply (string)","tvl":"AMM total value locked (XRP)","holders":"trustline holders with balance","holders24h":"24h holder-count CHANGE (negative = net loss)","trustlines":"total trustlines","uniqueTraders24h":"distinct traders in 24h","buyerPressure":"buy/sell volume ratio (24h); 1.0 = balanced, above 1 = net buying","buy24hxrp":"24h BUY volume in XRP","sell24hxrp":"24h SELL volume in XRP","buy24htx":"24h buy trade count","sell24htx":"24h sell trade count","vol24hAMM":"24h volume filled by AMM pools (XRP)","vol24hOrderbook":"24h volume filled on the order book (XRP)","liquidityRatio":"24h volume / marketcap (ratio, not a percent)","depthPlus2":"XRP that can be BOUGHT against the AMM pool before its price rises 2%","depthMinus2":"XRP received SELLING against the AMM pool before its price falls 2%","tvlChange24h":"TVL change vs ~24h ago in XRP (from hourly pool snapshots; absent = no 24h-old baseline)","vol6hxrp":"6h traded volume in XRP (hourly recompute)","vol7dxrp":"7d traded volume in XRP (hourly recompute)","gini":"holder-balance Gini coefficient 0-1 (1 = max inequality; basis-independent)","hhi":"Herfindahl concentration index, Σ(holding%)² 0-10000, on total supply","tradingFee":"AMM pool trading fee in rippled TradingFee units: 1/100000 (1000 = 1%)"},"signals":{"rsi15m_90d":"RSI oscillator per timeframe (rsi15m, rsi1h, rsi4h, rsi24h, rsi7d, rsi30d, rsi90d)","trendingScore":"decaying pageview+activity trend score","assessmentScore":"raw risk/quality score (see /token/review for the graded verdict)","creatorHoldingPercent":"DEV %: % of total issued supply (mintSupply) the creator holds or has locked in escrow; on a token with no escrow, its share of amount","top10":"% of total issued supply (mintSupply) held by the top 10 holders, escrow not counted as held","lpBurnedPercent":"% of AMM LP tokens burned","isOMCF":"\"yes\" = listed/indexed token (On Main Chart Filter); tokens without it are unlisted","verified":"verification tier: 1 (company) | 2 | 4 (paid); absent/false = unverified","kyc":"issuer passed Xaman wallet KYC","xVerified":"linked X account is verified","views24h":"site pageviews (24h)"},"notes":"The full object carries additional self-describing metric variants (7d/30d windows, per-fiat conversions). lightweight=true on /tokens strips to the ~50 essential fields listed here."},"tokens":[{"method":"GET","path":"/tokens","description":"List all tokens with metrics","params":{"limit":"int (max 100; default 100, or 50 with tag/filter/filterNe, 20 with sort=assessmentScore|pro5m|trendingScore)","offset":"int (default 0)","sort":"token field name: vol24hxrp|marketcap|pro5m|pro1h|pro24h|pro7d|exch|holders|tvl|trendingScore|assessmentScore|dateon (unknown field = no-op)","order":"asc|desc (default: desc)","tag":"filter by tag (see /tags, case-insensitive)","filter":"text search on token name","watchlist":"watchlist owner account (r-address) — filters to that account's watchlist tokens","show_new":"bool - only tokens created in the last 7 days","show_slug":"bool - only tokens whose slug equals their md5","show_date":"bool - only tokens with missing/unknown date","tags":"yes - include tags array","token_type":"trustline|lp|mpt|xls14|demurrage|nonstandard (invalid value = no filter, echoed back as token_type: null)","skip_metrics":"bool - skip the global metrics envelope (exch/H24/global) — combine with lightweight for the smallest payload","lightweight":"bool - strip each token to ~50 essential fields (~77% smaller per token; see tokenFields)","filterNe":"text - exclude matching tokens (negative filter)","min_vol24hxrp":"number - only tokens with at least this 24h volume in XRP; with sort=pro24h, the gainers that actually traded"},"response":"{ success, took (ms), total (tokens matching the query, pre-pagination), count (tokens in this response), start, limit, tagName, sortBy, sortType, tokens[], exch, H24, global (metrics omitted when skip_metrics=true) }"},{"method":"GET","path":"/tokens/slugs","description":"All token slugs","response":"{ success, count (rows returned), total (all matching), truncated, slugs[] }"},{"method":"GET","path":"/token/{id}","description":"Single token by id (see tokenFields for the token object dictionary)","params":{"desc":"yes - include description","skip_metrics":"bool - omit the global metrics envelope (exch/total/H24/global)"},"response":"{ success, took, total, exch, H24, global, token }"},{"method":"POST","path":"/search","body":"{ search, offset?, limit? }","description":"Search tokens, NFTs, collections, and accounts. limit max 100; offset clamped so offset+limit <= 100","response":"{ success, took, tokens[], collections[] (up to 20, best match first; tolerates one typo, missing spaces, initials like \"baxc\" and extra words like \"nft\" or \"the\"), nfts[] (up to 20; identical names in one collection collapse to one row with editions: N; \"name #N\" queries resolve item N of the named collection), order (result groups strongest match first, e.g. [\"nfts\",\"collections\",\"tokens\"]), account (always present: account object when search is an r-address, null otherwise), pagination: { offset, limit, total, hasMore, maxResults: 100 } }"},{"method":"GET","path":"/tags","description":"List all token tags","response":"{ success, took, exch, H24, global, tags }"},{"method":"POST","path":"/token/{md5}/metadata","auth":"wallet signature (token creator/issuer or admin)","body":"{ user?, domain?, whitepaper?, description?, social?, tags? (max 15; origin/launchpad tags admin-only), linkedCollections? (max 10) }","description":"Update token metadata. 3s per-token write cooldown.","response":"{ success, updated[] }"},{"method":"POST","path":"/token/{md5}/image","auth":"wallet signature (token creator/issuer or admin)","body":"{ imageData: base64 PNG/JPEG/GIF/WebP (max ~650KB decoded) }","description":"Upload token logo image. 3s per-token write cooldown.","response":"{ success, md5, imgUpdated }"}],"charts":[{"method":"GET","path":"/ohlc/{id}","description":"OHLC candlestick data","params":{"range":"1D|5D|7D|1M|3M|1Y|5Y|ALL","interval":"1m|5m|15m|30m|1h|2h|4h|1d|1w","vs_currency":"XRP|USD|EUR|JPY|CNH (default: XRP). EUR, JPY and CNH candles are converted at the ECB reference rate of their own day (the last business day before a weekend or holiday; CNH uses the ECB's CNY); a candle with no rate in the week before it is left out. In USD a candle closes at the XRP/USD rate of its end, and its high and low go only as far as a trade certainly reached (each 5-minute high at the lowest XRP/USD rate of those 5 minutes, each low at the highest), so XRP moving inside a candle draws no wick. In every currency a wick more than 5% past a 5-minute candle's body reaches only as far as real volume traded (2% of the candle's XRP, at least 10 and at most 1000), so a lone off-market trade draws no spike and a real one still shows","resolution":"int (minutes), or D|W|M (also 1D|1W|1M; a week starts Monday 00:00 UTC, a month on the 1st; 1m is a minute)","cb":"int (candle bar count, max 5000)","to":"unix ms (with resolution): a page of history, the cb candles that open before it. Page back by passing the first candle's time; an empty ohlc means the history ends there. Absent, or inside the current candle: the live window ending now","invert":"bool","ind":"indicators instead of candles (with resolution, not to): up to 10 studies, comma-separated, each its id from /ohlc/indicators (id@md5: read off that token's candles instead of this one's, at the same bars) then its inputs after colons in that order, empty = the default (relative-strength-index:14,macd,bollinger-bands:20:2). Computed on these candles with TradingView's formulas, each warmed up on the bars before the first one returned","from":"unix ms (with ind): the first bar wanted; default the last cb","tail":"int (with ind): only the last N bars (a chart's live update)","ha":"bool (with resolution): Heikin Ashi candles (TradingView's), warmed up on the bars before the window; with ind the studies are computed on them","chg":"bool: each row also carries the bar's change against the bar before's close and the change in % (format \"ohlc_change\")"},"response":"{ success, took, length, format, heikin_ashi? (with ha), resolution, interval, interval_seconds, vs_currency, fx_basis? (EUR/JPY/CNH), inverted, base_interval, to? (the page end, with to), ohlc: [[time, open, high, low, close, volume, change? and change %? (with chg)]] }; with ind: { success, took, metric, interval, interval_seconds, vs_currency, t: [time], indicators: [{ id, inputs, offsets (bars a plot is drawn shifted by), plots: [[value or null per bar]], graphics? (pivots, 52-week lines, volume profile: their objects, times in ms) }] }"},{"method":"GET","path":"/ohlc/indicators","description":"The indicators /ohlc/{id}?ind= computes: TradingView's built-in studies with their inputs and defaults, plots, bands, fills, palettes and the objects some draw. A `symbol` input takes another token's md5 (its price candles at the same bars; up to 4 other tokens a request); a `time` input is unix ms","response":"{ success, indicators: [{ id, name, short, overlay, format?, inputs: [{ id, name, type, def, min?, max?, options?, hidden? }], plots: [{ id, title?, kind, color?, width?, ... }], bands?, fills?, palettes?, paletteNames?, graphics?, tool? (placed from the chart's left toolbar, not its Indicators list) }] }"},{"method":"GET","path":"/sparkline/{id}","description":"Price sparkline","params":{"period":"24h|7d (default: 7d)","vs_currency":"XRP|USD (default: XRP)","lightweight":"bool - sample data points","max_points":"int (default: 20, range 2-500; only applies when lightweight=true)","pro24h":"float - override percentChange/chartColor when period=24h","pro7d":"float - override when period=7d"},"response":"{ success, chartColor, period, percentChange, lightweight, vsCurrency, data: { prices[], timestamps[], max, min } }"},{"method":"GET","path":"/holders/info/{id}","description":"Holder concentration (top10/20/50/100 %)","response":"{ success, took, richInfo: { time, length, holders, top10, top20, top50, top100, active24H } }"},{"method":"GET","path":"/holders/graph/{id}","description":"Holder count history","params":{"range":"1D|7D|1M|3M|1Y|5Y|ALL (default: 7D)"},"response":"{ success, took, length, history[] }"},{"method":"GET","path":"/holders/list/{id}","description":"Paginated holder richlist with acquisition tracking","params":{"offset":"int","limit":"int (default: 20, max 200)","freeze":"bool - only frozen trustlines","sort":"balance|value|holding|percent|change24h|pnl (value/percent are balance/holding aliases; default: balance desc)","order":"asc|desc","search":"account address (min 3 chars)","minBalance":"float - min balance (token units)","maxBalance":"float - max balance (token units)"},"response":"{ success, took, summary: { top10Hold, top20Hold, top50Hold, top100Hold, frozen, lockedPct (share of total issued supply in escrow) }, length (total holders; filtered count when search/minBalance/maxBalance set), richList[]: { account, balance, holding, rank, freeze, frozenByIssuer?, isCreator, xrpBalance, lastActive, name, domain, funder, tradedPct?, tradeCount?, source?, acquisition?, isAMM?, pnl? (realized+residual XRP), roi?, unrealized?, bought?/sold? (XRP volume), lastTrade? } }"},{"method":"GET","path":"/holders/movers/{id}","description":"Top 24h buyers and sellers by holder balance delta","params":{"limit":"int per side (default: 20, max 50)"},"response":"{ success, took, limit, holders (total), movers (holders with a 24h change), buyersTotal, sellersTotal, boughtTotal/soldTotal (token-unit sums over all movers) + boughtTotalXrp/soldTotalXrp, pressure?: { m15/m30/h1/h4/h24: { sellers, sellPressureXrp (holdings value of recently-active sellers), buyers, buyPressureXrp (XRP balances of recently-active buyers) } }, buyers[]/sellers[] (capped at limit): { account, name, balance, balance24h, change (token delta, signed), holding (% of supply), value? (current holdings worth, XRP), pnl?/roi? (trading gain/loss XRP + %), lastTrade? (last trade of this token), breakevenMcap? (entry breakeven: mcap XRP where their held tokens equal their FIFO cost), trades? (24h balance-change events [timeMs, side, xrpValue, mcapAtEvent, kind] from trusts_24h + hourly candles; side 1=received/-1=sent, kind 0=trade/1=LP/2=transfer, max 120), isAMM?, isCreator?, freeze? } }"},{"method":"GET","path":"/rsi","description":"RSI indicator data","params":{"offset":"int","limit":"int (max 100)","sort":"rsi15m|rsi1h|rsi4h|rsi24h|rsi7d|rsi30d|rsi90d|marketcap|vol24hxrp|pro24h|pro7d|pro30d|holders|tvl|uniqueTraders24h (default: rsi24h; invalid values fall back)","order":"asc|desc","timeframe":"15m|1h|4h|24h|7d|30d|90d","filter":"text search","tag":"string","origin":"string","minMarketCap":"float","maxMarketCap":"float","minVolume24h":"float","maxVolume24h":"float","minPriceChange24h":"float","maxPriceChange24h":"float","minRsi15m":"float","maxRsi15m":"float","minRsi1h":"float","maxRsi1h":"float","minRsi4h":"float","maxRsi4h":"float","minRsi24h":"float","maxRsi24h":"float","minRsi7d":"float","maxRsi7d":"float","minRsi30d":"float","maxRsi30d":"float","minRsi90d":"float","maxRsi90d":"float","activeOnly":"bool","minHolders":"int","minTVL":"float","excludeNeutral":"bool","minUniqueTraders":"int"},"response":"{ success, took, timeframe, sort, order, filters, tokens[], count, total, start, limit, cached? }"},{"method":"GET","path":"/stats","description":"Platform metrics","response":"{ success, took, total, exch, H24, global }"},{"method":"GET","path":"/stats/rates","description":"Exchange rates between tokens","params":{"token1":"token id","token2":"token id"},"response":"{ success, took, total, exch, H24, global, rate1, rate2 }"},{"method":"POST","path":"/stats/metrics","body":"{ timestamps: [unix_ms, ...] }","description":"Historical exchange rates by timestamps (XRP base)","response":"{ success, took, length, history[]: { timestamp, XRP (USD price), USD (XRP per 1 USD), EUR (XRP per 1 EUR), JPY (XRP per 1 JPY), CNY (XRP per 1 CNY) } }"},{"method":"GET","path":"/stats/marketcap-history","description":"Daily OMCF-filtered marketcap (same as homepage gMarketcap) back to 2021-09-26","params":{"from":"YYYY-MM-DD (required)","to":"YYYY-MM-DD (optional, default today)"},"response":"{ success, took, from, to, length, history[]: { date, gMarketcap (XRP, filtered), unfilteredMarketcap (XRP, incl. illiquid), tokenCount } }"},{"method":"GET","path":"/stats/fear-greed-history","params":{"days":"int 1-365 (default 30)"},"description":"xrpl.to's Fear & Greed index for the XRP Ledger market over time: one reading a day, taken when xrpl.to's daily report is built (late evening UTC; each reading carries its time), from 5 October 2026 on (nothing older was kept). The live value is global.sentimentScore in /stats","response":"{ success, took, index, days, from, readings[] { date, value, label, at }, bands, note }"},{"method":"GET","path":"/holders/ranks/{id}","description":"Bulk account-to-rank lookup within the holder richlist","params":{"accounts":"comma-separated r-addresses (max 200)"},"response":"{ success, ranks: { account: rank|null }, total, source: redis|mongo }"},{"method":"GET","path":"/holders/position/{id}/{address}","description":"One account's stake in a token — balance, share of supply and rank, without building the holder list","params":{},"response":"{ success, took, account, md5, balance, holding (% of supply, 2dp rounded down — same figure as /holders/list), rank (null until the worker ranks the token), holders }"},{"method":"GET","path":"/sparkline/batch","description":"Batch sparklines for multiple tokens (each rebuilt on the token's next trade)","params":{"md5s":"comma-separated md5 hashes (required, max 100)","period":"24h|7d (default: 7d)","maxPoints":"int (default: 50, range 2-500)","vs_currency":"XRP|USD (default: XRP)"},"response":"{ sparklines: { <md5>: [price, ...] } }"},{"method":"GET","path":"/holders/empty/{id}","description":"Zero-balance trustlines (dust/airdrop accounts; precomputed bot accounts excluded)","params":{"limit":"int (1-1000; omit to return the full list)","offset":"int (default: 0, only with limit)"},"response":"{ success, accounts[], count, total, excluded, offset?, limit? }"}],"trading":[{"method":"GET","path":"/history","description":"Trade history by token, account, or both","params":{"md5":"token id (optional if account provided)","account":"r-address","limit":"int (max 1000)","offset":"int","start_time":"unix ms","end_time":"unix ms","type":"trades|liquidity|all (default: trades)","liquidity_type":"create|deposit|withdraw","pair_type":"xrp|token","xrp_only":"bool","xrp_amount":"float (min XRP threshold)","cursor":"int","direction":"asc|desc (default: desc)"},"response":"{ success, took, data[], meta: { total, offset, limit, direction, nextCursor, hasMore, truncated?, timeRange } }","notes":"Cursor pagination: pass meta.nextCursor as cursor; it is null on the last page (meta.hasMore false). One row per Payment and pair (its fills merged), so meta.total counts fills; a page ends on a whole ledger and can hold a few rows more or fewer than limit. Offset pagination capped at 100k rows (meta.truncated: true, total capped to servable window; use cursor for deeper history). Trades carry sourceTag (numeric, on-chain SourceTag) and/or platform (canonical name for memo-identified platforms, e.g. SPYDEX)"},{"method":"GET","path":"/amm","description":"List AMM pools","params":{"sort":"fees|apy|liquidity|volume|volume24h|created (fees, apy, liquidity and volume rank the 7-day figures)","status":"active|all","limit":"int (max 100)","offset":"int","token":"token id","currency":"currency code filter","issuer":"issuer address filter","minLiquidity":"only pools holding at least this much, in XRP (7-day liquidity)"},"response":"{ success, took, summary, offset, limit, total (matching pools), count (pools in this response), totalPages, pools[] }"},{"method":"GET","path":"/amm/info","description":"Live AMM pool info","params":{"amm_account":"AMM account address","asset":"XRP | token id","asset2":"XRP | token id"}},{"method":"GET","path":"/amm/liquidity-chart","description":"Historical TVL chart data","params":{"token":"token id","token2":"token id","ammAccount":"address","period":"1d|5d|1w|1m|3m|6m|1y|5y|all","poolType":"xrp|token|all"}},{"method":"GET","path":"/pairs","params":{"md5":"token id (required)"},"description":"Trading pairs for a token","response":"{ success, took, count, limit: 100, truncated, pairs[] }"},{"method":"GET","path":"/orderbook","description":"Live DEX orderbook","params":{"base":"token id","quote":"token id","limit":"int (max 400, default 20)"},"response":"{ success, pair, base, quote, bids[], asks[], ledger_index (falls back to ledger_current_index) }"},{"method":"POST","path":"/dex/quote","description":"Get swap quote with routing / path finding (successor to POST /account/path-find). Exact-output via destination_value, or exact-input via source_value","body":"{ source_account, destination_token, destination_value?, source_value?, source_token?, slippage?, destination_account? (the Payment Destination when the output goes to another account; the price is the same, the receive-side checks read that account) }","response":"{ status, took, requiresTrustline, quote: { source_amount, destination_amount, execution_rate, price_impact, amm_price_change (pool spot price % change if the fill executes — buys positive, sells negative; single-leg XRP routes with AMM participation, else null), suggested_slippage (percent — recommended DeliverMin tolerance for this trade; use it instead of a flat value), paths (XRPL Paths the Payment must include — required for token-to-token routes, empty otherwise), platform_fee_drops (xrpl.to's swap fee for this quote as a drops string: platform_fee_bps of the XRP side — sent on a buy, delivered on a sell — rounded down; \"0\" for token-to-token. The xrpl.to swap UIs sign it in the same Batch as the swap), platform_fee_bps (40), warnings[] ({code, message} pre-flight checks: insufficient_balance | line_frozen | dest_limit_low | token_frozen | split_recommended; with a destination_account: recipient_no_trustline | recipient_not_authorized | recipient_line_frozen | recipient_limit_low | recipient_unverified — each means the Payment would fail), destination_account (echoed when it differs from source_account), route, paths_count }, alternatives[] }"},{"method":"GET","path":"/pairs/{id}","description":"Trading pairs for a specific token","response":"{ success, took, count, limit: 100, truncated, pairs[] }"}],"account":[{"method":"GET","path":"/account/balance/{account}","description":"XRP balance with reserves","params":{"simple":"bool - minimal response","rank":"bool - include rank (slow)"},"response":"{ balance, total, balanceDrops, spendableDrops, ownerCount, sequence, flags, reserve, reserveDrops, name, inception, parent, rank? }"},{"method":"POST","path":"/account/balance","body":"{ accounts[] }","description":"Batch balance lookup (max 100)","response":"{ success, took, count, data[]: { account, balance (spendable after reserves), total } }"},{"method":"GET","path":"/account/balance/pair/{account}","params":{"curr1":"currency","issuer1":"issuer","curr2":"currency","issuer2":"issuer"},"description":"Token pair balances"},{"method":"GET","path":"/account/offers/{account}","params":{"pair":"pair hash","md5":"token id (filters offers to that token)","offset":"int (snaps down to a limit-multiple; effective offset echoed)","limit":"int (1-50, default 10; out-of-range resets to 10)"},"description":"Open DEX offers","response":"{ success, took, offset, limit, total, offers[] }"},{"method":"GET","path":"/account/tx/{account}","description":"Full transaction history","params":{"limit":"int (1-200, default 200)","marker":"JSON string (URL-encoded): the marker of the previous page, e.g. {\"ledger\":N,\"seq\":M}. It names the last row returned; the next page starts right after it","types":"comma-separated TransactionTypes","forward":"bool (default: false)","ledgerMin":"int - ledger index lower bound","ledgerMax":"int - ledger index upper bound","local":"bool (default: false) - first page from our node only (its newest ~3-4 days, possibly fewer than limit); fast first paint"},"response":"{ success, took, count, hasMore, marker, txs[] }","notes":"Own rate window: 20 requests/min per IP on Free, 5/min without a key, more on paid tiers (see rateLimits.layers.perEndpoint)"},{"method":"GET","path":"/account/info/{account}","description":"Account info with DB enrichment"},{"method":"GET","path":"/account/trustlines/{account}","params":{"limit":"int (max 400)","marker":"string","peer":"r-address (filter to single counterparty)"},"description":"Account trustlines"},{"method":"GET","path":"/account/trustline/{account}/{issuer}/{currency}","description":"Single trustline lookup"},{"method":"GET","path":"/account/objects/{account}","params":{"type":"offer|escrow|check|nft_offer|...","limit":"int (max 400)","marker":"string"},"description":"Account objects"},{"method":"GET","path":"/account/currencies/{account}","description":"Sendable/receivable currencies"},{"method":"GET","path":"/account/nfts/{account}","params":{"limit":"int (max 400)","marker":"string"},"description":"NFTs owned by account"},{"method":"GET","path":"/account/gateway/{account}","params":{"hotwallet":"comma-separated addresses"},"description":"Gateway balances (issuer totals)","notes":"Returns 503 \"Account too large, retry shortly\" on issuer-scale timeouts"},{"method":"GET","path":"/account/noripple/{account}","params":{"role":"gateway|user"},"description":"NoRipple check"},{"method":"GET","path":"/account/channels/{account}","params":{"destination":"string","limit":"int (max 400)","marker":"string"},"description":"Payment channels"},{"method":"GET","path":"/account/deposit-authorized","params":{"source":"r-address","destination":"r-address"},"description":"Check deposit authorization"},{"method":"GET","path":"/account/ancestry/{account}","description":"Account ancestry tree","params":{"direction":"up|down|both","limit":"int (max 200)","include_tokens":"bool","token_limit":"int (1-500, default 100)"},"notes":"Requires Developer tier or above (403 otherwise)"},{"method":"GET","path":"/account/token-stats/{account}/{md5}","description":"Per-token trading stats for account","response":"{ success, account, md5, balance, avgBuyPrice, totalBought, totalSold, totalSpentXRP, totalReceivedXRP, realizedPnl, unrealizedPnl, totalPnl, holdingValue, avgBuyMcap, currentMcap, totalRoi, firstTradeTime, tradeCount, exit, took }","notes":"holdingValue/unrealizedPnl mark the bag at the pool SPOT price. `exit` is what selling the WHOLE balance returns right now through the swap engine (same fill as POST /dex/quote): { xrp (proceeds), impactPct (execution vs spot, negative), poolChangePct (pool spot after the fill, null off-AMM), pnl (P&L at that price, same basis as totalPnl — never above it), split (true when one transaction cannot fill it) }. null when the holding is dust, the pair has no liquidity, or the engine cannot price it. Refreshed every 10 s."},{"method":"GET","path":"/trustlines/{account}","description":"Trustlines with token values","params":{"offset":"int (exact, no page snapping)","limit":"int (1-50, default 20; up to 2000 when sort_by_value=true)","format":"raw|default|full","sort_by_value":"bool","search":"text - filter by currency/name/symbol","includeZero":"bool - include zero-balance lines","lightweight":"bool - strip each line’s token join to the ~30 essential fields (see tokenFields; ~90% smaller per line)"},"response":"{ success, took, account, accountActive, accountData, xrp, total, zeroCount, offset, limit, lines[], issued[]?, tokenCount?, totalValue?, portfolioChange24h? ({ usd, pct } | null with format=full: the XRP held plus the lines totalValue sums, valued now and 24h ago at today's balances), truncated? + subsetSize? (sort_by_value on accounts >2000 lines), partial? }"},{"method":"GET","path":"/watchlist","auth":"wallet signature","params":{"account":"r-address"},"description":"Get token watchlist"},{"method":"GET","path":"/watchlist/nft","auth":"wallet signature","params":{"account":"r-address"},"description":"Get NFT watchlist"},{"method":"GET","path":"/account/recent-trades/{account}","description":"Recent trades for an account","params":{"limit":"int (1-100, default 30)","since":"unix ms - only trades after this time"},"response":"{ success, account, count, trades[] }"},{"method":"POST","path":"/account/names","body":"{ accounts[] }","description":"Resolve account display names + verification status in bulk (max 50)","response":"{ success, names: { address: name } }"},{"method":"POST","path":"/watchlist","auth":"wallet signature","body":"{ md5, account, action: \"add\"|\"remove\" }","description":"Add/remove token on watchlist"},{"method":"POST","path":"/watchlist/nft","auth":"wallet signature","body":"{ nftokenId, account, action: \"add\"|\"remove\" }","description":"Add/remove NFT on watchlist"},{"method":"GET","path":"/account/snapshot/{account}","description":"Point-in-time XRP + token holdings valued in XRP at a historical ledger (resolve time to ledger via /ledger/at first)","params":{"ledger":"int (required) - ledger index","closeMs":"unix ms - close time; selects historical vs current pricing"},"response":"{ success, took, account, ledger, close_time, exists, xrp, tokens[], totalXrp, truncated? + maxLines? }"},{"method":"GET","path":"/account/checks/{account}","description":"Check ledger objects with live funding status for cashable checks","response":"{ success, took, account, objects[], truncated? }"},{"method":"GET","path":"/account/search-by-name","params":{"q":"string (min 2 chars; name, domain, or X handle)"},"description":"Search accounts by display name / X profile","response":"{ success, resolvedName, results[] }"}],"ledger":[{"method":"GET","path":"/ledger","description":"Current validated ledger info","response":"{ ledger_index, ledger_hash, close_time (ms), close_time_human, parent_hash, total_coins, txn_count, validated, reserve_base_drops, reserve_inc_drops }","notes":"reserve_base_drops / reserve_inc_drops are the validator-voted account reserve settings, in drops. reserve_inc_drops is the Fee an AccountDelete or AMMCreate MUST carry (one owner reserve) — read it here, never hardcode it: validators can vote the increment, and a stale constant means telINSUF_FEE_P if it rose or a silent overpayment if it fell."},{"method":"GET","path":"/ledger/{index}","description":"Ledger by index with transactions","params":{"index":"ledger index or: validated|closed|current","transactions":"bool (default: true)","expand":"bool (default: false)"}},{"method":"GET","path":"/ledger/entry","description":"Fetch specific ledger object","params":{"index":"entry index","account_root":"address","offer":"JSON { account, seq }","amm":"JSON { asset, asset2 }","check":"ID","escrow":"JSON { owner, seq }","directory":"JSON { dir_root | owner, sub_index? } or plain 64-hex index","payment_channel":"ID","deposit_preauth":"JSON { owner, authorized }","ticket":"JSON { account, ticket_seq }","nft_page":"index"},"notes":"Malformed JSON in object params returns 400"},{"method":"GET","path":"/tx/{hash}","description":"Transaction by hash (64-char hex)"},{"method":"GET","path":"/ledger/at","params":{"time":"unix ms or ISO date (required)"},"description":"Resolve a datetime to the last ledger closed at or before it (binary search). Close times are rounded to 10 seconds, so a ledger often closes exactly on the time asked. For a day [start, end): its first ledger is the one returned for start when that closed exactly at start, else the next; its last is the one returned for end, or the one before it when that closed exactly at end","response":"{ ledger_index, close_time, clampedToHead?, clampedToGenesis? }"}],"submit":[{"method":"POST","path":"/submit","description":"Submit signed transaction","body":"{ tx_blob, fail_hard? }","constraints":"Max tx_blob: 131KB hex. Per-tier rate limit: Free 1/sec, Developer 5/sec, Partner 10/sec, Business 50/sec, Professional 100/sec.","response":"{ success, took, engine_result, engine_result_code, engine_result_message, tx_json, hash, accepted, applied, broadcast, kept, queued, account_sequence_available, account_sequence_next, validated_ledger_index, outcome? }","notes":"Duplicate tx_blob within 30s returns 409"},{"method":"POST","path":"/submit/preview","body":"{ tx_json } or { tx_blob }","description":"Dry-run transaction preview (400 on invalid tx input); under fee escalation a tx_json is simulated at the fee that enters the open ledger and reported for the Fee it named; sender_after = simulated sender position {balance, reserve, spendable}, null when no sender AccountRoot change","response":"{ success, took, engine_result, engine_result_message, meta, tx_json, sender_after }"},{"method":"POST","path":"/rpc","description":"The XRP Ledger's own READ commands against MAINNET — the twin of POST /testnet/rpc, so a developer who learned the sandbox changes the prefix and nothing else. Reads only, answered as the node answers any client: feature without vetoed, ledger without full or accounts, the node's non-admin page limits, server_info without host name or ports (submitting is POST /submit); the node's in-band error (actNotFound, entryNotFound) rides in result with a 200, as on the node","body":"{ method (one of the read commands), params (the command's own fields as an object) }","response":"{ success, took, method, result (the node's own result, verbatim) }","notes":"Same list as /testnet/rpc: account_channels, account_currencies, account_info, account_lines, account_nfts, account_objects, account_offers, account_tx, amm_info, book_changes, book_offers, channel_verify, deposit_authorized, feature, fee, gateway_balances, get_aggregate_price, ledger, ledger_closed, ledger_current, ledger_data, ledger_entry, ledger_header, ledger_index, manifest, mpt_holders, nft_buy_offers, nft_history, nft_info, nft_sell_offers, nfts_by_issuer, noripple_check, owner_info, ping, random, ripple_path_find, server_definitions, server_info, server_state, simulate, transaction_entry, tx, tx_history, tx_reduce_relay, vault_info, version. subscribe, unsubscribe and path_find need a session — use the WebSocket. History (account_tx, tx, tx_history, transaction_entry, nft_history), nft_info, nfts_by_issuer, mpt_holders, ripple_path_find and any read pinned to a ledger older than our node keeps (a few days) are answered by full-history nodes, back to the ledger's start; everything else by our own node"},{"method":"GET","path":"/submit/types","description":"Every transaction type POST /submit relays, each with an example: examples[type] = { json (the reference example, unsigned), txid (a real Mainnet transaction of that type, when the reference names one), summary }","response":"{ success, types[], examples: { [type]: { json, txid, summary } }, pool_size }"},{"method":"GET","path":"/submit/fee","description":"Current network fees","response":"{ success, base_fee, median_fee, minimum_fee, open_ledger_fee, current_ledger, current_queue_size, max_queue_size }"},{"method":"GET","path":"/submit/account/{address}/sequence","description":"Account sequence number","response":"{ success, sequence, ledger_index, balance, ownerCount, flags, regularKey, cached? }"},{"method":"GET","path":"/submit/account/{address}/inbound-tags","description":"Destination-tag usage of recent inbound payments (send-screening advisory; tagsExpected = recent deposits overwhelmingly tagged)","response":"{ success, took, account, sampleSize, tagged, taggedPct, distinctSenders, tagsExpected }"},{"method":"GET","path":"/submit/tx/{hash}","description":"Lookup a submitted XRPL transaction by hash"},{"method":"GET","path":"/submit/ledger-entry/check/{checkId}","description":"Lookup a Check ledger entry by id"}],"nft":[{"method":"GET","path":"/nft","params":{"cid":"collection id","issuer":"address","taxon":"int (NFTokenTaxon; requires issuer)","offset":"int (>=0)","limit":"int (max 200, default 20; response echoes effective limit)","sort":"created|time|rarity_rank|volume|cost.amount","order":"asc|desc"},"description":"List NFTs"},{"method":"GET","path":"/nft/{nftId}","params":{"includeCollection":"bool"},"description":"NFT by NFTokenID (64-char hex)"},{"method":"GET","path":"/nft/{nftId}/offers","description":"Buy/sell offers for NFT"},{"method":"GET","path":"/nft/history","params":{"NFTokenID":"64-char hex","account":"r-address","limit":"int (max 200; up to 50 ownership events past the limit, such as sales and transfers, are included too)","types":"comma-separated: SALE|MINT|BURN|TRANSFER|CREATE_BUY_OFFER|CANCEL_BUY_OFFER|CREATE_SELL_OFFER|CANCEL_SELL_OFFER|ACCEPT_OFFER"},"description":"NFT transaction history"},{"method":"GET","path":"/nft/history/{nftId}","params":{"limit":"int (max 100)","marker":"string"},"description":"Live NFT history"},{"method":"GET","path":"/nft/activity","params":{"cid":"collection id","limit":"int (max 200)"},"description":"Recent NFT activity feed"},{"method":"GET","path":"/nft/transactions/{hash}","description":"NFT transaction by hash"},{"method":"GET","path":"/nft/offers/buy/{nftId}","params":{"limit":"int (max 500)","marker":"string"},"description":"Live buy offers"},{"method":"GET","path":"/nft/offers/sell/{nftId}","params":{"limit":"int (max 500)","marker":"string"},"description":"Live sell offers"},{"method":"GET","path":"/nft/collections","description":"List NFT collections","params":{"sort":"vol24h|totalVol24h|volume|new|created|latest|trendingScore (or any collection field)","order":"asc|desc","interval":"24h|7d|30d (default 24h) — trending window; with sort=trendingScore ranks by the 7d/30d score","limit":"int (max 200, default 20)","offset":"int (>=0)","tag":"string","origin":"string","minOwners":"int (>=0, default 0) — only collections held by at least this many distinct owners","includeGlobalMetrics":"bool","lightweight":"bool — strip each collection to the essential discover/trending fields","skip_metrics":"bool — omit the tags/countries facets"}},{"method":"GET","path":"/nft/collections/resolve","params":{"account":"issuer r-address (alias: issuer)","taxon":"int (NFTokenTaxon)"},"description":"Resolve a collection from its XLS-20 on-chain identity (issuer account + taxon) to the full collection object incl. slug/cid. Every {slug} param on /nft/* endpoints ALSO accepts this identity directly as \"<issuer>:<taxon>\" (e.g. /nft/collections/rLyr...:1234/history)"},{"method":"GET","path":"/nft/collections/{slug}","params":{"includeNFTs":"bool","nftLimit":"int (max 100, default 20)","nftSort":"created|time|rarity_rank|volume|cost.amount","nftOrder":"asc|desc"},"description":"Collection details"},{"method":"GET","path":"/nft/collections/{slug}/nfts","description":"NFTs in collection","params":{"sort":"activity|price-low|price-high|offer-low|offer-high|minted-latest|minted-earliest|rarity-rare|rarity-common|volume-high|volume-low|recent-sale|recent-listed","listed":"true|false|xrp|non-xrp","traits":"type:value (comma-separated)","limit":"int (max 200, default 50)","offset":"int","owner":"r-address","lightweight":"bool"}},{"method":"GET","path":"/nft/collections/{slug}/related","description":"Related projects: other tokens and NFT collections that share the audience of this collection. An edge means wallets hold both and accounts post about both — it is NOT a wallet link and does not assert a shared operator (see /token/review for that).","response":"{ success, took, count (rows in this response), truncated, subject, related[] } — each row: { type: token|collection, md5|collectionId, slug, name, sharedHolders, posters, wallets[] (a sample of the wallets holding both), posts[] (posts naming both), sameProject }"},{"method":"GET","path":"/nft/collections/{slug}/traits","description":"Collection trait breakdown"},{"method":"GET","path":"/nft/collections/{slug}/traders","params":{"sort":"volume7d|all","limit":"int (max 200, default 100)"},"description":"Top traders"},{"method":"GET","path":"/nft/collections/{slug}/orderbook","params":{"limit":"int (max 200, default 50)"},"description":"Collection orderbook"},{"method":"GET","path":"/nft/collections/{slug}/metrics","description":"Collection metrics snapshot (floor + deltas, volumes, sales, owners, listings)"},{"method":"GET","path":"/nft/collections/{slug}/history","params":{"type":"SALE|MINT|BURN|TRANSFER|OFFER|LIST|DELIST|CANCEL","account":"r-address — only events where this account is buyer or seller","limit":"int (max 200, default 20)","offset":"int","since":"unix ms — only events at/after this time","sinceLedger":"ledger index — only events at/after this ledger (sorts by ledger desc)"},"description":"Collection activity history"},{"method":"GET","path":"/nft/collections/{slug}/floor/history","params":{"limit":"int (max 500, default 100)"},"description":"Floor price history"},{"method":"GET","path":"/nft/collections/{slug}/ohlc","params":{"limit":"int (max 1000, default 365; legacy daily mode)","offset":"int","resolution":"5|15|30|60|240|D|W","range":"1D|5D|1M|3M|1Y|ALL","cb":"int candle count (max 2000)","to":"unix ms (with resolution): a page of history, the cb candles that open before it; page back by passing the first candle's time, an empty ohlc means the history ends there","chg":"bool: each row also carries [change, change %] against the bar before","ha":"bool (with resolution): Heikin Ashi candles (TradingView's), warmed up on the bars before the window","ind":"indicators instead of candles (with resolution, not to), as /ohlc/{id}?ind= takes them, on the floor candles","from":"unix ms (with ind): the first bar wanted; default the last cb","tail":"int (with ind): only the last N bars"},"response":"{ success, collection, interval, ohlc: [[time, open, high, low, close, volume, sales, topSale, change?, changePct?]] }; with ind: { success, interval, interval_seconds, vs_currency, t, indicators: [{ id, inputs, offsets, plots, graphics? }] }","description":"Collection OHLC chart data"},{"method":"GET","path":"/nft/collections/{slug}/ohlc/{date}/sales","params":{"date":"YYYY-MM-DD (also accepts unix timestamp with optional interval)","interval":"string (with unix timestamp date)","limit":"int (max 100)","offset":"int","sortBy":"costXRP|time"},"description":"NFTs sold on specific date"},{"method":"GET","path":"/nft/collections/{slug}/sparkline","params":{"period":"7d|30d|365d","lightweight":"bool","maxPoints":"int (2-500, default 20)"},"description":"Collection floor price sparkline"},{"method":"GET","path":"/nft/collections/{slug}/ownership","params":{"limit":"int (max 100, default 20)"},"description":"Ownership distribution: total owners and the top holders, with how many NFTs each holds"},{"method":"GET","path":"/nft/traders/{account}/volume","description":"Trader volume stats"},{"method":"GET","path":"/nft/account/{account}/nfts","params":{"cid":"collection id","limit":"int (max 100; max 50 in search mode)","skip":"int","sortByValue":"bool","search":"string"},"description":"NFTs/collections owned with portfolio value"},{"method":"GET","path":"/nft/account/{account}/offers","params":{"type":"buy|sell","limit":"int (max 200, default 50)","skip":"int"},"description":"NFT offers owned + incoming buy/sell offers (top 500 newest owned NFTs scanned; incomingOffersTruncated flag; fraud/fraudType detection)"},{"method":"GET","path":"/nft/stats/global","description":"Global NFT market stats (same source as /nft/global-metrics)"},{"method":"GET","path":"/nft/global-metrics","description":"Global NFT metrics"},{"method":"GET","path":"/nft/analytics/market","description":"NFT market analytics","params":{"startDate":"YYYY-MM-DD","endDate":"YYYY-MM-DD","period":"24h|7d|30d|all","includeDailyHistory":"bool"}},{"method":"GET","path":"/nft/holders/{slug}","description":"Collection holder list","params":{"offset":"int","limit":"int (max 100)"}},{"method":"GET","path":"/nft/holders/address/{account}","description":"Collections/NFTs held by wallet","params":{"offset":"int","limit":"int (max 100)"}},{"method":"GET","path":"/nft/holders/{slug}/distribution","description":"Holder distribution by tier (whales/orcas/dolphins/fish/shrimp)"},{"method":"GET","path":"/nft/floor/history/{cid}","params":{"limit":"int (max 500, default 100)"},"description":"Floor history by collection ID"},{"method":"GET","path":"/nft/brokers/stats","description":"Broker fee statistics"},{"method":"GET","path":"/nft/analytics/traders","description":"NFT trader leaderboard","params":{"offset":"int","limit":"int (max 100, default 50)","sort":"volume|profit|trades|roi|winRate|combinedProfit|lastTrade|xrpBalance","order":"asc|desc","interval":"24h|7d|30d|all","minVolume":"number","minTrades":"number","minProfit":"number","minROI":"number","address":"substring","marketplace":"string (see /nft/analytics/marketplaces)"}},{"method":"GET","path":"/nft/analytics/trader/{account}","description":"Single NFT trader profile"},{"method":"GET","path":"/nft/analytics/trader/{account}/history","params":{"startDate":"YYYY-MM-DD","endDate":"YYYY-MM-DD","limit":"int (default 365; returns last N days)"},"description":"Trader daily history","notes":"total = pre-slice count"},{"method":"GET","path":"/nft/analytics/trader/{account}/collections","params":{"sort":"volume|profit|roi","order":"asc|desc","limit":"int"},"description":"Trader per-collection breakdown"},{"method":"GET","path":"/nft/analytics/trader/{account}/trades","params":{"offset":"int","limit":"int (max 200, default 50)","type":"SALE|MINT|BURN|TRANSFER|OFFER"},"description":"Trader trade history"},{"method":"GET","path":"/nft/analytics/collection/{slug}/traders","description":"Top traders for collection","params":{"limit":"int (max 200, default 50)","page":"int (1-based, default 1)","sort":"volume|sales|profit|roi|globalVolume|globalProfit|combinedProfit|holdingsCount","order":"asc|desc","interval":"24h|7d|30d|all","address":"substring filter"}},{"method":"GET","path":"/nft/analytics/rotations","description":"NFT collection rotation analytics","params":{"period":"24h|7d|30d (default 7d)","minXrp":"number (default 1)","limit":"int (max 100, default 50)"}},{"method":"GET","path":"/nft/analytics/rotations/{cid}","description":"NFT collection rotations by collection id","params":{"period":"24h|7d|30d (default 24h)","minXrp":"number (default 1)"}},{"method":"GET","path":"/nft/analytics/trader/{account}/rotations","description":"NFT trader rotations history","params":{"period":"24h|7d|30d (default 24h)"}},{"method":"GET","path":"/nft/pin/status/{hash}","description":"IPFS pin status by hash","notes":"hash must be an IPFS CID (Qm… or b…)"},{"method":"GET","path":"/nft/scam","description":"NFT-scam insights snapshot — totals (XRP/token drained, NFTs flagged, scam wallets), monthly timeline, top scammers/victims, tokens drained with XRP values, recent drain feed, and scamIssuersList (flat array of every flagged-issuer address for client-side filtering). For per-NFT checks, use GET /nft/:nftId — it already returns scam + scamType."},{"method":"GET","path":"/nft/collections/{slug}/listings/distribution","description":"Listed-price distribution vs floor (buckets at 1x/1.25x/1.5x/2x/3x/5x/10x floor), floor/median/avg/max"},{"method":"GET","path":"/nft/collections/{slug}/listings/history","params":{"from":"unix ms","to":"unix ms"},"description":"Daily listed-count history merged with floor/volume/sales"},{"method":"GET","path":"/nft/collections/{slug}/holders/history","params":{"from":"unix ms","to":"unix ms"},"description":"Daily holder-count history"},{"method":"GET","path":"/nft/collections/{slug}/mints","params":{"interval":"day|hour","from":"unix ms","to":"unix ms"},"description":"Mint counts over time with cumulative total"},{"method":"GET","path":"/nft/collections/sparkline/batch","params":{"slugs":"comma-separated (max 100)","period":"7d|30d|365d","maxPoints":"int (2-500)"},"description":"Batch floor sparklines for multiple collections"},{"method":"GET","path":"/nft/analytics/marketplaces","description":"Distinct NFT marketplaces with trader counts"},{"method":"POST","path":"/nft/pin","body":"{ NFTokenID? (64-hex), ipfsHash? (CID) }","description":"Pin NFT media to platform IPFS node (resolves hash from NFT URI if only NFTokenID given)","notes":"Admin key. The node pins in the background (status queued); poll /nft/pin/status/{hash}."},{"method":"POST","path":"/nft/collections/{slug}/metadata","auth":"wallet signature (collection account/issuer or admin)","body":"{ description?, twitter?, website? }","description":"Edit collection metadata"}],"accountTxExplain":[{"method":"GET","path":"/account-tx-explain/{account}","description":"Wallet activity analysis and profiling (20 credits)","params":{"limit":"int (200-400, default 200)"},"response":"{ account, accountInfo, period, txCount, stats, analysis: { profile, headline, keyFindings[], riskLevel }, topAddresses[], model, disclaimer, timestamp }"}],"tokenReview":[{"method":"GET","path":"/token/review/{id}","description":"Token risk assessment (score 1-10, lower = safer), and collapseChance: the chance in percent that the token loses 90% of its price or of the XRP in its pools within 60 days (a model graded on what later happened to past tokens), or { inactive: true } for a quiet token (a month old, under 3 trades a day), or null for an Official (tier 1, admin-verified) company token, which also scores 1","response":"{ md5, token, score, riskLevel, collapseChance: { pct, horizonDays } | { inactive: true } | null, riskCount, positiveCount, supplyControl, creatorTokens[], timestamp }"},{"method":"GET","path":"/token/{md5}/related","description":"Related projects: other tokens and NFT collections that share the audience of this token. An edge means wallets hold both and accounts post about both — it is NOT a wallet link and does not assert a shared operator (see /token/review for that).","response":"{ success, took, count (rows in this response), truncated, subject, related[] } — each row: { type: token|collection, md5|collectionId, slug, name, sharedHolders, posters, wallets[] (a sample of the wallets holding both), posts[] (posts naming both), sameProject }"}],"traders":[{"method":"GET","path":"/traders/{account}","description":"Full trader profile","params":{"limit":"int (max 200, default 50)","offset":"int","sortTokensBy":"volume|profit|trades|roi|lastTradeDate","tokenIds":"comma-separated token md5s (filter, skips pagination)"},"response":"{ success, address, rank, totalTokensTraded, holdingValue, tokenPerformance[], recentTokens[], pagination: { totalTokens (servable, capped 500), lifetimeTokens, truncated, limit, offset, hasMore } }"},{"method":"GET","path":"/traders/token-traders/{id}","description":"Top traders for a token","params":{"interval":"24h|7d|30d|all","sortBy":"volume|pnl|trades|bought|sold|roi|wash|lastActive|unrealized|balance","limit":"int (max 500, default 50)","offset":"int","minVolume":"float (filters all-time buyVolume)","search":"address prefix"},"response":"{ success, tokenId, interval, traders[], count (rows in this response), total (matching traders), offset, sortBy, limit }"},{"method":"GET","path":"/traders/rotations","description":"Cross-token trader rotation analytics","params":{"period":"24h|7d|30d (default 24h)","minXrp":"float (default 1)","limit":"int (max 100, default 50)"}},{"method":"GET","path":"/traders/rotations/{tokenId}","description":"Trader rotations for a specific token","params":{"period":"24h|7d|30d (default 24h)","minXrp":"float (default 1)"},"notes":"tokenId must be a raw md5 (no slug resolution)"},{"method":"GET","path":"/traders/{account}/rotations","description":"Per-account token rotation flow (XRP bought/sold per token in window)","params":{"period":"24h|7d|30d (default 24h)","minXrp":"float (default 0.01)"},"response":"{ success, period, account, totalTrades, tokensTraded, rotationCount, tokens[] }"}],"analytics":[{"method":"GET","path":"/token/analytics/token/{id}","description":"Token analytics"},{"method":"GET","path":"/token/analytics/token/{id}/traders","params":{"offset":"int","limit":"int (max 100, default 50)","sort":"volume24h|volume7d|volume30d|volumeTotal|trades24h|tradesTotal|realizedPnl|unrealizedPnl|totalPnl|holding|lastTradeAt|firstTradeAt","address":"filter"},"description":"Top traders for token"},{"method":"GET","path":"/token/analytics/trader/{address}","description":"Trader cumulative stats"},{"method":"GET","path":"/token/analytics/trader/{address}/tokens","params":{"token":"token id"},"description":"Trader token-specific metrics"},{"method":"GET","path":"/token/analytics/traders","description":"All traders with cumulative stats","params":{"offset":"int","limit":"int (max 500, default 50)","sort":"volume24h|volume7d|totalVolume|totalProfit|totalTrades|avgROI|winRate|lastTradeDate|washTradingScore|activityScore (unknown values fall back to volume24h)","order":"asc|desc","period":"24h|7d|30d|all","startDate":"ISO","endDate":"ISO","minVolume":"float (filters volume24h)","minTrades":"int","minProfit":"float","minROI":"float","minTokens":"int","address":"partial match","includeAMM":"bool (default true)","sourceTag":"int","compact":"bool"}},{"method":"GET","path":"/token/analytics/market","description":"Daily market metrics","params":{"startDate":"ISO (required)","endDate":"ISO"}},{"method":"GET","path":"/token/analytics/trader/{address}/history","params":{"startDate":"ISO","endDate":"ISO","page":"int (1-based, default 1; page 1 = most recent window)","limit":"int (max 500, default 100)"},"description":"Trader volume history","notes":"Returns a bare JSON array"},{"method":"GET","path":"/token/analytics/trader/{address}/trades","params":{"startDate":"ISO","endDate":"ISO","page":"int (1-based, default 1; page 1 = most recent window)","limit":"int (max 500, default 100)"},"description":"Trader trade history","notes":"Returns a bare JSON array"},{"method":"GET","path":"/token/analytics/traders/summary","description":"Trader balance snapshots summary"},{"method":"GET","path":"/token/analytics/sources","description":"Distinct trade sourceTags with trader counts and volume totals"}],"tokenFlow":[{"method":"GET","path":"/token/flow/{id}","description":"Creator allocation and distribution analysis (precomputed)","response":"{ success, token, summary, linkedAddresses[], exchangeBreakdown[], graph: { nodes[], edges[] }, processedAt, lastLedger } or { success, pending: true }","notes":"{id} must be a raw md5 (no slug resolution); serves a precomputed document, no query params. summary.creatorAllocated/Delivered/Locked* describe the creator's own cut (escrow / check / issuer payment) and where it went; their percentages are measured against mintSupply, since escrowed tokens sit outside every trustline, and an escrow the creator wrote to itself counts as locked, not as a second allocation. summary.placed* account for everything the creator passed on and sum to totalTransferred exactly: Held, Locked (in an escrow for that wallet), Sold, Pooled, Burned, ToExchange, Returned (back to the creator), plus two residuals that are never merged into the others - Untraced (a hop we do not follow) and Unscanned (a wallet whose history exceeded the page budget)."}],"market":[{"method":"GET","path":"/market/funding","params":{"asset":"XRP|BTC|ETH (default XRP)"},"description":"Perpetual funding rates and open interest on OKX, Binance, Bybit, Hyperliquid, Bitget and Gate: each venue's rate for its own period (intervalHours: Hyperliquid pays every hour) and the same rate over 8 hours (fundingRate8h, comparable across venues), the total open interest, long/short ratios (all accounts on OKX, Binance, Bybit and Gate; top traders by accounts and by position size on OKX and Binance) and taker buy/sell volume over the last full hour (Binance, OKX). Positive funding = longs pay shorts. longShort is the OKX all-accounts ratio, kept for older clients","response":"{ success, asset, venues[] { venue, instrument, fundingRate, intervalHours, fundingRate8h, nextFundingTime, openInterest, openInterestUsd, at }, openInterestTotal { openInterest, openInterestUsd, fundingRate8hWeighted, venues[] }, longShortRatios[] { venue, measure, longShortRatio, longShare?, shortShare?, longAccounts?, shortAccounts?, period, at }, takerVolume[] { venue, hourStart, buyVolume, sellVolume, buySellRatio }, longShort { longShortAccountRatio, at }, note, source }"},{"method":"GET","path":"/market/funding-history","params":{"asset":"XRP|BTC|ETH (default XRP)","days":"int 1-30 (default 7; today included, still open)"},"description":"Funding and open interest over the last days: the funding rates settled per UTC day on Binance, OKX, Bybit and Hyperliquid (summed, with the number of payments), and each venue's daily open-interest readings on Binance, OKX and Bybit at the venue's own timestamps","response":"{ success, asset, days, from, funding[] { date, venues { <venue>: { sum, payments } } }, openInterest { <venue>: [{ at, openInterest, openInterestUsd? }] }, unavailable?, note, source }"},{"method":"GET","path":"/market/liquidations","params":{"asset":"XRP|BTC|ETH (default XRP)"},"description":"Liquidations on perpetuals. OKX (top level): its latest filled liquidation orders (up to 100): longs and shorts liquidated, sizes and USD values, the largest ones. Gate (gate): hourly totals by side over the last 24 hours and 7 days, each UTC day of the week and the three heaviest hours. Streams (streams): every liquidation Binance, Bybit and OKX streamed, collected by xrpl.to since 5 October 2026: totals by venue and side over 24 hours and 7 days, per UTC day, the day's largest, and whether each venue's collector is connected (Binance streams at most one per symbol per second, a floor). A source that does not answer carries unavailable: true","response":"{ success, asset, venue, instrument, contractSize, window { from, to, events }, longsLiquidated { count, size, usd }, shortsLiquidated { count, size, usd }, largest[], gate { venue, instrument, last24h { from, to, hours, longsLiquidated { size, usd }, shortsLiquidated { size, usd } }, last7d { … }, days[] { date, hours, longsLiquidated, shortsLiquidated }, largestHours[] { hourStart, longsLiquidated, shortsLiquidated } }, streams { collectingSince, venues { <venue>: { connected, collectingSince, lastMessageAt, last24h, last7d { longs, shorts { count, size, usd } } } }, days[] { date, venues }, largest24h[] { venue, side, size, price, usd, time }, note }, note, source }"},{"method":"GET","path":"/market/liquidation-levels","params":{"asset":"XRP|BTC|ETH (default XRP)"},"description":"ESTIMATED liquidation levels (a heatmap): where positions opened in the last 7 days on Binance, Bybit and OKX would be liquidated, in 1% price bands up to 25% either side (longs below the price, shorts above), from hourly open-interest changes and an assumed leverage mix stated in the result; levels the price has crossed are dropped. A model, not a reading of anyone's positions","response":"{ success, asset, estimate: true, price, at, window { from, to, hours }, venues[], openInterestShare, assumptions { leverage, maintenanceMarginRate, margin, rangePct }, longs { totalUsd, levels[] { fromPct, toPct, priceLow, priceHigh, usd, cumulativeUsd }, largest[] }, shorts { … }, note, source }"},{"method":"GET","path":"/market/global","description":"The whole crypto market (all venues): total market cap, 24h volume, BTC/ETH/XRP/USDT/USDC dominance, and XRP rank, market cap, volume and all-time high","response":"{ success, totalMarketCapUsd, totalVolume24hUsd, marketCapChange24hPct, dominancePct, xrp { priceUsd, marketCapUsd, rank, volume24hUsd, change24hPct, athUsd, athDate, circulatingSupply, at }, at, source }"},{"method":"GET","path":"/market/tickers","params":{"currency":"3-character code or 40-hex currency (required)","issuer":"r-address (required)"},"description":"An XRPL token's markets on other venues, from CoinGecko: every market it tracks for the token (matched on the token's currency and issuer, never on a ticker symbol), busiest first, each with its last price in USD, 24h volume and whether its exchange is centralized (CoinGecko's own flag; null until looked up). listed false when CoinGecko has no coin for the token","response":"{ success, listed, coinId?, name?, tickers[] { market, centralized, pair, last, lastUsd, volume24hUsd, spreadPct, trust, stale, anomaly, lastTradedAt }, note, source, at? }"},{"method":"GET","path":"/market/korea-premium","description":"XRP's price on Upbit against Binance: Upbit KRW-XRP converted at Upbit's own KRW-USDT, against Binance XRP/USDT, and the premium in percent (positive: XRP costs more in Korea). No bank FX rate involved","response":"{ success, upbit { xrpKrw, usdtKrw, xrpUsdt }, binance { xrpUsdt }, premiumPct, note, source, at }"},{"method":"GET","path":"/market/coinbase-premium","description":"XRP's price on Coinbase against Binance: Coinbase XRP-USD converted at Coinbase's own USDT-USD, against Binance XRP/USDT, and the premium in percent (positive: XRP costs more on Coinbase, where US buyers trade)","response":"{ success, coinbase { xrpUsd, usdtUsd, xrpUsdt }, binance { xrpUsdt }, premiumPct, note, source, at }"},{"method":"GET","path":"/market/xrpl-cex-ranking","description":"XRPL-issued tokens ranked by how many centralized exchanges list them: every XRPL token CoinGecko tracks, with the exchanges named (CoinGecko's own centralized flag) and stale markets marked. Built in the background daily; building:true until the first ranking after a restart","response":"{ builtAt, coinsListed, coinsRead, tokens: [{ coinId, name, issuer, currency, centralizedExchanges, activeOnCentralized, exchanges[], decentralizedMarkets }], note, source }"},{"method":"GET","path":"/market/etf-flows","description":"US spot XRP ETFs from SoSoValue: daily net inflow, value traded, net assets and cumulative net inflow for the last 20 US trading days, the last week's total, each fund's latest day, and an estimate of the XRP held (net assets over XRP's Binance daily close)","response":"{ latest, lastWeek { from, to, tradingDays, netInflowUsd }, funds: [{ ticker, name, exchange, date, netInflowUsd, cumInflowUsd, netAssetsUsd, priceUsd, sponsorFeePct }], days: [{ date, netInflowUsd, netAssetsUsd, cumNetInflowUsd, valueTradedUsd, xrpClose, xrpHeldEstimate }], note, source, at }"},{"method":"GET","path":"/market/predictions","params":{"topic":"fed|crypto|economy (default fed)"},"description":"Active Polymarket prediction markets on a topic, busiest first, with each outcome priced as a probability","response":"{ success, topic, events[] { title, url, endDate, volume24hUsd, markets[] { question, outcomes[] { name, probability }, volume24hUsd } }, note, source, at }"},{"method":"GET","path":"/market/history","params":{"asset":"BTC|ETH|XRP|SOL|BNB|DOGE|ADA|LTC|XLM|HBAR (required)","days":"int 1-365 (default 30)"},"description":"Daily closes of a major crypto asset from Binance spot (USDT, UTC days; the last is today's, still open)","response":"{ success, asset, quote, days, closes[] {date, close}, note, source }"},{"method":"GET","path":"/market/macro","params":{"id":"DFF|DGS10|CPIAUCSL|UNRATE|ICSA|DTWEXBGS|M2SL (optional: one series' history instead)","months":"int 1-24 (default 12, with id)"},"description":"Latest US macro readings from FRED: effective fed funds rate, 10-year Treasury yield, CPI and M2 year over year, unemployment, jobless claims, broad dollar index. With id: every observation of that series over the last months","response":"{ success, series[] { id, name, units, value, date, previous { value, date }, url }, source, note }; with id { success, id, name, units, url, months, from, observations[] { date, value }, source, note }"}],"news":[{"method":"GET","path":"/news","auth":"Developer tier+ API key","params":{"offset":"int (clamped >= 0)","limit":"int (1-100, default 20)","source":"filter (approved sources only)","date":"YYYY-MM-DD (single-day filter)"},"description":"Latest XRP news with sentiment","response":"{ sentiment: { 24h, 7d, 30d, all }, sources[], data[], pagination: { offset, limit, total, totalPages } }"},{"method":"GET","path":"/news/search","auth":"Developer tier+ API key","params":{"q":"string (required)","sort":"'date' (default, newest first) or 'relevance' (stories matching more of the words first)","offset":"int (clamped >= 0)","limit":"int (1-100, default 20)","source":"filter (approved sources only)","date":"YYYY-MM-DD (single-day filter)"},"description":"Search news articles","response":"{ query, sort, sentiment, data[], pagination }"},{"method":"GET","path":"/news/unapproved-sources","auth":"Developer tier+ API key","params":{"offset":"int","limit":"int (1-100, default 20)"},"description":"News sources pending approval"},{"method":"GET","path":"/news/sentiment-chart","auth":"Developer tier+ API key","params":{"days":"int (1-365, default 30)"},"description":"Historical sentiment analysis chart","response":"{ labels[], bullish[], bearish[], neutral[], updated }"}],"mentions":[{"method":"GET","path":"/mentions/search","auth":"Developer tier+ API key","params":{"q":"string (full-text search on tweet content, max 200 chars)","author":"string (username prefix match, case-insensitive)","token":"string (token name, ticker, or md5)","from":"ISO date or unix ms (lower bound on tweetCreatedAt)","to":"ISO date or unix ms (upper bound)","minLikes":"int","minViews":"int","excludeSpam":"bool (default true; pass false to include spam-flagged tweets)","excludeImpostors":"bool (filter out impostor accounts)","sort":"recent|engagement|views|relevance (relevance requires q)","limit":"int (max 100, default 20)","offset":"int (default 0)"},"description":"Agent-friendly search across X/Twitter mentions with full-text + filters. Returns enriched tweets with token name, spam/impostor flags."},{"method":"GET","path":"/mentions/feed","auth":"Developer tier+ API key","params":{"limit":"int (max 100, default 20)","offset":"int (clamped >= 0)","period":"1d|7d|30d|90d|all","author":"string","q":"string (full-text)","excludeSpam":"bool (default true — spam rows removed in-query; pagination and count share the filtered row space; pass false to include)","excludeImpostors":"bool (impostor rows removed in-query; also filters topAuthors)","minViews":"int","minLikes":"int","sort":"recent|engagement|views|likes (default recent)"},"description":"Global social feed with trending tokens, top authors, activity chart, and impostor list"},{"method":"GET","path":"/mentions/{md5}","auth":"Developer tier+ API key","params":{"period":"1d|7d|30d|90d|all","author":"string","limit":"int (max 100, default 20)","offset":"int (clamped >= 0)"},"description":"Per-token mention feed with stats, top mentioners, and activity chart"},{"method":"GET","path":"/mentions/spaces","auth":"Developer tier+ API key","params":{"limit":"int (hosts, max 500, default 200)","offset":"int","spacesLimit":"int (spaces per host, max 100, default 10)"},"description":"X Space hosts with full history and inferred schedule (days/hour UTC). Returns all known space hosts sorted by most recent activity."},{"method":"GET","path":"/mentions/openapi.json","auth":"Developer tier+ API key","description":"OpenAPI 3.0 spec for the mentions endpoints (agent/MCP discoverable)"},{"method":"GET","path":"/mentions/timeline","params":{"limit":"int (max 100, default 50)","offset":"int","period":"1d|7d|30d|90d|all (default 30d)","author":"string","sort":"recent|engagement|views|likes"},"description":"Full timeline of tracked influencer tweets (not just token matches)","response":"{ mentions[], count, period }"},{"method":"GET","path":"/mentions/x-search","params":{"q":"string (required, min 3 chars)","limit":"int (max 20, default 10)"},"description":"Live X/Twitter search via scraper (503 if scraper unavailable)","response":"{ query, count, tweets[] }"},{"method":"GET","path":"/mentions/identify","auth":"API key (your own, not a shared one)","params":{"address":"r-address (a wallet)","token":"token md5 or slug (instead of address)","context":"string (optional, max 300 chars)"},"description":"Who is behind a wallet or a token, searched on the web and X: leads with the sources that support them, not verified on-chain. Needs your own API key (not available anonymously); 20,000 credits per call","response":"{ success, subject, answer, sources[], searches: { web, x }, asOf, note }"},{"method":"GET","path":"/mentions/grok-pulse","params":{"topic":"string (optional)"},"description":"Grok-generated XRPL insight topics with sentiment"},{"method":"GET","path":"/mentions/profile/{username}","description":"Latest X profile snapshot + change history for a handle","response":"{ username, latest, updates, followersDelta, samples }"}],"launch":[{"method":"POST","path":"/launch-token","body":"{ currencyCode, tokenSupply, ammXrpAmount, name, origin, user, antiSnipe?, shield? (default true), imageData?, domain?, description?, telegram?, twitter?, userCheckAmount?, platformRetentionPercent?, ammTradingFee?, bundleRecipients?[], vesting? }","description":"Create new token with AMM","notes":"Every wallet the launch will send to (userAddress, bundleRecipients[].address) is checked on the ledger first. One that cannot receive is refused with 400 \"Wallet cannot receive\" before a session exists: not activated, requires a destination tag, refuses checks, or (vesting) refuses trust lines. The anti-snipe shield is on by default, vesting included; only antiSnipe (the gated window) cannot be combined with vesting."},{"method":"GET","path":"/launch-token/status/{sessionId}","description":"Launch session status"},{"method":"GET","path":"/launch-token/queue-status/{sessionId}","description":"Queue position status"},{"method":"POST","path":"/launch-token/authorize","body":"{ sessionId, userAddress }","description":"Authorize trustline (anti-snipe)"},{"method":"GET","path":"/launch-token/auth-info/{issuer}/{currency}","description":"Token authorization info"},{"method":"GET","path":"/launch-token/check-auth/{issuer}/{currency}/{address}","description":"Check if address is authorized"},{"method":"GET","path":"/launch-token/calculate-funding","params":{"amm_xrp_amount":"float (1-100000000000)","anti_snipe":"bool","token_supply":"float (positive)","user_check_amount":"float (>=0)","bundle_count":"int (max 10)","bundle_percent":"float (max 50 per recipient)","vesting_tranches":"int (0-12)","platform_retention_percent":"float (0-10)"},"description":"Calculate required XRP funding (400 on invalid params)"},{"method":"GET","path":"/launch-token/my-launches","auth":"your own API key (the shared browser key gets 403)","params":{"sessionId":"string (optional)","start":"int (default 0)","limit":"int (default 50, max 100)"},"description":"Launches created with your API key, with total count (a wallet lists its own with /launch-token/my-launches-wallet)"},{"method":"POST","path":"/launch-token/{sessionId}/image","body":"{ imageData }","description":"Upload token image (base64, max 500KB)"},{"method":"GET","path":"/launch-token/my-launches-wallet","auth":"wallet signature","params":{"start":"int (default 0)","limit":"int (default 20, max 100)"},"description":"Token launches by wallet with total count"},{"method":"GET","path":"/launch-token/vesting-by-token/{issuer}/{currency}","description":"Vesting schedule for a launched token (response includes truncated flag when escrow list is incomplete)"}],"presale":[{"method":"GET","path":"/presale","params":{"status":"'all' to include non-open"},"description":"List presales (open by default)"},{"method":"POST","path":"/presale","body":"{ currencyCode, name, origin, tokenSupply, goalXrp, softCapXrp, durationHours, tradingFee, creatorPct?, creatorVestDays?, buyerUnlockPct?, buyerVestDays?, imageData?, description?, twitter?, telegram?, website? }","description":"Create a presale (issuer + sale wallets are provisioned server-side)"},{"method":"POST","path":"/presale/{id}/join","body":"{ address, amountXrp }","description":"Join — checks the wallet can fund the escrow above its reserve, returns the conditional EscrowCreate to sign; an unconfirmed slot may join again, an escrow that already landed is confirmed instead"},{"method":"POST","path":"/presale/{id}/confirm","body":"{ address, txHash }","description":"Confirm a signed escrow contribution"},{"method":"POST","path":"/presale/{id}/finalize","description":"Finalize after deadline/goal: capture escrows, deliver claim Checks, seed AMM, blackhole wallets"},{"method":"GET","path":"/presale/{id}","description":"Presale status (public view: raise, socials, confirmed contributors, vesting; refreshes claim states on success)"},{"method":"GET","path":"/presale/{id}/claim-info","params":{"address":"contributor r-address"},"description":"Per-buyer claim (CheckCash) or refund (EscrowCancel) transaction to sign"},{"method":"GET","path":"/presale/{id}/refund-info","params":{"address":"contributor r-address"},"description":"Alias of claim-info"}],"studio":[{"method":"GET","path":"/studio/config","description":"Network, minter account and prices","response":"{ network: \"TESTNET\", minter_address, price_per_nft_drops, price_per_mb_drops, included_mb_per_file, review }","notes":"Testnet today. review: whether a new drop waits for review before it mints (off today). A creator lets the minter mint for it once: AccountSet with NFTokenMinter = minter_address and SetFlag 10."},{"method":"POST","path":"/studio/mint","body":"{ itemCount, sizes?[] (bytes per art file) }","description":"Order a mint: what to pay, and the tag to pay it with","response":"{ id, amountDrops, destinationTag, walletAddress, status, expiresAt, quote }","notes":"Pay walletAddress amountDrops with DestinationTag, then upload with this orderId. Priced as /studio/quote: send the art sizes, or an upload with a file over included_mb_per_file MB is refused (402)."},{"method":"POST","path":"/studio/quote","body":"{ nfts?, sizes?[] (bytes per art file) }","description":"What NFTs and their art cost, before any order","response":"{ nfts, per_nft_drops, nfts_drops, included_mb_per_file, extra_mb, per_mb_drops, storage_drops, total_drops }","notes":"One price per NFT, art up to included_mb_per_file MB a file included; each MB over it (per file, rounded up) adds per_mb_drops. Orders and drop listing fees are priced the same way. No setup fee, no cut of sales."},{"method":"GET","path":"/studio/order/{id}","description":"An order and whether it is paid"},{"method":"POST","path":"/studio/upload","body":"multipart: files[] or zip, metadata[] (JSON), media[]? (video, audio or a 3D model beside the image of its name), name, collectionName?, description?, taxon?, transferFee?, flags?, orderId? with issuer? (mint now) or publish? (true: store for your own wallet to mint), blind? (true for a hidden drop)","description":"Upload art: with a paid orderId, pinned to IPFS and minted to the issuer, or only pinned (publish) for your own wallet to mint; without, staged for a drop","response":"{ jobId, orderId, status, total }","notes":"Up to 100 MB. media: video (MP4, MOV, WebM), audio (MP3, AAC, WAV, OGG, FLAC, M4A) or a 3D model (binary glTF), told by its bytes (in a ZIP too), each beside the image of its name (1.mp4 with 1.png), which the NFT shows: the metadata names it as animation_url and as video, audio or file; one without such an image, or that is not media, is refused. It is stored, staged and counted in an order's storage like its image. A ZIP's metadata is any .json (one _metadata.json array matched by edition, or 1.json beside 1.png) or a _metadata/ folder. With issuer, each NFT is minted with that issuer (which names minter_address as its NFTokenMinter first) and offered to it for 0: accept the offers. With publish=true nothing is minted: the job's cids are the metadata files, each minted as ipfs://<cid> by your own wallet, which names no minter. Without either, nothing reaches IPFS yet: a paid drop publishes the files (a hidden one at its reveal), and files no drop claims within a day are deleted. A file the node does not pin is left out, and its order stays open for it. The same paid orderId sent again while its job runs gets that jobId back: a retry never mints twice."},{"method":"GET","path":"/studio/upload/job/{id}","description":"Upload progress","response":"{ status, phase, current, total, cids[], results[{ uri, nftoken_id, offer_id, hash, engine_result }], error }","notes":"error says how many files were not added."},{"method":"POST","path":"/studio/upload/raw","body":"multipart: file (PNG, JPEG, GIF or WebP)","description":"Upload one image for a drop page (cover or placeholder)","response":"{ url: \"/uploads/<uuid>.<ext>\" }","notes":"Send https://api.xrpnft.com + url as image or placeholder_image; the image is served at /studio/uploads/<uuid>.<ext>. An image no drop shows within a day is deleted."},{"method":"GET","path":"/studio/uploads/{file}","description":"An uploaded image (cover or placeholder)"},{"method":"GET","path":"/studio/drop","description":"Active drops","response":"{ drops: [{ slug, name, image, creator, price_display, supply, remaining, minted_count, edition, edition_copies, dynamic, claim_code_required, review, boost { d1, d7, d30, all }, blind_mint, reveal_at, revealed, taxon, mint_start, public_start, mint_end, phase, paused }], boosters { d1, d7, d30, all }, boost_min_drops }"},{"method":"GET","path":"/studio/drop/{slug}","description":"One drop: price in drops, supply, remaining and minted_count (supply and remaining null for an edition without a limit), an edition's copies (edition, edition_copies, copy_fee_drops), its NFT flags (flags, dynamic) and the art its creator put on them since (art), the most NFTs a Batch holds (batch_max), whether it mints with a claim code (claim_code_required), what its creator earned (earned: by currency, with NFTs and sales), when its art last changed (art_changed_at), its review (review: approved, pending or rejected, with review_note), its events (events), its boosts (boost, boost_tag), reveal state, schedule (mint_end: when the sale ends) and phase (upcoming, allowlist, public, closed), its phases and what opens next (opens_at), its fingerprint (provenance, provenance_tx), paused or ended, links, royalty (transfer_fee), and the minter and reveal_tag a reveal or a change pays"},{"method":"POST","path":"/studio/drop","body":"{ slug, name, creator, cids[], price_amount (XRP, or the token's amount), price_currency? (XRP, or a token's code), price_issuer? (the token's issuer), blind_mint, reveal_at? (\"manual\" or RFC 3339), placeholder_image?, image?, description?, per_wallet_limit?, per_ip_limit?, referral_bps? (0 to 5000), whitelist?[], mint_start? (RFC 3339), public_start? (RFC 3339, with a whitelist or phases), mint_end? (RFC 3339), phases?[] ({ name, start, end?, price_amount (XRP), per_wallet_limit?, max_mints?, wallets?[], wallet_limits? { address: n }, holders? { kind: \"nft\", issuer, taxon?, burn? } or { kind: \"token\", currency, issuer, min } }), raffle_until? and claim_until? (RFC 3339, fair mode), links? { x, discord, website }, taxon?, transfer_fee?, flags? (tfBurnable 1, tfOnlyXRP 2, tfTransferable 8, tfMutable 16), edition? (true: cids holds one artwork, sold as numbered copies), edition_copies? (0 or none: no limit), claim_code? (4 to 64 characters), fee_payment_hash }","description":"Publish a drop of uploaded NFTs","notes":"The listing fee is paid first: without fee_payment_hash the answer is 402 with fee_drops, minter_address and its quote (per NFT, plus staged art over the included MB a file). Its cids are files uploaded here: each NFT's files go on IPFS once it is minted (a hidden drop's once revealed), so the art of an NFT not sold yet is never public: a buyer shown its URI before signing (a Batch) cannot look it up and choose. Its fingerprint goes on the ledger when it is made (GET /studio/drop/{slug}/provenance). slug: up to 64 of a-z, 0-9 and -. Schedule: nobody mints before mint_start; with a whitelist, only it until public_start (without one, the whitelist for good), then everyone. mint_end: the sale ends (phase closed): from it on nobody starts a purchase, phases included, but a payment made before it still mints (POST /studio/drop/{slug}/mint) and a Batch prepared before it can still be signed; it comes after mint_start, public_start, every phase's start and claim_until, and is in the future. Fair mode (raffle_until with claim_until, no whitelist or schedule beside them): wallets enter a raffle until raffle_until (POST /studio/drop/{slug}/enter), its winners mint until claim_until, then everyone. per_ip_limit: at most this many NFTs minted from one IP address (an IPv6 /64 counts as one; mints and Batches over it answer 429): it slows scripts, not determined bots, and people sharing a connection count as one. Phases (phases, with public_start for the public sale after them, or none: the phases alone; no whitelist, mint_start or raffle beside them): each has a name, a start and an optional end, a price in XRP, a limit per wallet and a cap (0: none), and is open to its wallets (up to 10,000) and/or the holders of an NFT collection (issuer, taxon; each NFT held mints once there, so passing one on mints nothing more) or of a token (currency, issuer, at least min, read as they mint). Phases may overlap: a wallet mints in the cheapest one open to it with room left, which GET /studio/drop/{slug}/wallet/{wallet} names. The creator mints its own drop free (a payment to itself is refused by the ledger). Priced in a token (price_currency and price_issuer): the creator must issue it or hold a trust line for it, and its issuer must charge no transfer fee; price_amount and each phase's price are amounts of it (up to 15 significant digits), and it is bought in one transaction only (POST /studio/drop/{slug}/batch), the buyer holding enough of it. referral_bps: the share of each purchase, in basis points (up to 5,000: 50%), paid to whoever shared the link the buyer came by, in the purchase's own Batch; a sharer that cannot take the payment (not on the ledger, deposit authorization or a required destination tag, no trust line for the token) gets none, and the creator is paid all. Burn to mint: an NFT collection's holders phase with burn: true; each mint burns one of the holder's NFTs of it, in the same Batch, so none is burned without its new NFT (at most 2 a Batch; not by POST /studio/drop/{slug}/mint). links: https addresses, x on x.com, discord on discord.gg. An edition (edition: true) sells its one artwork as numbered copies, numbered in the order they are taken (a copy given back is sold again), up to edition_copies (0: no limit; supply and remaining are then null and minted_count counts up); it is never hidden and has no raffle. It pays no listing fee: each copy's buyer pays copy_fee_drops (the price per NFT when it was made) to minter_address in its purchase, so it is bought in one transaction only (POST /studio/drop/{slug}/batch). claim_code: only who sends it (POST /studio/drop/{slug}/mint or .../batch, claim_code) mints, its creator excepted; it is kept as a hash with the drop's slug and never shown (claim_code_required). wallet_limits: a listed wallet's own limit in its phase (0: none), in place of the phase's per_wallet_limit, as an allowlist CSV of address,mints gives it. flags: tfBurnable 1, tfOnlyXRP 2 (resold for XRP only), tfTransferable 8 and tfMutable 16 (dynamic: its creator can put new art on its NFTs later, manage art) only."},{"method":"POST","path":"/studio/drop/{slug}/mint","body":"{ wallet, quantity, payment_hash?, use_balance?, async?, claim_code? }","description":"Mint from a drop","response":"{ results[{ nftoken_id, offer_id, uri }], delivered, credit_drops, balance_drops, remaining }","notes":"A drop waiting for review mints nothing (403, \"Minting opens once this drop passes review\"), nor one refused (403, with why), here or in a Batch. A drop priced in a token is not minted here, nor an edition (each copy pays its fee in the purchase): buy it in one transaction (POST /studio/drop/{slug}/batch). A payment made inside a Batch is refused here, as wherever a payment is checked: it paid for what that Batch settled. After the sale's end (mint_end) a payment made before it still mints, as it would have then; one made after it mints nothing (403) and stays on the wallet's balance for the creator to refund. Every payment to the creator counts toward this wallet's balance on the drop, in full and once, and a mint spends from that balance: pay short and the answer is 402 with balance_drops and due_drops (the NFTs stay held: pay the rest, then send that payment_hash); pay more and the rest stays for the next mint; use_balance: true mints from it with no new payment. A price check (no payment_hash, no use_balance) holds what it prices for 3 minutes and answers 402 with total_drops and due_drops (total less the balance), or 403 saying why the wallet cannot mint now (not open yet, the whitelist's turn, paused); it holds nothing for a wallet that is not on the ledger or cannot pay due_drops. Asking again for another quantity replaces the hold. A payment hash counts once whatever its letter case. Past the wallet's limit (429) or after the last NFT (410) a payment stays on the balance for the creator to refund. A payment sent to minter_address by mistake (no destination tag) is refused and sent back. Each NFT is offered to wallet for 0: accept the offers; the price of one not delivered goes back on the balance. async: true answers 202 with a jobId (GET /studio/upload/job/{id})."},{"method":"GET","path":"/studio/drop/{slug}/provenance","description":"What a drop sells, fixed before anyone bought","response":"{ slug, provenance, provenance_tx, rule, cids, minted_nfts, art }","notes":"The drop's fingerprint (provenance): the SHA-256, in hex, of its CIDs in order, joined by a newline. The minter writes it on the ledger when the drop is made: an AccountSet of minter_address that changes nothing, whose memo (type xrpnft/provenance) reads \"<slug> <provenance>\" (provenance_tx, once validated). Each CID names its file's content, so anyone can check from the ledger that what each NFT shows was fixed before the sale (minted_nfts: CID to NFTokenID). The files of NFTs not sold yet stay off IPFS: their CIDs show nothing. An edition's copies are keyed \"<cid>#<n>\" in minted_nfts. art: the new art a dynamic drop's creator has put on its NFTs since (by NFT key, \"*\" for every copy of an edition)."},{"method":"GET","path":"/studio/drop/{slug}/wallet/{wallet}","description":"Where a wallet stands on a drop, and what a mint now would get","response":"{ slug, wallet, balance_drops, minted, phases: [{ id, name, open, eligible, minted, can_mint, price_drops }], offer: { phase_id, phase, price_drops, can_mint, fee_drops, batch_max } or { error } }","notes":"Its balance on the drop (balance_drops), what it minted, how it stands in each phase (open, eligible: on its list or holding what it asks; what it minted there; how many more it may; its price for this wallet), and the offer a mint now would get: the phase (or null for the public sale), the price of each NFT (price_drops in drops, or price_value in the drop's token), and how many more (can_mint; null: no limit), or the error saying why it may not mint now. Holders are read from the ledger as asked. fee_drops: what each copy of an edition costs besides its price (0 otherwise); batch_max: how many NFTs one Batch holds for this wallet (fewer when an edition's fee or a sharer's cut is paid beside the price)."},{"method":"GET","path":"/studio/drop/{slug}/balance/{wallet}","description":"What a wallet has left to spend on a drop","response":"{ slug, wallet, balance_drops }","notes":"What this wallet has paid on the drop and not spent, in drops: short, split or extra payments, a payment past its limit or after the last NFT, and the price of an NFT a mint did not deliver."},{"method":"GET","path":"/studio/drop/{slug}/balances","description":"What a drop owes its buyers","response":"{ slug, total_drops, wallets[{ wallet, drops }] }","notes":"Every wallet with XRP left on the drop, most first: what the creator owes back (the creator holds that XRP). Refund one with a payment from the creator to that wallet, then POST /studio/drop/{slug}/refund."},{"method":"POST","path":"/studio/drop/{slug}/refund","body":"{ payment_hash }","description":"Record the creator's refund of a buyer's balance","response":"{ wallet, refunded_drops, balance_drops }","notes":"The creator's refund: a validated XRP payment from the drop's creator to a wallet with a balance on it, made after the drop existed, presented once (any letter case). It takes what it paid, up to the balance, off that wallet's balance. 403 from anyone but the creator, 400 for a wallet owed nothing, 409 once used."},{"method":"POST","path":"/studio/drop/{slug}/enter","body":"{ wallet, payment_hash? }","description":"Enter a drop's raffle (fair mode)","response":"{ entered, entries, raffle_until }","notes":"Fair mode: enter a drop's raffle while it takes entries (phase raffle). Only a wallet that existed before the drop was made, and that can spend what a winner pays (price x allocation), may enter, once. It proves the wallet is its own with a payment of 1 drop to minter_address carrying the drop's entry_tag; asked without payment_hash, the answer is 402 with minter_address and entry_tag, after the other checks, so nobody pays to be refused."},{"method":"POST","path":"/studio/drop/{slug}/boost","body":"{ wallet, payment_hash? }","description":"Boost a drop: pay to promote it on the drops page","response":"{ slug, boosted_drops, wallet_drops, rank, boost }","notes":"Anyone, any drop still live (xrp.cafe's boosts). A boost is a payment of at least min_drops (1 XRP) from the wallet to minter_address with DestinationTag = the drop's boost_tag (GET /studio/drop/{slug}), made after the drop existed; asked without payment_hash, the answer is 402 with minter_address, boost_tag and min_drops. A payment counts once and is not refunded. Its drops are the boost's points: GET /studio/drop/{slug} shows boost { d1, d7, d30, all (drops in the last day, week, month and ever), boosters (its top 10, most first), min_drops, largest_drops (the largest boost anyone has made) }, and GET /studio/drop gives each drop its boost by window and the top boosters of every drop by window (boosters { d1, d7, d30, all }), as the drops page ranks them."},{"method":"GET","path":"/studio/drop/{slug}/raffle","description":"A drop's raffle: entrants, draw and winners","response":"{ phase, raffle_until, claim_until, allocation, entry_tag, entries, entrants[], drawn, seed_ledger, seed_hash, winners[], rule }","notes":"A drop's raffle: phase (raffle, claim, public), raffle_until, claim_until, allocation, entry_tag, the entrants and, once drawn, seed_ledger, seed_hash and winners. The seed is the hash of the first validated ledger closed at or after raffle_until; entrants are ordered by SHA-256 of seed_hash (uppercase hex) followed by the address, lowest first, and the first floor(supply / allocation) win: anyone can redo the draw. Winners mint their allocation until claim_until; then what is left is for everyone."},{"method":"POST","path":"/studio/drop/{slug}/batch","body":"{ wallet, quantity (1 to 3), referrer?, claim_code? }","description":"Prepare a purchase settled in one Batch (pay, mint, accept)","response":"{ batch_id, batch, quantity, price_drops, total_drops, price_value, total_value, currency, issuer, phase, referral, burn, fee_drops, total_fee_drops, copies, sign_within_seconds }","notes":"An edition adds the buyer's payment of its copies' fee to minter_address (total_fee_drops, tagged for the drop) and names the copies' numbers (copies); a Batch holds 8 transactions, so with a price, a sharer's cut and that fee 2 NFTs fit (batch_max on the wallet's offer). A burn-to-mint phase adds the buyer's NFTokenBurn of each NFT it gives up (burn: their NFTokenIDs), at most 2 a Batch. With referrer (the wallet that shared the link, never the buyer or the creator) on a drop that gives a share (referral_bps), the price is two payments: the sharer's cut, rounded down, and the rest to the creator (referral: { referrer, bps, cut }). A purchase settled on the ledger in one all-or-nothing Batch (XLS-56): the buyer's payment of the price to the creator (XRP in drops, or the drop's token: an Amount of its currency, issuer and value), the minter's NFTokenMint of each NFT offered to the buyer for 0, and the buyer's NFTokenAcceptOffer of each offer, which is named before it exists (SHA-512Half of 'q', the minter's AccountID and the mint's Ticket). Same checks as a mint (phase, allowlist, raffle winners, limit, funds); 1 to 3 NFTs a Batch (8 inner transactions at most). Check what it asks you to sign, co-sign it with xrpl.js signMultiBatch(wallet, batch, { batchAccount: wallet }) and send the BatchSigner to /studio/drop/{slug}/batch/{batch_id}/sign within sign_within_seconds, or its NFTs go back on sale. The ledger then applies all of it or none of it: a payment never goes without its NFTs."},{"method":"POST","path":"/studio/drop/{slug}/batch/{id}/sign","body":"{ batch_signer }","description":"Submit a prepared Batch with the buyer's co-signature","response":"{ jobId, quantity }","notes":"Submits a prepared Batch with the buyer's co-signature ({ Account, SigningPubKey, TxnSignature }), once, and answers 202 with a jobId (GET /studio/upload/job/{id}): each NFT's result, minted and already in the buyer's wallet, or not minted and nothing paid."},{"method":"POST","path":"/studio/drop/{slug}/reveal","body":"{ payment_hash }","description":"Reveal a hidden drop (manual reveal)","notes":"The proof is a payment from the creator to minter_address with DestinationTag = the drop's reveal_tag. It publishes the files of the NFTs minted so far to IPFS (the others as they are minted); if any fails the drop stays hidden and the same payment_hash can be sent again."},{"method":"POST","path":"/studio/drop/{slug}/manage","body":"{ changes (a JSON object as a string: paused, ended, price_amount, per_wallet_limit, per_ip_limit, referral_bps, phases[] (a phase sent with its id keeps it, and without wallets keeps its list), whitelist[], mint_start, public_start, mint_end, description, image, links, edition_copies (an edition's: 0 for no limit, or at least what is taken), art ({ cid, nft? }: new art for a dynamic drop's NFTs), claim_code (a new one, or null: none), events[] (the whole list)), payment_hash? }","description":"Change a live drop (its creator): pause, resume, end, price, limits, whitelist, schedule, description, cover, links","notes":"mint_end moves the sale's end (into the future only: to stop at once, pause or end the drop) or takes it away (null); a sale that has ended opens again when its end moves later. Without payment_hash: 402 with minter_address, reveal_tag and invoice_id. The proof is a payment of at least 1 drop from the creator to minter_address with DestinationTag = reveal_tag and InvoiceID = invoice_id (SHA-256 of \"<slug>:<changes>\"), so it carries exactly these changes; used once. A change that would be refused is refused before any proof. ended: true ends the drop for good. events: up to 20, each { id? (kept when sent back), title (up to 100 characters), starts_at (RFC 3339), kind (online or in_person), location?, url? (https), image? (uploaded here: POST /studio/upload/raw), description?, expires_at? (RFC 3339, in the future: the event leaves the page then; null: when removed) }; GET /studio/drop/{slug} shows those not expired, soonest first. A phase sent back without wallets keeps its list and their own limits (wallet_limits). art: a new metadata file uploaded here (POST /studio/upload) for a dynamic drop's NFTs (minted with tfMutable): every copy of an edition (no nft; its next copies mint with it), or one NFT of a collection (nft: its NFTokenID). The answer's art_job (GET /studio/upload/job/{id}) follows the minter publishing it and putting it on each NFT minted so far, wherever it is now (NFTokenModify, as the issuer's authorized minter); an NFT it could not change is named in the job's error, and can be asked again."}],"xaman":[{"method":"POST","path":"/xaman/payload","auth":"xrpl.to platform key (our own surfaces only; 403 otherwise)","body":"{ txjson: { TransactionType, ... }, options?: { submit?, expire? (min, 1-60), return_url?: { web?, app? }, multisign?, pathfinding? }, custom_meta?: { identifier?, instruction? } }","description":"Create a Xaman sign request — returns { uuid, next.always (open in Xaman), refs.qr_png, refs.websocket_status }. xrpl.to surfaces only (platform key); user_token is refused"},{"method":"GET","path":"/xaman/payload/{uuid}","description":"Sign request status (live, never cached): meta.signed/expired/cancelled, response.account (SignIn answer), response.txid (submitted transaction)"},{"method":"DELETE","path":"/xaman/payload/{uuid}","auth":"xrpl.to platform key (our own surfaces only; 403 otherwise)","description":"Cancel an unsigned sign request (xrpl.to surfaces only)"}],"referral":[{"method":"GET","path":"/referral/{address}","description":"Get soldier profile"},{"method":"GET","path":"/referral/{address}/stats","description":"Badge progress and tier stats"},{"method":"GET","path":"/referral/leaderboard/{type}","params":{"type":"recruits|streak|season|whales","limit":"int (default 100, max 500)","offset":"int (default 0)"},"description":"Leaderboard"},{"method":"GET","path":"/referral/meta/tiers","description":"Tier definitions"},{"method":"GET","path":"/referral/meta/badges","description":"Badge definitions"},{"method":"GET","path":"/referral/meta/missions","description":"Mission quest chains"},{"method":"GET","path":"/referral/meta/season","description":"Seasonal leaderboard rewards"},{"method":"GET","path":"/referral/{address}/earnings","description":"Referral earnings breakdown for an address"},{"method":"GET","path":"/referral/{address}/fees","params":{"limit":"int (default 50, max 100)","offset":"int (default 0)"},"description":"Referral fee history; response includes asReferrerTotal/asRecruitTotal for paging"},{"method":"GET","path":"/referral/resolve/{code}","description":"Resolve a referral code to validity/owner"}],"promotion":[{"method":"GET","path":"/promotion/pool/{md5}","description":"Get promotion pool info (status, amounts, expiry)"},{"method":"GET","path":"/promotion/reward/{md5}/{account}","description":"Get reward status for a token"},{"method":"GET","path":"/promotion/leaderboard/{md5}","params":{"offset":"int (default 0)","limit":"int (default 20, max 1000)"},"description":"Per-token promoter leaderboard"},{"method":"GET","path":"/promotion/profile/{account}","description":"Global promotion stats"},{"method":"GET","path":"/promotion/tiers","description":"Promoter tier metadata"},{"method":"POST","path":"/promotion/claim","body":"{ md5, account }","description":"Claim earned promotion rewards (paid in XRP)"}],"bridge":[{"method":"GET","path":"/bridge/currencies","description":"List available exchange currencies"},{"method":"GET","path":"/bridge/estimate","params":{"fromCurrency":"string","toCurrency":"string","fromAmount":"string","fromNetwork":"string","toNetwork":"string"},"description":"Get exchange rate estimate"},{"method":"GET","path":"/bridge/min-amount","params":{"fromCurrency":"string","toCurrency":"string"},"description":"Get minimum exchange amount"},{"method":"POST","path":"/bridge/create","body":"{ fromCurrency, toCurrency, fromAmount, address, refundAddress? }","description":"Create exchange"},{"method":"GET","path":"/bridge/status","params":{"id":"string"},"description":"Get exchange status"},{"method":"GET","path":"/bridge/validate-address","params":{"currency":"string","address":"string"},"description":"Validate destination address"}],"verify":[{"method":"GET","path":"/verify/pricing","description":"Verification tier pricing"},{"method":"GET","path":"/verify/stripe/status/{sessionId}","params":{"id":"string (required — token md5 or collection id)"},"description":"Check Stripe verification payment status; fulfills if webhook was missed"},{"method":"POST","path":"/verify/request","body":"{ type, id, tier, account }","description":"Create XRP invoice for token/collection verification"},{"method":"POST","path":"/verify/confirm","body":"{ invoiceId | txHash }","description":"Confirm verification payment"},{"method":"POST","path":"/verify/stripe/checkout","body":"{ type, id, tier, account }","description":"Stripe checkout for verification"}],"tweetVerify":[{"method":"GET","path":"/tweet/token/{md5}","params":{"limit":"int (max 1000, default 20)","offset":"int (clamped >= 0)"},"description":"Tweet verifications for a token (md5) or NFT collection (slug)"},{"method":"GET","path":"/tweet/account/{account}","params":{"limit":"int (max 1000, default 20)","offset":"int (clamped >= 0)"},"description":"Tweet verifications by account"},{"method":"POST","path":"/tweet/verify","auth":"wallet signature","body":"{ md5 (token md5 or NFT slug), tweetUrl, account, type?: token|nft }","description":"Verify a promotional tweet and credit the wallet. One verification per wallet every 2 hours, up to 6 tokens per X account per UTC day, and a tweet earns from one pool."}],"boost":[{"method":"GET","path":"/boost/quote/{md5}","params":{"rank":"int","termHours":"int, a multiple of 12: also prices this length"},"description":"Get quote for boosting a token"},{"method":"POST","path":"/boost/purchase","body":"{ md5, packId, payerAddress, bidUsd?, termHours?, ad?: { text, cta?, url? } }","description":"Create boost payment request. `termHours` is 12 (default) or any multiple of 12 up to 8760, a year; the bid is per 12 hours and ranks the board, the total is the bid once per 12 hours (the quote prints each). At most 8 of the 10 slots can be on long runs: past that a longer run answers 409. A token that holds a slot adds the time to what is left: `packId: \"extend\"` keeps its bid, a lower bid is refused. `ad` is the Top spot's ad line: one sentence (max 70 chars) and a button word (max 14), linking only to the token's own website"},{"method":"GET","path":"/boost/verify/{invoiceId}","description":"Verify payment and apply boost"},{"method":"POST","path":"/boost/stripe","body":"{ md5, packId, payerAddress, bidUsd?, termHours?, ad? }","description":"Create Stripe checkout for boost (`termHours` and `ad` as in POST /boost/purchase)"},{"method":"GET","path":"/boost/stripe/{sessionId}","params":{"wallet":"r-address (required, must match invoice payer)"},"description":"Check Stripe boost payment status"},{"method":"GET","path":"/boost/active","auth":"X-Admin-Key","description":"Currently active token boosts (admin only)"},{"method":"GET","path":"/boost/nft/quote/{slug}","params":{"termHours":"int, a multiple of 12: also prices this length"},"description":"Quote price for boosting an NFT collection (slug also accepts \"<issuer>:<taxon>\")"},{"method":"POST","path":"/boost/nft/purchase","body":"{ slug, packId, payerAddress, bidUsd?, termHours?, ad? }","description":"Initiate XRP-paid NFT collection boost (`termHours` and `ad` as in POST /boost/purchase, linking only to the collection's own website)"},{"method":"GET","path":"/boost/nft/verify/{invoiceId}","description":"Verify NFT boost payment"},{"method":"POST","path":"/boost/nft/stripe","body":"{ slug, packId, payerAddress, bidUsd?, termHours?, ad? }","description":"Initiate Stripe-paid NFT collection boost (`termHours` and `ad` as in POST /boost/nft/purchase)"},{"method":"GET","path":"/boost/nft/stripe/{sessionId}","params":{"wallet":"r-address (required, must match invoice payer)"},"description":"Check Stripe checkout status for NFT boost"}],"scams":[{"method":"GET","path":"/scams/addresses","params":{"category":"wallet_drainer|dust_spam|phishing|impersonation|fake_airdrop|other (optional)"},"description":"THE canonical scam blocklist: top-level addresses[] is the deduped union of every blocked account (drainers, dust/token spam, phishing, impersonation, fake airdrops, NFT-scam issuers). Check a Destination/counterparty against it BEFORE a user signs. Real-time, never cached (Cache-Control: no-store) — poll every ~5s. Per-category sections (nftPhishing, paymentDrain) are additive metadata and may overlap; addresses is deduped."},{"method":"GET","path":"/scams","description":"Full scam report with abuse statistics"},{"method":"GET","path":"/scams/tokens","params":{"issuer":"address (REQUIRED)","page":"int (default 1)","limit":"int (default 50, max 100)"},"description":"Tokens issued by one named issuer. issuer is required — this endpoint lists an issuer's tokens, it does not classify them. For a scam verdict use /scams/addresses or /scams/check/{account}."},{"method":"GET","path":"/scams/domains","description":"Known scam domains with registrar info"},{"method":"GET","path":"/scams/nft","description":"NFT scam accounts and recent sales"},{"method":"GET","path":"/scams/check/{account}","description":"Check if account is a known scam. is_scam is true ONLY for accounts on the verified blocklist or confirmed scam NFT issuers — never inferred from an account's token names or token count. Also returns first_seen/age_seconds/new_account (advisory counterparty freshness, independent of is_scam)"},{"method":"GET","path":"/scams/report-scam","params":{"page":"int (default 1)","limit":"int (default 50, max 100)","status":"pending|reviewed|confirmed|dismissed"},"description":"List community scam reports"},{"method":"GET","path":"/scams/report/{domain}","description":"Confirmations for a reported domain"},{"method":"POST","path":"/scams/report","body":"{ domain, ... }","description":"Report a scam domain"}],"creatorActivity":[{"method":"GET","path":"/creator-activity/{id}","description":"Token creator trading activity","params":{"id":"md5 or creator r-address","since":"unix ms — return events strictly newer (exclusive)","before":"unix ms — return events strictly older (exclusive cursor)","limit":"int (max 200)","side":"buy|sell","type":"event type","stream":"bool"}},{"method":"GET","path":"/creator-activity/{id}/receiver/{address}","description":"Creator activity filtered by recipient address"},{"method":"GET","path":"/whale-activity/feed","description":"Global whale tape — the newest notable trades across ALL tokens. `tokens` maps each event md5 to its display identity (name, user, slug, verified)","params":{"limit":"int (default 30, max 100)","since":"unix ms — return events strictly newer (exclusive)","minXrp":"float — minimum XRP size","side":"buy|sell"},"response":"{ success, took, count, events[], tokens{} }"},{"method":"GET","path":"/whale-activity/{id}","description":"Whale social-proof trade events — top holders of major tokens, smart money, named accounts, whale-sized and fresh-wallet trades on this token","params":{"id":"md5 or trader r-address","since":"unix ms — return events strictly newer (exclusive)","limit":"int (max 200)"}}],"platformStats":[{"method":"GET","path":"/platform-stats","params":{"period":"24h|7d|30d (default: 24h)"},"description":"Aggregated trading stats grouped by platform key (numeric SourceTag, or canonical name for memo-identified platforms like SPYDEX)","response":"{ success, took, period, totals, platforms[]: { sourceTag, name, slug, txCount, uaw, uniqueTraders, volume, buyVolumeXrp, sellVolumeXrp, pctVolume, pctUAW, pctTx, ... } }"},{"method":"GET","path":"/platform-stats/detail/{sourceTag}","params":{"sourceTag":"numeric tag or platform slug","period":"24h|7d|30d (default: 7d)","limit":"int (default 50, clamped 1-200)"},"description":"Top tokens traded via a single platform; summary covers ALL tokens for the platform/period, not just returned rows"},{"method":"GET","path":"/platform-stats/hourly/{sourceTag}","params":{"sourceTag":"numeric tag or platform slug","period":"24h|7d|30d (default: 24h)"},"description":"Hourly breakdown for a single platform"}],"platformReport":[{"method":"GET","path":"/platform-report","params":{"date":"YYYY-MM-DD (default: today)"},"description":"Full daily platform report","notes":"404 if no report exists for the date (today's report generates at 00:05 UTC)"},{"method":"GET","path":"/platform-report/dates","params":{"offset":"int (default 0)","limit":"int (default 90, max 365)"},"description":"Daily report dates with metadata, newest first","response":"[{ date, generatedAt, durationMs, errorCount }]"}],"websocket":[{"endpoint":"wss://api.xrpl.to/ws/sync/","description":"Multi-token market sync (handshake first, token data via updates)","messages":"subscribe { tokens: [md5,...] } OR { filter: { minVolume?, tags?, topN? (max 500) } }, optionally + { lightweight: true (strip tokens to the ~30 tokenFields essentials), skip_metrics: true (omit exch/H24/global from updates), snapshot: false (skip the initial snapshot frame) }; addTokens { tokens }, removeTokens { tokens }, unsubscribe, getSubscriptions, checkSeq { lastSeq }, resync, auth { apiKey }","response":"initial: { res, initial: true, connectionInfo, tier, session, total, exch, H24, global, tags, tokens: [] }. subscribe ack: { type: \"subscribed\", mode, ... } — in tokens mode its tokens[] is the ECHOED md5 list, not token data. Right after the ack (both modes) comes a snapshot frame { res, snapshot: true, seq, time, count, tokens[] } with the current state of the subscribed set (cap 100 tokens; same lightweight shaping as updates; suppress with snapshot: false). updates: { res, seq, prevSeq, time, session, total, exch, H24, global, tokens[] } — updates carry NO type field; identify them by seq/prevSeq (also your gap detector); tokens[] may be EMPTY (heartbeat/envelope tick, every ~10th seq during quiet periods). lightweight/skip_metrics shrink updates per the subscribe flags"},{"endpoint":"wss://api.xrpl.to/ws/token/{md5}","description":"Single token real-time updates","query":{"fields":"ticker|price|trading|full|all or comma-separated field list","delta":"true|1 — only changed fields in updates"},"messages":"setFields { fields }, setDelta { enabled: bool }, resync, auth { apiKey }","response":"{ res, type: \"initial\", seq, time, fieldPreset, fieldCount, deltaMode, exch, H24, global, token }"},{"endpoint":"wss://api.xrpl.to/ws/ohlc/{md5}","description":"OHLC candlestick streaming","query":{"interval":"1m|5m|15m|30m|1h|2h|4h|1d|1w|1M (default: 5m; 1w from Monday 00:00 UTC, 1M the calendar month)","vs_currency":"XRP|USD|EUR|JPY|CNH (default: USD)","snapshot":"0 to skip the initial frame (default: 1)"},"response":"initial: { type, md5, interval, vs_currency, count, ohlc: [[time, o, h, l, c, volume]] } — last 100 intervals (omitted with snapshot=0); updates: { e: \"kline\", E, s, k: { t, T, i, o, h, l, c, v, x, ch, cp (the change against the close before, and in %) } }","notes":"Max 10 subscriptions per connection. Candles join as in GET /ohlc: a kline's o (and every initial row's o but the first) is the previous candle's close, not the interval's first trade; prices keep 9 significant digits"},{"endpoint":"wss://api.xrpl.to/ws/history/{md5}","description":"Trade history streaming","query":{"limit":"int 1-100 (default: 20, initial snapshot rows)","type":"trades|liquidity|all (default: trades)","pair_type":"xrp — only fills with an XRP side"},"response":"initial: { type, md5, count, trades[] }; updates: { e: \"trades\", E, s, trades[] } (filters apply per-socket)"},{"endpoint":"wss://api.xrpl.to/ws/holders/{id}","description":"Token holder richlist streaming (id = token md5)","query":{"limit":"int 1-100 (default: 50) — honored per-socket in snapshot and broadcasts"},"response":"initial: { type, md5, total, summary: { top10Hold, top20Hold, top50Hold, top100Hold, lockedPct }, holders[] }; updates: { e: \"update\", E, s, total, holders[] }"},{"endpoint":"wss://api.xrpl.to/ws/orderbook","description":"DEX orderbook depth streaming","query":{"base_currency":"currency code/hex or XRP (required)","base_issuer":"issuer address (omit for XRP)","quote_currency":"currency code/hex or XRP (required)","quote_issuer":"issuer address (omit for XRP)","limit":"int 1-100 (default: 20, depth per side)"},"response":"initial: { type: \"snapshot\", pair, bids[], asks[], ledger }; updates: { e: \"depth\", E, s, b[], a[], l }","notes":"Params are base_currency/quote_currency — the REST-style base={md5} form is NOT supported and closes the socket (4020)"},{"endpoint":"wss://api.xrpl.to/ws/ledger","description":"Ledger close stream","response":"initial: last closed ledger; then { e: \"ledger\", E, ledger_index, ledger_hash, ledger_time, close_time, txn_count, validated, reserve_base, reserve_inc, fee_base, fee_ref } per close"},{"endpoint":"wss://api.xrpl.to/ws/trustlines/{account}","description":"Account trustlines streaming","query":{"page":"int >= 0 (default: 0)","limit":"int 1-50 (default: 10)","format":"default|raw|full","sort_by_value":"true — rank by XRP value"},"messages":"setParams { page?, limit?, format?, sort_by_value? } (strict integer validation; ack: { type: \"paramsUpdated\", params }); resync (max 1 per 5s)","response":"{ account, accountActive, accountData, total, page, limit, lines[] } as type:\"initial\" then e:\"update\" (first frame may be either). With sort_by_value only the first 1000 trustlines are ranked: total <= 1000, totalLines = real count, truncated: true"},{"endpoint":"wss://api.xrpl.to/ws/news","description":"Real-time news feed","messages":"filter { sentiment: \"Bullish\"|\"Bearish\"|\"Neutral\"|null }","response":"initial: { type, data: [10 latest] }; updates: { e: \"news\", E, data }"},{"endpoint":"wss://api.xrpl.to/ws/account/balance/{account}","description":"Account XRP balance streaming","response":"initial adds inception/parent/domain/name; updates: { e: \"balance\", E, account, balance, total, balanceDrops, spendableDrops, reserve, ownerCount, sequence, flags }"},{"endpoint":"wss://api.xrpl.to/ws/account/balance/pair/{account}","description":"Token pair balance streaming","query":{"curr1":"currency (XRP allowed)","issuer1":"issuer (required even for XRP)","curr2":"currency","issuer2":"issuer"},"notes":"All four params required or socket closes (4021)"},{"endpoint":"wss://api.xrpl.to/ws/account/offers/{account}","description":"Open DEX offers streaming","query":{"pair":"pair hash","md5":"token id (32-hex) — filter offers to one token","page":"int >= 0 (default: 0)","limit":"int 1-50 (default: 10)"}},{"endpoint":"wss://api.xrpl.to/ws/amm/info","description":"Live AMM pool info streaming (5s poll)","query":{"amm_account":"AMM account address","asset":"\"XRP\", token id, or {\"currency\",\"issuer\"} JSON","asset2":"same forms as asset"},"notes":"Provide amm_account OR both asset and asset2; response: initial { type, amm } then { e: \"amm\", E, amm } on change"},{"endpoint":"wss://api.xrpl.to/ws/account/watch/{account}","description":"Live account activity feed (parsed tx events)","query":{"history":"int 0-25 (default: 10) — recent txs replayed with historical: true"},"response":"{ type: \"subscribed\", account } then per-tx event payloads"},{"endpoint":"wss://api.xrpl.to/ws/account/tx/{account}","description":"Raw account transaction stream (same shape as GET /account/tx)","query":{"history":"int 0-50 (default: 0)"},"response":"{ type: \"subscribed\", account }; { e: \"history\", txs[] } if history > 0; then live { ...event, tx } per transaction"},{"endpoint":"wss://api.xrpl.to/ws/creator/{identifier}","description":"Creator activity stream (identifier = token md5 or creator address)","response":"initial: { type, identifier, count, events[] }; live: { type: \"activity\", h (tx hash), r (engine result — anything but tesSUCCESS is a rejected attempt), t, s (side), y (tx type), x (xrp), a (token amount), c (currency), token, creator?, fa? }"},{"endpoint":"wss://api.xrpl.to/ws/social","description":"X/Twitter mentions feed","query":{"md5":"token id (omit for global feed)"},"messages":"subscribe { md5 } to switch scope","response":"{ type: \"subscribed\", md5|\"global\" }, 2-min backfill { e: \"mention\", backfill: true }, live { e: \"mention\" } and sentiment patches { e: \"sentiment\" }"}],"websocketNotes":{"heartbeat":"Send { type: \"ping\" } to receive { type: \"pong\", time }","compression":"All endpoints support permessage-deflate","reconnection":"Implement exponential backoff","malformedMessages":"Non-JSON frames on /ws/sync get an error frame { error, errorCode: 4026 } (connection stays open)","closeCodes":"4010 rate limit, 4011 max connections/subscriptions, 4012 message size, 4020-4025 invalid params, 4029 connection limit reached ({ error } sent before close)"},"keys":[{"method":"POST","path":"/keys","description":"Create new API key (1 active key on free tier, 5 on paid tiers)","headers":"X-Wallet, X-Signature, X-Timestamp, X-Public-Key","body":"{ name?: string, project: { name, url: \"https://…\", category: \"wallet|explorer|trading_bot|analytics|ai_agent|other\", description, expectedDaily: \"lt1k|1k-10k|10k-100k|100k+\", contact?: \"email or @handle\" }, agreePolicy: true }","response":"{ success, apiKey, keyPrefix, wallet, credits, project }","notes":"Requires wallet signature. Save the API key immediately - it cannot be retrieved later. The project block and agreePolicy record which project the key serves and that it accepted the usage policy (attribution, no redistribution); the agreement is stamped server-side. Legacy name-only bodies are still accepted for now."},{"method":"GET","path":"/keys/{wallet}","auth":"wallet signature","description":"List API keys for wallet"},{"method":"DELETE","path":"/keys/{wallet}/{id}","auth":"wallet signature","description":"Revoke API key (first call revokes; calling again on a revoked key permanently deletes it)","headers":"X-Wallet, X-Signature, X-Timestamp, X-Public-Key"},{"method":"PUT","path":"/keys/{wallet}/{id}","auth":"wallet signature","body":"{ name: string (1-64) }","description":"Rename an active API key (metadata only)","headers":"X-Wallet, X-Signature, X-Timestamp, X-Public-Key"},{"method":"GET","path":"/keys/{wallet}/credits","auth":"wallet signature","description":"Remaining credits and billing info"},{"method":"GET","path":"/keys/{wallet}/usage","auth":"wallet signature","description":"Usage breakdown by key and endpoint"},{"method":"GET","path":"/keys/{wallet}/subscription","auth":"wallet signature","description":"Subscription details, available upgrades and partnerRequest { status: pending|approved|declined, creditUrl, requestedAt, decidedAt, reason } (null when never requested). billingCycle.end is the paid term end: 30 days for a monthly purchase, 365 for yearly. Buying the same tier again before it ends extends the term; an ended term downgrades the wallet and its keys to Free."},{"method":"POST","path":"/keys/{wallet}/partner-request","auth":"wallet signature","description":"Apply for the free Partner tier (see usagePolicy.partner)","body":"{ creditUrl?: \"https://… page where the Data by xrpl.to credit is visible (optional if your app is not public yet)\", contact: \"email or @handle\" }","notes":"Needs an active key created with project details that has made real requests; one open request per wallet (409 while pending). A person reviews the request (opening creditUrl when given), and approving upgrades every active key on the wallet. Result appears in GET /keys/{wallet}/subscription."},{"method":"GET","path":"/keys/tiers","description":"Available API tiers with live XRP pricing. Developer, Business and Professional are bought for a term (30 days monthly, 365 yearly) and renew by buying the same tier again; Partner is by application."},{"method":"GET","path":"/keys/packages","description":"Credit top-up packages with pricing"},{"method":"GET","path":"/keys/costs","description":"Per-endpoint credit costs"},{"method":"POST","path":"/keys/purchase","description":"Create XRP payment request","body":"{ wallet, type: \"credits\"|\"tier\", package?: \"starter|standard|bulk|mega\", tier?: \"developer|business|professional\", billing?: \"monthly|yearly\" }","notes":"Invoice expires in 30 minutes. After payment, call POST /keys/verify-payment with the tx hash."},{"method":"POST","path":"/keys/verify-payment","body":"{ txHash: \"64-char hex\" }","description":"Verify XRP payment and apply credits/tier"},{"method":"GET","path":"/keys/xrp/status/{txHash}","description":"Check XRP payment status"},{"method":"POST","path":"/keys/stripe/checkout","body":"{ wallet, type, package?, tier?, billing? }","description":"Create Stripe checkout session"},{"method":"GET","path":"/keys/stripe/status/{sessionId}","params":{"wallet":"XRPL address that created the session (required)"},"description":"Check Stripe payment status"},{"method":"GET","path":"/keys/payment/{txHash}","auth":"wallet signature","description":"Look up processed payment by tx hash"},{"method":"GET","path":"/keys/{wallet}/logs","auth":"wallet signature","description":"Recent API request logs for a wallet"},{"method":"GET","path":"/keys/payments/{wallet}","auth":"wallet signature","params":{"limit":"int (max 200, default 200)","offset":"int"},"description":"Payment history for wallet","response":"{ success, total, payments[] }"},{"method":"GET","path":"/keys/invoices/{paymentId}","auth":"wallet signature","description":"Invoice detail for a payment (sequential INV-YYYY-NNNNN number assigned on first fetch)"},{"method":"POST","path":"/feedback","auth":"API key (your own, not a shared one)","description":"Tell us an endpoint, field or parameter you needed and did not get, or a value that was wrong. Built for agents: point at the request with its X-Request-ID and pick a kind; there is no free-text field","body":"{ kind: \"missing_endpoint|missing_field|missing_param|wrong_value|null_field|stale_data|error|slow|docs_mismatch\", requestId?: \"X-Request-ID of the call (32 hex)\", path?: \"/v1/... when there is no requestId\", method?: \"GET|POST|PUT|PATCH|DELETE\", name?: \"the field or parameter (letters, digits, _; dotted for nesting)\" }","response":"{ success, took, received { kind, method, route, name, verified, status }, reports }","notes":"Every response carries X-Request-ID, and a 404 or 500 body carries a ready-made feedback block. With a requestId from your last 50 requests the method, route and status are read from our own log (verified: true); otherwise the path you send is used (verified: false). name is required for missing_field, missing_param, wrong_value and null_field, optional for stale_data and docs_mismatch, refused otherwise. Unknown fields are refused. Sending the same report again the same day adds to its count. Limits: 10 a minute, 50 different reports per wallet a day. A person reads the counts and decides; nothing is changed automatically"}],"user":[{"method":"GET","path":"/user/tiers","description":"User tiers with pricing (member, vip, nova, diamond, verified)"},{"method":"GET","path":"/user/{account}","description":"Get user profile"},{"method":"POST","path":"/user/{account}","auth":"wallet signature","body":"{ username?, referredBy? }","description":"Create user profile"},{"method":"GET","path":"/user/{account}/perks","description":"Resolved perks (tier + roles merged)"},{"method":"GET","path":"/user/{account}/badges","description":"All unlocked badges"},{"method":"GET","path":"/account/{address}/admin","auth":"X-Admin-Key or ADMIN_WALLETS signature","description":"Full account doc split into editable editorial fields vs read-only ledger/derived state, plus the manualFields lock list, the lockedByChain list (fields tx derives from the ledger) and a derived block (decoded on-chain Domain, attributed domain, desc kind)","response":"{ success, took, account, editable, readOnly, manualFields, lockedByChain, fieldOwners, derived }"},{"method":"PATCH","path":"/account/{address}/admin","auth":"X-Admin-Key or ADMIN_WALLETS signature","body":"{ name?, desc?, domain?, twitter?, adminVerified?, verifiedLogo?, verifiedTagRequired? }","description":"Edit editorial fields. Each written field is added to manualFields so the owning background service skips it (tokendata + grok-whale-labeler honour this) — null/empty unsets. Raw ledger fields and scam flags are not editable; `domain` returns 409 when the account sets an on-chain Domain, because tx re-derives it from the ledger."},{"method":"POST","path":"/account/{address}/logo","auth":"X-Admin-Key or ADMIN_WALLETS signature","body":"{ imageData: base64 (PNG/JPEG/GIF/WebP, ~650KB) }","description":"Upload an account logo; served self-hosted at GET /api/account-thumb/{address}"},{"method":"GET","path":"/account-thumb/{address}","description":"Admin-set account logo image bytes (404 when unset)","notes":"Needs an API key (401 without one): send X-Api-Key, or ?apiKey= where headers cannot be sent."},{"method":"POST","path":"/account/resolve","body":"{ addresses: string[] (max 100) }","description":"Batch-resolve admin-verified destination identity (name, logo, tagRequired). Only verified accounts are returned — absence means unknown, which is the safety signal.","response":"{ success, took, count, accounts[] }"},{"method":"PUT","path":"/account/{address}/verified","auth":"X-Admin-Key","body":"{ name (1-60), logo (image id), tagRequired? }","description":"Set verified destination identity (admin only — never user-settable)"},{"method":"DELETE","path":"/account/{address}/verified","auth":"X-Admin-Key","description":"Remove verified destination identity"},{"method":"GET","path":"/user/{account}/labels","auth":"wallet signature (owner only)","description":"Get your own wallet labels — private data, owner-authenticated"},{"method":"GET","path":"/user/{account}/nfts","params":{"limit":"int (max 100)","offset":"int"},"description":"NFTs owned (with thumbnails)","response":"{ success, count, total, nfts[] }"},{"method":"GET","path":"/user/badges/meta","description":"Badge system metadata"},{"method":"GET","path":"/user/badges/{badge}/holders","params":{"limit":"int (max 500)","offset":"int"},"description":"Users holding a specific badge","response":"{ success, badge, count, total, holders[] }"},{"method":"PUT","path":"/user/{account}","auth":"wallet signature","body":"{ username? }","description":"Update username (VIP+ required to change an existing one; tier field is admin-only)"},{"method":"PUT","path":"/user/{account}/display-badge","auth":"wallet signature","body":"{ badge: \"category:id\" | null }","description":"Set displayed badge (must be unlocked)"},{"method":"GET","path":"/user/{account}/labels/watched","auth":"wallet signature (owner only)","description":"Only actively-watched wallet labels — private data, owner-authenticated"},{"method":"POST","path":"/user/{account}/labels","auth":"wallet signature","body":"{ wallet, label (1-50 chars), watching? }","description":"Create/update a wallet label (per-tier cap; 402 when limit reached)"},{"method":"PUT","path":"/user/{account}/labels/{wallet}/watch","auth":"wallet signature","body":"{ watching: bool }","description":"Toggle live-watch on a label (per-tier watcher cap)"},{"method":"DELETE","path":"/user/{account}/labels/{wallet}","auth":"wallet signature","description":"Delete a wallet label"},{"method":"PUT","path":"/user/{account}/avatar","auth":"wallet signature","body":"{ nftId: 64-hex }","description":"Set avatar from an owned NFT"},{"method":"POST","path":"/user/tier/purchase","body":"{ tier: \"vip|nova|diamond\", address }","description":"Create XRP invoice for user tier (expires 30 min)"},{"method":"GET","path":"/user/tier/verify/{invoiceId}","description":"Verify tier XRP payment (scans fee collector; expired invoices honored if exact payment exists)"},{"method":"POST","path":"/user/tier/stripe","body":"{ tier, address }","description":"Stripe checkout for user tier"},{"method":"GET","path":"/user/tier/stripe/{sessionId}","params":{"wallet":"required"},"description":"Check Stripe tier payment status"},{"method":"POST","path":"/user/tier/gift/purchase","body":"{ gifterAddress, recipientAddress, tier }","description":"Gift a tier via XRP invoice"},{"method":"GET","path":"/user/tier/gift/verify/{invoiceId}","description":"Verify gift tier payment"},{"method":"POST","path":"/user/tier/gift/stripe","body":"{ gifterAddress, recipientAddress, tier }","description":"Gift a tier via Stripe"}],"ai":[{"method":"GET","path":"/ai/models","description":"Models available for inference. Billing is metered: each call costs what it uses. Each model lists its price per 1M input and output tokens (price_per_million_usd), what a typical question costs (question_estimate_cents, also as cost_cents), and its published intelligence index (Artificial Analysis, reconciled against OpenRouter's benchmarks API)"}],"chat":[{"method":"GET","path":"/chat/status","params":{"wallet":"r-address"},"description":"Online count + unread status"},{"method":"GET","path":"/chat/messages","params":{"limit":"int (max 100, default 50)","before":"unix ms cursor"},"description":"Public chat history"},{"method":"GET","path":"/chat/session","params":{"wallet":"r-address"},"auth":"X-Api-Key","description":"5-min WebSocket session token"},{"method":"GET","path":"/chat/access","auth":"X-Api-Key","description":"Check chat access for key tier"}],"embed":[{"method":"GET","path":"/embed/{md5}","description":"Embeddable candlestick chart (HTML iframe)","params":{"theme":"dark|light","interval":"string","range":"string","height":"int","showVolume":"bool","showLogo":"bool","logoUrl":"https URL to image (whitelisted formats)"}},{"method":"GET","path":"/embed/score/{md5}","description":"Embeddable token safety-score badge (HTML iframe)","params":{"theme":"dark|light","size":"full - include name + check counts"}}],"lpPositions":[{"method":"GET","path":"/lp-positions/{account}","description":"AMM LP positions with impermanent loss analysis","response":"{ result, positions: { <poolId>: { ilPercent, holdValueXrp, poolValueXrp, currentAsset1, currentAsset2, feesEarnedXrp, currentLpBalance, poolShare, ... } } } (uses result: \"success\", not success: true)"}],"thumbnails":[{"method":"GET","path":"/thumb/{md5}","description":"Token thumbnail (WebP)","params":{"md5":"the token id","w":"16|32|40|48|64|96|128 (default: 48)","v":"source mtime (ms) — exact match returns immutable 1-year cache headers"},"notes":"Needs an API key (401 without one): send X-Api-Key, or ?apiKey= where headers cannot be sent. Fetch it from your server and cache it — a key in a public page's <img> src is visible to everyone."},{"method":"GET","path":"/nft-thumb/{hash}","description":"NFT image/thumbnail (original format, CSP-sandboxed for SVG)","params":{"hash":"an NFT's files[].thumbnail.small, .medium or .large from GET /nft/{nftId} (or a collection's logoNft)"},"notes":"Needs an API key (401 without one): send X-Api-Key, or ?apiKey= where headers cannot be sent."},{"method":"GET","path":"/nft-collection-thumb/{hash}","description":"NFT collection cover image","params":{"hash":"the collection's logoImage from GET /nft/collections/{slug}; without one, show /nft-thumb/{logoNft}"},"notes":"Needs an API key (401 without one): send X-Api-Key, or ?apiKey= where headers cannot be sent."}],"utilities":[{"method":"GET","path":"/health","description":"Service health check with per-service latency","response":"{ status: healthy|degraded, timestamp, version, uptime (s), services: { database, rippled, cache: { status, latency } } }"},{"method":"GET","path":"/openapi.json","description":"OpenAPI 3.1 spec for the whole API (270+ paths)"},{"method":"GET","path":"/docs","description":"This documentation (JSON). Browsers (Accept: text/html) are redirected to the human-readable docs."},{"method":"GET","path":"/ipfs/{cid}","description":"Raw IPFS content by CID, served from our own IPFS node","params":{"cid":"CIDv0 (Qm…) or CIDv1 base32 (bafy…); append /{path} for a file inside a directory CID"},"notes":"No API key. Images, video, audio and JSON are served inline; anything else (HTML, PDF, unknown types) is delivered as a download and never rendered. Content is immutable by CID, so responses carry a one-year immutable cache header. 30 requests/s per IP (burst 60), then 429. The query string is ignored; a malformed CID or path returns 400."}],"testnet":[{"method":"GET","path":"/testnet/fixtures","description":"What the simulation keeps alive on Testnet: live accounts, its tokens (tokens[]: name, currency, issuer, holders, amm, role — stablecoin | deep (the high-liquidity pool) | volatile (a thin pool, swung every round) | crowd — with a note saying what each does, and peg_usd on the stablecoin), its collections (collections[]: name, issuer, taxon, role blue-chip | drop, note, items), an NFT with offers, one of every ledger object, and a real transaction of each type (catalogue[]: id, kind, about, result, hash, and tx — the JSON it sent, without fee, sequence, expiry or signature, a working template for that variation) — start here","response":"{ network_id, generated_at, age_seconds, population, accounts: { issuer, holders[], trader, busy[] }, tokens[], nft, objects, catalogue[] }","notes":"Rebuilt every few minutes by the simulation; age_seconds past a few minutes means Testnet was reset and it is catching up."},{"method":"GET","path":"/testnet/health","description":"Both collectors' head ledger against the network, and whether that is current","response":"{ ok, validated, node: { state, networkId, ledgers }, stages: { tokens: { ledger, at, lag }, nfts: { ledger, at, lag } } }"},{"method":"GET","path":"/testnet/tokens","description":"Testnet tokens. tvl is the token's XRP pool valued on both sides (twice its XRP reserve); a token with no XRP pool has none","params":{"sort":"vol24h | holders | trades24h | trustlines | tvl | firstSeen (default vol24h)","limit":"1-100 (default 50)","page":"from 1"},"response":"{ tokens: [{ _id (md5), issuer, currency, name, exch, spot, amm, vol24h, trades24h, holders, trustlines, supply, firstSeen }], count, total, page, limit, sortBy }"},{"method":"GET","path":"/testnet/tokens/{md5}","description":"A Testnet token with its AMM pool","response":"{ token, pool }"},{"method":"GET","path":"/testnet/tokens/{md5}/holders","description":"Holders of a Testnet token, largest first","params":{"limit":"1-200","page":"from 1"},"response":"{ holders: [{ account, balance, limit, time }], count, total }"},{"method":"GET","path":"/testnet/tokens/{md5}/offers","description":"The order book of a Testnet token against XRP","params":{"limit":"per side, 1-100"},"response":"{ bids[], asks[] } — price is XRP per token; a bid gives XRP, an ask takes it"},{"method":"GET","path":"/testnet/tokens/{md5}/trades","description":"Fills of a Testnet token, newest first","params":{"limit":"1-200","page":"from 1"},"response":"{ trades: [{ maker, taker, paid, got, price, xrp, isAMM, hash, ledger, time }], count, total }"},{"method":"GET","path":"/testnet/tokens/{md5}/ohlc","description":"5-minute candles of a Testnet token","params":{"from":"unix ms (default 7 days ago)","to":"unix ms (default now)","limit":"1-5000"},"response":"{ interval: \"5m\", candles: [{ time, open, high, low, close, xrp_volume, trades, vwap }] }"},{"method":"GET","path":"/testnet/accounts/{address}","description":"A Testnet account as the collector last saw it, with its trust lines, resting offers and last 20 trades","response":"{ account, lines[], offers[], trades[] }"},{"method":"GET","path":"/testnet/amm","description":"Testnet AMM pools, deepest first. A pool of two tokens has token and token2 (in a fixed order, by code) and xrp 0; its spot is token2 per token","params":{"limit":"1-100","page":"from 1"},"response":"{ pools: [{ _id (pool account), xrp, token: { currency, issuer, value }, token2?: { currency, issuer, value }, spot, fee, lp }], count, total }"},{"method":"GET","path":"/testnet/amm/{account}","description":"A Testnet AMM pool by its account","response":"{ pool }"},{"method":"GET","path":"/testnet/nft/collections","description":"Testnet NFT collections, by issuer and taxon. volume/sales are all-time XRP sales; vol24h/sales24h the last 24 h, recounted every minute","params":{"sort":"volume | items | sales | time | vol24h | sales24h (default volume)","limit":"1-100","page":"from 1"},"response":"{ collections: [{ _id, issuer, taxon, items, burned, owners, listed, floor, sales, volume, vol24h, sales24h, time }], count, total, sortBy }"},{"method":"GET","path":"/testnet/nft/collections/{id}","description":"A Testnet NFT collection","params":{"id":"issuer:taxon"},"response":"{ collection }"},{"method":"GET","path":"/testnet/nft/collections/{id}/nfts","description":"The live NFTs of a Testnet collection","params":{"id":"issuer:taxon","owner":"only this owner's","limit":"1-200","page":"from 1"},"response":"{ nfts: [{ _id (NFTokenID), owner, minter, URI, serial, minted }], count, total }"},{"method":"GET","path":"/testnet/nft/history","description":"Testnet NFT events, newest first","params":{"collection":"issuer:taxon","nft":"NFTokenID","account":"the sending account (one of the three is required)","type":"MINT | BURN | LIST | BID | CANCEL | SALE | TRANSFER | MODIFY","limit":"1-200","page":"from 1"},"response":"{ history: [{ type, NFTokenID, cid, account, buyer, seller, amount, broker, hash, ledger, time }], count, total }"},{"method":"GET","path":"/testnet/nft/{id}","description":"A Testnet NFT with the offers standing on it","response":"{ nft, offers[] }"},{"method":"GET","path":"/testnet/nft/{id}/offers","description":"The offers standing on a Testnet NFT, split by side","response":"{ sell[], buy[], count }"},{"method":"GET","path":"/testnet/{address}","description":"Live Testnet XRP balance, reserve, sequence and settings of an account, straight from the network","response":"{ address, activated, balance (drops), balanceXRP, reserve: { baseReserveXRP, ownerReserveXRP, ownerCount, totalReserveXRP }, available: { drops, xrp }, sequence, domain (the Domain field as text), domainHex, flags, ledgerIndex }"},{"method":"POST","path":"/testnet/rpc","description":"The XRP Ledger's own READ commands against our Testnet node — the half of Testnet that is not in the index: account_objects, book_offers, amm_info, ledger_entry, server_info and the rest. Reads only, answered as the node answers any client: feature without vetoed, ledger without full or accounts, the node's non-admin page limits, server_info without host name or ports (submitting is POST /testnet/submit); the node's in-band error (actNotFound, entryNotFound) rides in result with a 200, as on Mainnet","body":"{ method (one of the read commands), params (the command's own fields as an object) }","response":"{ success, took, method, result (the node's own result, verbatim) }","notes":"Every public read command of rippled's own table (src/xrpld/rpc/detail/Handler.cpp, Role::USER), minus the ones that sign or submit and the ones that need a session: account_channels, account_currencies, account_info, account_lines, account_nfts, account_objects, account_offers, account_tx, amm_info, book_changes, book_offers, channel_verify, deposit_authorized, feature, fee, gateway_balances, get_aggregate_price, ledger, ledger_closed, ledger_current, ledger_data, ledger_entry, ledger_header, ledger_index, manifest, mpt_holders, nft_buy_offers, nft_history, nft_info, nft_sell_offers, nfts_by_issuer, noripple_check, owner_info, ping, random, ripple_path_find, server_definitions, server_info, server_state, simulate, transaction_entry, tx, tx_history, tx_reduce_relay, vault_info, version (the Clio-only ones answer unknownCmd on a node that is not Clio, which is itself the answer). subscribe, unsubscribe and path_find are WebSocket commands — open wss://api.xrpl.to/v1/testnet/ws (our Testnet node, no key) and send them there"},{"method":"POST","path":"/testnet/submit","description":"Submit a SIGNED transaction to Testnet through our own node — the contract of POST /submit with the prefix changed. Nothing here signs or holds a seed; fund an account with POST /faucet first","body":"{ tx_blob, fail_hard? }","constraints":"tx_blob is the signed transaction as hex, max 131KB. Duplicate tx_blob within 30s returns 409.","response":"{ success, took, engine_result, engine_result_code, engine_result_message, tx_json, hash, accepted, applied, broadcast, kept, queued, account_sequence_available, account_sequence_next, validated_ledger_index, outcome? }","notes":"engine_result is PRELIMINARY — only a validated ledger is final; follow hash with GET /testnet/tx/{hash} until validated is true or a validated ledger passes the LastLedgerSequence. A 400 with error_code is the node refusing it before processing; outcome:\"unknown\" (502, 409, or no engine_result) means it may still apply — resolve by hash, never by re-signing on a fresh Sequence. Every response carries hash."},{"method":"GET","path":"/testnet/tx/{hash}","description":"A Testnet transaction and its final result, from our node: validated, the ledger it closed in, result (meta.TransactionResult), the transaction and its meta. 404 until the node has seen it","response":"{ hash, validated, ledger_index, result, close_time_iso, tx, meta }"}],"internalEndpoints":["/diagnostics","/faucet","/dapp-session","/ai"],"tierInfo":{"free":{"price":"$0/mo","credits":"1M","rate":"10 req/sec","submit":"1 tx/sec","support":"Community"},"developer":{"price":"$49/mo","credits":"10M","rate":"50 req/sec","submit":"5 tx/sec","support":"Chat"},"partner":{"price":"Invitation-only","credits":"20M","rate":"100 req/sec","submit":"10 tx/sec","support":"Direct"},"business":{"price":"$499/mo","credits":"100M","rate":"200 req/sec","submit":"50 tx/sec","support":"Priority chat"},"professional":{"price":"$999/mo","credits":"200M","rate":"500 req/sec","submit":"100 tx/sec","support":"Telegram + Slack"}},"mcp":{"endpoint":"POST /v1/mcp","description":"MCP server (Model Context Protocol, stateless Streamable HTTP). Connect any MCP client to xrpl.to token analytics: JSON-RPC 2.0 with initialize / tools/list / tools/call. Read-only.","tools":"32 read-only tools, 8 prompts (token-audit, investigate-wallet, trace-funds, market-brief, swap-check, new-launch-scan, whale-watch, compare-tokens) 6 reference resources and 2 resource templates (xrplto://token/{id}, xrplto://account/{address}) — call tools/list, prompts/list, resources/list, resources/templates/list for the authoritative sets","auth":"X-Api-Key header or Authorization: Bearer, with a credit pack (from $5, pay per call) or a paid plan (Developer, Business or Professional), for tools/call and the data resources; initialize and the tool, prompt and resource lists need no key","clientConfig":"{\"url\": \"https://api.xrpl.to/v1/mcp\"} (Streamable HTTP transport)"},"reference":{"tokenId":"md5 | slug (issuer-currency) | issuer_currency | mpt-<mptIssuanceID>","account":"^r[1-9A-HJ-NP-Za-km-z]{24,34}$","NFTokenID":"64-char hex","txHash":"64-char hex","timestamps":"Unix milliseconds (13 digits)","pagination":"offset + limit, or cursor-based with marker","sorting":"sort + order params"},"errors":{"400":"Bad Request (malformed JSON bodies return { success: false, error: \"Invalid JSON in request body\" })","401":"Unauthorized - Missing or invalid API key","402":"Insufficient credits","403":"Forbidden - Wallet does not match resource owner. A NON-JSON 403 comes from the CDN/WAF, not the API — usually a generic programmatic User-Agent; send a descriptive one (see authentication.userAgent).","404":"Not Found","410":"Gone - Invoice expired","429":"Too Many Requests - Rate limit exceeded (Retry-After + X-RateLimit-* headers, retryAfter in body; see rateLimits)","500":"Internal Server Error","format":"JSON: { success: false, error: string, ... }. error is either a short human-readable sentence OR a stable UPPER_SNAKE code for machine matching (e.g. ACCOUNT_NOT_FOUND) — whenever it is a code, a message field carries the human sentence. Bodies may add context fields (retryAfter, docs, hint, balance). requestId (mirroring the x-request-id response header) is included on gateway-level errors — quote it in support requests."}}