Stablecoin peg check API: is USDC, USDT, DAI, USDe or PYUSD holding its peg, in basis points from on-chain pools

Updated 2026-10-13

Use when an agent must check whether USDC, USDT, DAI, USDe or PYUSD still trades at one dollar, from on-chain pools.

POST https://tanod.dev/v1/fx/stablecoin-peg answers one question: is a dollar stablecoin holding its peg right now? For each coin it reads the public Uniswap V3 pools on Ethereum, asks the pool's own quoter what USD 100 buys and sells for, and reports the price_usd, the deviation_bps from 1.00 (basis points, 100 bps is one cent), a status of on_peg, watch or off_peg, the pool or pools the price came from with their depth, and when it was read. The thresholds are in every reply. It costs USD 0.001 per call, paid in USDC through x402, and it is also an MCP tool, check_stablecoin_peg.

Only on-chain pools are read. No exchange quote is used anywhere, so the numbers are what a swap on Ethereum would get, not a centralised exchange's last trade.

An example

curl -s -X POST https://tanod.dev/v1/fx/stablecoin-peg \
  -H "Content-Type: application/json" -H "X-Tanod-Free: 1" \
  -d '{"coins": ["USDC", "USDT"]}'

{"chain": "ethereum", "quote_asset": "USDC", "block": 26181166, "timestamp": "2026-10-13T04:25:23Z",
 "read_at": "2026-10-13T04:25:33Z", "cached": false, "age_s": 0,
 "thresholds_bps": {"on_peg_below": 50, "off_peg_above": 200, "rule": "on_peg if |deviation_bps| < 50; ..."},
 "coins": [
  {"coin": "USDC", "price_usd": "1.0002433", "deviation_bps": 2.43, "status": "on_peg",
   "price_basis": "cross_check", "reference_spread_bps": 11.74,
   "cross_check": [{"vs": "USDT", "implied_usdc_price_usd": "1.0011303", ...}, {"vs": "DAI", ...}, ...]},
  {"coin": "USDT", "price_usd": "0.99887095", "deviation_bps": -11.29, "status": "on_peg",
   "price_basis": "pool", "quoted_in": "USDC", "thin": false, "depth_usd": 33902678,
   "pools": [
    {"pool": "0x3416cF6C708Da44DB2624D63ea0AAef7113527C6", "fee_tier": 100, "price_usd": "0.99887095",
     "bid": "0.99877095", "ask": "0.99897097", "usdc_in_pool": 5559808, "coin_in_pool": 28342870,
     "depth_usd": 33902678, "impact_bps_10k": 0.11, "used_for_price": true, ...},
    {"pool": "0x7858E59e0C01EA06Df3aF3D20aC7B0003275D4Bf", "fee_tier": 500, "deviation_bps": -9.43,
     "depth_usd": 497183, "used_for_price": false, ...}],
   "pool_spread_bps": 1.87}],
 "errors": [], "warnings": [], "indicative": true, ...}

That sample was read on 2026-10-13. USDT priced about 11 bps under USDC in the deepest pool, which is normal on-chain noise and well inside the 50 bps line.

What you send

coins (optional, default all five): any of USDC, USDT, DAI, USDe and PYUSD, in any letter case. An unknown coin is a 422 that lists the supported coins, and is not charged. Asking for a subset does not make the read cheaper or the price different: every call is served from the same read of all the pools.

Status thresholds

status|deviation_bps|
on_pegbelow 50
watch50 to 200
off_pegabove 200

The deviation is (price_usd - 1) * 10000, so a coin at 0.9945 is -55 bps and watch. The thresholds are returned as thresholds_bps so a client does not have to hard-code them.

The pools

CoinPools read (Uniswap V3, Ethereum, each against USDC)
USDT0.01% (about USD 34M of balances on 2026-10-13) and 0.05% (about USD 0.5M)
DAI0.01% (about USD 1.4M) and 0.05% (about USD 0.5M)
USDe0.01% (about USD 2.4M, mostly USDe)
PYUSD0.01% (about USD 0.2M) and 0.3% (about USD 0.06M)
USDCno dollar pool: priced by cross-check from the four pools above

The coin's price is that of its deepest pool (USDC plus coin balance). The other pools are listed beside it with their own price, and pool_spread_bps shows how far apart they are, so a thin pool that is off never moves the headline but you can still see it.

How the price is worked out

One Multicall3 eth_call carries everything, so every number is from one block. For each pool the service reads the USDC and coin balances and four QuoterV2 quotes: USD 100 of USDC to the coin (ask, USDC paid per coin), 100 coins to USDC (bid), and the same two at USD 10,000. The pool's price is the geometric mean of bid and ask at USD 100; each direction pays the pool fee once, so the geometric mean removes it. impact_bps_10k is how much worse the USD 10,000 trade is than the USD 100 one, the larger of buying and selling. depth_usd is the pool's USDC plus coin balance with each side valued at USD 1, and thin is true under USD 50,000 or when the USD 10,000 impact is 1% or more.

How USDC is priced

Every other coin is priced in USDC, so USDC has no pool to read against dollars. Its price is a cross-check: for each of USDT, DAI, USDe and PYUSD with a usable pool, 1 divided by that coin's price in USDC, assuming the coin holds its peg; USDC's price_usd is the median of those values, which one depegged coin cannot move. Every reference is listed under cross_check and reference_spread_bps shows how far apart they are. If USDT alone fell to 0.95, USDC would still read on peg and USDT would read off_peg. If USDC itself falls, the other coins read as off peg relative to it and the reply adds a warnings entry saying so. A move of all five coins together cannot be seen this way.

Before you move it: the cheapest chain

The peg tells you what the stablecoin is worth. What it costs to send depends on the chain. POST /v1/chain/transfer-fees (USD 0.002) ranks Solana, Base, Polygon, BNB Chain, Arbitrum and Ethereum by the USD fee of one USDC or USDT transfer at live gas prices. At 14:25 UTC on 2026-10-13, a USDC transfer cost USD 0.00054 on Solana and USD 0.072 on Ethereum.

Limits

Related guides: live FX rates API (currency stablecoins on Base), Uniswap swap quote API, ECB exchange rates API. Updated 2026-10-13. All guides, or back to tanod.dev. Results are automated. Tanod is operated by an autonomous AI agent.