Skip to content

Rates feed

A public, read-only feed of Textile's live rates and executed trades across every deployed corridor, in the standard exchange-integration format (the tickers / pairs / historical_trades shape aggregators like CoinGecko and Monierate consume). Poll it directly — no key, no signing.

Every corridor we run is listed automatically: cNGN (NGN), plus BRL, ARS, XAUt, WETH, USDC and more. New corridors appear as soon as they deploy, with no config.

Base URL: https://api.textilecredit.com

GET only. CORS is open. Responses are cached for a few seconds, so poll as often as you like.

Conventions

  • Each pair is quoted in its natural direction, so the price reads as a normal number:
    • Fiat corridors quote the foreign currency per stablecoin — USDT_NGN = NGN per USDT (~1394). cNGN is the naira stablecoin, 1:1 with NGN, so it's published as NGN.
    • Asset corridors quote the stablecoin per unit — WETH_USDT = USDT per WETH (~1918).
  • ticker_id is BASE_TARGET. last_price is always target per base (the CoinGecko convention).
  • Numeric fields are decimal strings.

GET /tickers

Live rate for every pair. Pass ticker_id (or base + optional target) to filter.

ParamRequiredExampleDescription
ticker_idnoUSDT_NGNReturn only this pair. Unknown id → 404.
basenoUSDTReturn every pair with this base.
targetnoNGNCombine with base to pin one pair.

No parameters returns all pairs.

json
[
  {
    "ticker_id": "USDT_NGN",
    "base_currency": "USDT",
    "target_currency": "NGN",
    "last_price": "1394.02",
    "bid": "1394.02",
    "ask": "1394.28",
    "high": "1394.28",
    "low": "1394.02",
    "base_volume": "0",
    "target_volume": "0"
  }
]
FieldDescription
ticker_idPair identifier, BASE_TARGET.
base_currency / target_currencyThe two legs.
last_priceOur best available rate right now (the more competitive of bid/ask), target per base.
bid / askBest available (top-of-book) rate from the live order book, net of feesbid = best rate selling the base, ask = best rate buying it.
high / lowHighest / lowest cleared price over the last 24h.
base_volume / target_volume24h traded volume in the base / target asset.

bid/ask are the fee-inclusive rate of the best slice on our order book — the rate a small, top-of-book trade clears at, not an indicative mid. A corridor with no live order book falls back to the market mid.

GET /pairs

The pairs the feed supports. Updates automatically as corridors deploy.

json
[
  { "ticker_id": "USDT_NGN", "base": "USDT", "target": "NGN" },
  { "ticker_id": "USDC_NGN", "base": "USDC", "target": "NGN" },
  { "ticker_id": "WETH_USDT", "base": "WETH", "target": "USDT" }
]

GET /historical_trades

Executed trades for one pair, newest first. Same source as the 24h volume on /tickers — the swaps that actually filled, so these are real cleared prices, not quotes.

ticker_id is required (a full-history firehose across every pair isn't useful to anyone).

ParamRequiredExampleDescription
ticker_idyesUSDT_NGNThe pair. base + target works too. Unknown pair → 404.
typenobuybuy or sell. Omit for both.
limitno500Max trades per side. Default 200, max 1000 (higher is clamped).
start_timeno1754800000Unix seconds, inclusive lower bound.
end_timeno1754886400Unix seconds, inclusive upper bound.
json
{
  "buy": [
    {
      "trade_id": "0xa1b2c3d4…-7",
      "price": "1394.02",
      "base_volume": "100.00",
      "target_volume": "139402.00",
      "trade_timestamp": 1754800000,
      "type": "buy"
    }
  ],
  "sell": []
}
FieldDescription
trade_idThe fill's transaction hash and log index, joined by -. Stable and unique, so it works as a dedupe key when polling.
priceCleared price, target per base.
base_volume / target_volumeThe two legs of that trade.
trade_timestampUnix seconds.
typebuy = the base was bought, sell = the base was sold — the same side convention as bid/ask above.

One row is one filled order. A swap large enough to clear against several resting orders prints one trade per order, each at the price that order actually cleared at — so the rows are real executions, not batch averages.

type=buy/type=sell still returns both keys — the filtered-out side is just an empty array.

To walk back through history, take the oldest trade_timestamp in the page you just got and request end_time = that - 1. Repeat until a page comes back empty.

Stepping one second back is safe because a page never splits a second: every second we return is complete. That also means a page can come back slightly larger than limit — if a single second holds more trades than you asked for, you get all of them rather than a fragment you'd have no way to ask for the rest of. Treat limit as a target, not a hard cap.

buy and sell share one cutoff, so a side can also come back shorter than limit, or empty, when the other side is the busier one. That's what keeps a single end_time valid for the whole response — the next page picks up whatever was held back.

Errors

Errors return { "error": "..." } with an appropriate status: 400 for a missing or malformed parameter, 404 for an unknown ticker_id, 405 for a non-GET method, 502 when the feed can't be built.