GET /docs
GET

/v1/docs

This documentation (JSON). Browsers (Accept: text/html) are redirected to the human-readable docs.

AuthNone — works without a key

Request

GEThttps://api.xrpl.to/v1/docs
curl 'https://api.xrpl.to/v1/docs'

Response

200Example response2 ms · just now · 50 KB · 1157 lines
https://api.xrpl.to/v1/docs
{
  "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 …"
    },
    "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, 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",
      "amount": "total issued supply (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": "% of supply held by the creator wallet",
      "top10": "% of supply held by the top 10 holders",
      "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)"
      },
      "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\"]), acc…"
    },
    {
      "method": "GET",
      "path": "/tags",
      "description": "List all token tags",
      "response": "{ success, took, exch, H24, global, tags }"
    }
  ],
  "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)",
        "resolution": "int (minutes), or D|W",
        "cb": "int (candle bar count, max 5000)",
        "invert": "bool"
      },
      "response": "{ success, took, length, format, resolution, interval, interval_seconds, vs_currency, inverted, base_interval, preAgg?, ohlc: [[time, open, high, low, close, volume, ...]] }"
    },
    {
      "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 }, 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?, bough…"
    }
  ],
  "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": "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|created",
        "status": "active|all",
        "limit": "int (max 100)",
        "offset": "int",
        "token": "token id",
        "currency": "currency code filter",
        "issuer": "issuer address filter"
      },
      "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[] }"
    }
  ],
  "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), e.g. {\"ledger\":N,\"seq\":M}",
        "types": "comma-separated TransactionTypes",
        "forward": "bool (default: false)",
        "ledgerMin": "int - ledger index lower bound",
        "ledgerMax": "int - ledger index upper bound"
      },
      "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)"
    }
  ],
  "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)",
      "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); 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 (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…"
    },
    {
      "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 }"
    }
  ],
  "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"
    }
  ],
  "txExplain": [
    {
      "method": "GET",
      "path": "/tx-explain/{hash}",
      "description": "Human-readable transaction explanation (20 credits)",
      "response": "{ hash, extracted, summary: { summary, keyPoints[] }, timestamp }"
    }
  ],
  "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)",
      "response": "{ md5, token, score, riskLevel, 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"
      }
    }
  ],
  "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 circulating supply PLUS anything still locked in escrow, since escrowed tokens sit outside every trustline. summary.placed* account for every…"
    }
  ],
  "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)",
        "offset": "int (clamped >= 0)",
        "limit": "int (1-100, default 20)"
      },
      "description": "Search news articles",
      "response": "{ query, 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)"
    }
  ],
  "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?[] }",
      "description": "Create new token with AMM"
    },
    {
      "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"
    }
  ],
  "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"
    }
  ],
  "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"
    }
  ],
  "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"
    }
  ],
  "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"
    }
  ],
  "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"
      },
      "description": "Get quote for boosting a token"
    },
    {
      "method": "POST",
      "path": "/boost/purchase",
      "body": "{ md5, boostAmount, payerAddress }",
      "description": "Create boost payment request"
    },
    {
      "method": "GET",
      "path": "/boost/verify/{invoiceId}",
      "description": "Verify payment and apply boost"
    },
    {
      "method": "POST",
      "path": "/boost/stripe",
      "body": "{ md5, packId, payerAddress }",
      "description": "Create Stripe checkout for boost"
    },
    {
      "method": "GET",
      "path": "/boost/stripe/{sessionId}",
      "params": {
        "wallet": "r-address (required, must match invoice payer)"
      },
      "description": "Check Stripe boost payment status"
    }
  ],
  "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 ov…"
    },
    {
      "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"
    }
  ],
  "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)"
      }
    }
  ]
}

Long arrays and strings are shortened in the example; Send request for the full answer.

What it does

This documentation (JSON). Browsers (Accept: text/html) are redirected to the human-readable docs.

OpenAPI