curl --request GET \
--url https://api.paycrest.io/v2/rates/{network}/{from}/{amount}/{to}import requests
url = "https://api.paycrest.io/v2/rates/{network}/{from}/{amount}/{to}"
response = requests.get(url)
print(response.text)const options = {method: 'GET'};
fetch('https://api.paycrest.io/v2/rates/{network}/{from}/{amount}/{to}', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.paycrest.io/v2/rates/{network}/{from}/{amount}/{to}",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api.paycrest.io/v2/rates/{network}/{from}/{amount}/{to}"
req, _ := http.NewRequest("GET", url, nil)
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://api.paycrest.io/v2/rates/{network}/{from}/{amount}/{to}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.paycrest.io/v2/rates/{network}/{from}/{amount}/{to}")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
response = http.request(request)
puts response.read_body{
"status": "success",
"message": "Operation successful",
"data": {
"buy": {
"rate": "<string>",
"providerIds": [
"<string>"
],
"orderType": "<string>",
"refundTimeoutMinutes": 123
},
"sell": {
"rate": "<string>",
"providerIds": [
"<string>"
],
"orderType": "<string>",
"refundTimeoutMinutes": 123
}
}
}Get Token Rate
Public quote on a specific network. Returns buy and/or sell quotes, order type, and refund timeout.
Token + fiat: each of from / to is either an enabled fiat currency code or an enabled crypto (token) symbol on network. Exactly one must be fiat and one crypto (.../USDT/100/NGN and .../NGN/100/USDT are equivalent). amount is the crypto notional. Rates are fiat per 1 token for each side. from and to cannot both be crypto.
Ambiguous segment: if a path value matches both an enabled fiat code and an enabled crypto symbol on that network (e.g. same string), the request returns 400 unless from_source and/or to_source is set to fiat or crypto to pick the leg.
Fiat + fiat: both segments are fiat codes. The quote bridges through USDC on the same network. amount is denominated in from fiat. sell is destination fiat per 1 unit of from fiat (buy USDC with from, sell USDC for to). buy is from fiat per 1 unit of to fiat (inverse corridor; buy USDC with to, sell USDC for from)—asymmetric from sell, not a simple reciprocal.
Path order does not imply trade direction for token/fiat pairs; omitting side returns both sides when available.
Provider queue: each side carries providerIds — up to 3 provider ids ranked best first, in the order assignment would try them. Pass them to destination.providerIds on POST /sender/orders to lock routing to the providers you quoted. Use limit to request fewer.
For the legacy single-number response, use GET /v1/rates/{token}/{amount}/{fiat} with optional network query.
curl --request GET \
--url https://api.paycrest.io/v2/rates/{network}/{from}/{amount}/{to}import requests
url = "https://api.paycrest.io/v2/rates/{network}/{from}/{amount}/{to}"
response = requests.get(url)
print(response.text)const options = {method: 'GET'};
fetch('https://api.paycrest.io/v2/rates/{network}/{from}/{amount}/{to}', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.paycrest.io/v2/rates/{network}/{from}/{amount}/{to}",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api.paycrest.io/v2/rates/{network}/{from}/{amount}/{to}"
req, _ := http.NewRequest("GET", url, nil)
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://api.paycrest.io/v2/rates/{network}/{from}/{amount}/{to}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.paycrest.io/v2/rates/{network}/{from}/{amount}/{to}")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
response = http.request(request)
puts response.read_body{
"status": "success",
"message": "Operation successful",
"data": {
"buy": {
"rate": "<string>",
"providerIds": [
"<string>"
],
"orderType": "<string>",
"refundTimeoutMinutes": 123
},
"sell": {
"rate": "<string>",
"providerIds": [
"<string>"
],
"orderType": "<string>",
"refundTimeoutMinutes": 123
}
}
}GET /v1/rates/{token}/{amount}/{fiat} (optional network query) returns a single numeric data field. The v2 path below returns a structured buy / sell payload.Path parameters
| Parameter | Description |
|---|---|
network | Network id: ethereum, base, bnb-smart-chain, lisk, celo, arbitrum-one, polygon, starknet, tron |
from | Fiat currency code or token symbol on network (paired with to) |
amount | For token + fiat: crypto (token) notional. For fiat + fiat: amount in from fiat. |
to | Fiat currency code or token symbol on network (paired with from) |
.../USDT/100/NGN ≡ .../NGN/100/USDT). from/to cannot both be tokens. Path order does not imply trade direction—use side or read data.buy / data.sell for on-ramp vs off-ramp display.
Fiat + fiat: both are fiat codes; the server quotes through USDC on the same network. sell.rate = to per 1 from. buy.rate = from per 1 to (inverse corridor, not the reciprocal of sell—each side uses the appropriate leg bid/ask).
Query parameters
| Parameter | Required | Description |
|---|---|---|
side | No | buy or sell to return only that side. Omit to receive both when available. |
from_source | No | If from matches both a fiat currency and a crypto asset on the network, set to fiat or crypto. Omit when unambiguous. |
to_source | No | If to matches both a fiat currency and a crypto asset on the network, set to fiat or crypto. Omit when unambiguous. |
provider_id | No | Exactly 8 alphabetic characters (A–Z, a–z) to pin the quote to one provider. Returns a single entry in providerIds. |
limit | No | Max ranked provider ids per side in providerIds. Integer 1–3, default 3. Anything else returns 400. |
Example
# Both buy and sell quotes on Base (token then fiat)
GET https://api.paycrest.io/v2/rates/base/USDT/100/NGN
# Same pair with fiat then token
GET https://api.paycrest.io/v2/rates/base/NGN/100/USDT
# Sell side only (typical for offramp display)
GET https://api.paycrest.io/v2/rates/base/USDT/100/NGN?side=sell
# Specific provider
GET https://api.paycrest.io/v2/rates/base/USDT/100/NGN?provider_id=AbCdEfGh
# Best provider only (single id in providerIds)
GET https://api.paycrest.io/v2/rates/base/USDT/100/NGN?limit=1
Success response (data)
Each populated side includes:
| Field | Type | Description |
|---|---|---|
rate | string | For token+fiat: fiat per crypto. For fiat+fiat: see path rules above (sell = to per from, buy = from per to). This is the rate of providerIds[0]. |
providerIds | string[] | Ranked provider queue, best first—up to 3 ids, in the order assignment would try them. See Rate resolution. |
orderType | string | e.g. regular |
refundTimeoutMinutes | integer | Minutes until automatic refund for that flow |
{
"status": "success",
"message": "Rate fetched successfully",
"data": {
"buy": {
"rate": "1498.25",
"providerIds": ["AbCdEfGh"],
"orderType": "regular",
"refundTimeoutMinutes": 60
},
"sell": {
"rate": "1500.50",
"providerIds": ["AbCdEfGh", "IjKlMnOp", "QrStUvWx"],
"orderType": "regular",
"refundTimeoutMinutes": 60
}
}
}
side=buy or side=sell, only that key is present under data.
providerIds is always an array—[] when no provider is available, never null. It carries exactly one id when you pass ?provider_id= or ?limit=1. Fewer than 3 ids means fewer eligible providers for that corridor and notional. The fallback provider is never listed.
Errors
| HTTP | Typical cause |
|---|---|
| 400 | Invalid amount, invalid side, invalid from_source/to_source (must be fiat or crypto), ambiguous segment without disambiguation, invalid provider_id, limit outside 1–3, unsupported asset on network, or conversion rules (e.g. stable only to its base fiat) |
| 404 | No provider available for the amount/currency/network |
| 503 | Banking/mobile network issues affecting providers for the currency |
| 500 | Internal error while resolving the rate |
Rate resolution
Behavior matches the priority-queue–based resolution used when creating orders: the quote reflects provider selection and validation for the requested side (buy vs sell). Path segment order (from / to) does not select direction—use ?side=, or read data.sell for off-ramp (crypto → fiat) display and data.buy for on-ramp (fiat → crypto) before POST /v2/sender/orders.
The provider queue
providerIds is ranked, best first, and is the same queue an order created from this quote will walk. providerIds[0] is the provider whose rate is quoted in rate; the remaining ids are the ones assignment falls through to if the head declines, expires, or exhausts its retries.
The queue is resolved once, at order creation, and the order carries it for its whole life. Two ways to use that:
Let the API resolve it
destination.providerId and destination.providerIds on create. The API resolves a fresh queue at creation time. Nothing to do—this is the default.Pass the queue you quoted
data.sell.providerIds straight through as destination.providerIds on POST /v2/sender/orders. Routing is then pinned to the providers your user actually saw a rate from. See Pin a provider queue.providerIds lists only providers open to every sender. If you route to a provider dedicated to you, name it directly in destination.providerIds on create—it will not appear in a public quote.
See also Get Token Rate (v1) for the legacy scalar response.Path Parameters
Blockchain network identifier (lowercase in URL).
ethereum, base, bnb-smart-chain, lisk, celo, arbitrum-one, polygon, starknet, tron Fiat code or token symbol on network (paired with to).
For token+fiat pairs, the token notional. For fiat+fiat pairs, the amount in from fiat.
Fiat code or token symbol on network (paired with from).
Query Parameters
Return only the buy or sell quote. Omit for both sides.
buy, sell When from matches both a fiat and a crypto asset on network, set to fiat or crypto to disambiguate. Ignored when from is unambiguous.
fiat, crypto When to matches both a fiat and a crypto asset on network, set to fiat or crypto to disambiguate. Ignored when to is unambiguous.
fiat, crypto Optional. When set, limits the quote to this provider. Must be exactly 8 alphabetic characters (A–Z, a–z). Returns a single entry in providerIds.
^[A-Za-z]{8}$Maximum number of ranked provider ids to return in providerIds, per side. Values outside 1–3 return 400.
1 <= x <= 3