API reference
Blockchains & tokens
The public catalog of supported chains and the stablecoins each accepts, with each chain's rail0 deployment and measured settlement time.
List blockchains available as payment methods
GET /blockchains
Public — no credentials
Query parameters
-
network_type string
Filter by network type.
One of:testnet,mainnet -
symbol string
Filter by native symbol (case-insensitive, e.g. ETH, USDC, CELO).
Responses
-
200
Active blockchains that carry at least one active token (a payment method is a chain+token pair). Each carries
settlement, the measured broadcast-to-confirmation time on this gateway over a trailing window — served from an in-process cache refreshed at most every 10 minutes, so the listing never runs the aggregate per request.
curl "https://api.rail0.xyz/blockchains"const res = await fetch("https://api.rail0.xyz/blockchains");
if (!res.ok) { const { code, detail } = await res.json(); throw new Error(`${res.status} ${code}: ${detail}`);}const data = await res.json();require "json"require "net/http"
uri = URI("https://api.rail0.xyz/blockchains")req = Net::HTTP::Get.new(uri)
res = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == "https") { |http| http.request(req) }raise "#{res.code}: #{res.body}" unless res.is_a?(Net::HTTPSuccess)data = JSON.parse(res.body)package main
import ( "fmt" "io" "net/http")
func main() { url := "https://api.rail0.xyz/blockchains" req, err := http.NewRequest(http.MethodGet, url, nil) if err != nil { panic(err) }
res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close()
data, err := io.ReadAll(res.Body) if err != nil { panic(err) } fmt.Println(res.Status, string(data))}[ { "chain_id": 84532, "name": "Acme Store", "native_symbol": "string", "network_type": "string", "explorer_url": "https://acme.example/rail0", "required_confirmations": 1, "finality_tag": null, "contract": { "address": "0x71C7656EC7ab88b098defB751B7401B5f6d8976F", "version": "string", "deployed_at": "2026-10-01T12:00:00Z" }, "settlement": { "p50_seconds": null, "p90_seconds": null, "sample_size": 1, "window_days": 1 } }]List active tokens, optionally filtered by chain
GET /tokens
Public — no credentials
Query parameters
-
chain_id integer
EVM chain ID to filter tokens.
-
symbol string
Token symbol to filter by (case-insensitive, e.g. USDC).
-
active boolean
Filter by active flag. OMITTED RETURNS EVERY TOKEN, retired ones included — this endpoint is the historical catalogue, so a payment created years ago still resolves its token. Only an active token may be used for a NEW payment (POST /payments answers 422 unknown_token otherwise), so a checkout picker should pass ?active=true.
Responses
- 200 Active tokens.
curl "https://api.rail0.xyz/tokens?chain_id=84532"const res = await fetch("https://api.rail0.xyz/tokens?chain_id=84532");
if (!res.ok) { const { code, detail } = await res.json(); throw new Error(`${res.status} ${code}: ${detail}`);}const data = await res.json();require "json"require "net/http"
uri = URI("https://api.rail0.xyz/tokens?chain_id=84532")req = Net::HTTP::Get.new(uri)
res = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == "https") { |http| http.request(req) }raise "#{res.code}: #{res.body}" unless res.is_a?(Net::HTTPSuccess)data = JSON.parse(res.body)package main
import ( "fmt" "io" "net/http")
func main() { url := "https://api.rail0.xyz/tokens?chain_id=84532" req, err := http.NewRequest(http.MethodGet, url, nil) if err != nil { panic(err) }
res, err := http.DefaultClient.Do(req) if err != nil { panic(err) } defer res.Body.Close()
data, err := io.ReadAll(res.Body) if err != nil { panic(err) } fmt.Println(res.Status, string(data))}[ { "chain_id": 84532, "symbol": "USDC", "address": "0x71C7656EC7ab88b098defB751B7401B5f6d8976F", "decimals": 6, "active": false }]