Base URL https://xrp.keystonetech.io · all responses are JSON · IDs are returned as strings.
Create an account on the signup page — you get a Starter key instantly, no card. Keep it somewhere safe; it's shown once.
Pass the key as a bearer token. This returns index totals and the current lag:
curl https://xrp.keystonetech.io/v1/stats \ -H "Authorization: Bearer $KEY"
const res = await fetch("https://xrp.keystonetech.io/v1/stats", {
headers: { Authorization: `Bearer ${process.env.XRPL_KEY}` },
});
const data = await res.json();
console.log(data);
import os, requests
r = requests.get(
"https://xrp.keystonetech.io/v1/stats",
headers={"Authorization": f"Bearer {os.environ['XRPL_KEY']}"},
)
print(r.json())
Pull an account's recent activity (below), or on Dev+ get a signed webhook the instant something happens. Check your usage anytime on your dashboard.
Every request except /v1/health, /v1/plans, and /v1/public-stats needs your key as a bearer token. Usage is metered per your plan; over-limit requests return 429.
Recent transactions touching an account (from tx metadata), most-recent first.
curl "https://xrp.keystonetech.io/v1/accounts/rYourAccount.../activity?limit=5" \ -H "Authorization: Bearer $KEY"
const account = "rYourAccount...";
const res = await fetch(
`https://xrp.keystonetech.io/v1/accounts/${account}/activity?limit=5`,
{ headers: { Authorization: `Bearer ${process.env.XRPL_KEY}` } },
);
const { activity } = await res.json();
for (const tx of activity) console.log(tx.ledger_index, tx.tx_type, tx.tx_hash);
import os, requests
account = "rYourAccount..."
r = requests.get(
f"https://xrp.keystonetech.io/v1/accounts/{account}/activity",
headers={"Authorization": f"Bearer {os.environ['XRPL_KEY']}"},
params={"limit": 5},
)
for tx in r.json()["activity"]:
print(tx["ledger_index"], tx["tx_type"], tx["tx_hash"])
Payments to or from an account — XRP or issued-currency.
Index totals + current indexing lag.
Subscribe an HTTPS endpoint; we POST a signed event whenever a matching transaction is validated. Filter by a single event, a whole category, an account, or any combination.
| field | meaning |
|---|---|
url | your HTTPS endpoint (required) |
event | one event code, e.g. payment.received (optional) |
category | a whole family, e.g. nft · dex · amm (optional) |
watch_account | fire only for this account (optional) |
watch_tx_type | raw XRPL type, e.g. Payment (optional) |
direction | any · incoming · outgoing |
min_drops | minimum XRP amount in drops (optional) |
include_failed | also deliver failed (non-tesSUCCESS) transactions — default false |
curl -X POST https://xrp.keystonetech.io/v1/subscriptions \
-H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
-d '{"url":"https://you.example/hook","event":"payment.received","min_drops":1000000000}'
# → { "id":"…", "secret":"whsec_…" } (the secret signs your webhooks — shown once)
const res = await fetch("https://xrp.keystonetech.io/v1/subscriptions", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.XRPL_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
url: "https://you.example/hook",
category: "nft", // every NFT event; or event: "nft.minted" for one
watch_account: "rYourAccount...",
}),
});
const { id, secret } = await res.json();
console.log("store this signing secret:", secret);
import os, requests
res = requests.post(
"https://xrp.keystonetech.io/v1/subscriptions",
headers={"Authorization": f"Bearer {os.environ['XRPL_KEY']}"},
json={
"url": "https://you.example/hook",
"event": "payment.received",
"min_drops": 1_000_000_000, # 1000 XRP
},
)
data = res.json()
print("store this signing secret:", data["secret"])
Every event we emit, live from GET /v1/events (no auth). Subscribe to a
single event code or to an entire category.
{
"id": "12345",
"event": "payment.received",
"ledger_index": 106012013,
"tx_hash": "A1B2…",
"ts": "2026-08-02T13:22:04.113Z",
"data": {
"event": "payment.received",
"category": "payment",
"tx_type": "Payment",
"account": "rSender…", "destination": "rYou…",
"result": "tesSUCCESS", "success": true, "fee_drops": 12,
"matched_account": "rYou…", "role": "destination",
"close_time": "2026-08-02T13:22:01.000Z",
"payment": { "is_xrp": true, "amount_drops": 25000000,
"from": "rSender…", "to": "rYou…" },
"delivered_amount": "25000000",
"tx": { "TransactionType": "Payment", "…": "full transaction fields, minus signatures" }
}
}
We sign every webhook: X-XRPL-Signature: sha256=HMAC_SHA256(secret, rawBody). Compute the same HMAC over the raw request body and reject anything that doesn't match. Deliveries are at-least-once — dedupe on X-XRPL-Delivery.
# Signature verification is done in your webhook receiver — see the # JavaScript or Python tab for a copy-paste verifier.
import crypto from "node:crypto";
// Express: use express.raw({ type: "application/json" }) so req.body is the raw Buffer.
function verify(rawBody, header, secret) {
const expected =
"sha256=" + crypto.createHmac("sha256", secret).update(rawBody).digest("hex");
return header === expected;
}
app.post("/hook", express.raw({ type: "*/*" }), (req, res) => {
if (!verify(req.body, req.get("X-XRPL-Signature"), process.env.WHSEC))
return res.status(401).end();
const event = JSON.parse(req.body.toString());
console.log(event.event, event.tx_hash, event.data);
res.json({ received: true });
});
import hmac, hashlib
from flask import Flask, request, abort
app = Flask(__name__)
WHSEC = "whsec_..."
def verify(raw_body: bytes, header: str, secret: str) -> bool:
expected = "sha256=" + hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()
return hmac.compare_digest(header or "", expected)
@app.post("/hook")
def hook():
if not verify(request.get_data(), request.headers.get("X-XRPL-Signature"), WHSEC):
abort(401)
event = request.get_json()
print(event["event"], event["tx_hash"], event["data"])
return {"received": True}
Payments over a ledger range. Row count is metered against your monthly export quota.
curl "https://xrp.keystonetech.io/v1/export/payments?from_ledger=105000000&to_ledger=105001000&limit=10000" \ -H "Authorization: Bearer $KEY" -o payments.json
const res = await fetch(
"https://xrp.keystonetech.io/v1/export/payments?from_ledger=105000000&to_ledger=105001000&limit=10000",
{ headers: { Authorization: `Bearer ${process.env.XRPL_KEY}` } },
);
const { count, payments } = await res.json();
console.log(`${count} payments`);
import os, requests
r = requests.get(
"https://xrp.keystonetech.io/v1/export/payments",
headers={"Authorization": f"Bearer {os.environ['XRPL_KEY']}"},
params={"from_ledger": 105000000, "to_ledger": 105001000, "limit": 10000},
)
data = r.json()
print(data["count"], "payments")
Your plan, limits, and current-month usage — or manage it all on your dashboard.
| code | meaning |
|---|---|
401 | missing / invalid API key |
403 | feature not on your plan (upgrade) |
429 | rate limit or monthly quota exceeded |
404 | not found |