HTTP API

Two endpoints do the work: /v1/quote prices a trade, and /v1/swap prices it and returns calldata you can send. Both are plain GET requests with query parameters and JSON responses, and both are callable without credentials.

Base URL and conventions

https://exen.wraxyn.io/v1/quote?chainId=8453&mode=sell&…
ConventionDetail
TransportHTTP GET, query parameters, JSON response bodies.
AmountsAlways decimal strings of base units (wei). "1500000"is 1.5 USDC. Never floats — and never assume 6 decimals for a stablecoin, because BSC's are 18.
Addresses0x-prefixed 20-byte hex, case-insensitive.
Native tokenETH / BNB / AVAX / OKB is the sentinel 0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee on either side.
Field namingcamelCase on /v1/quote and /v1/swap. /v1/{chain}/sources answers in snake_case — a known inconsistency, not a typo in your client.
ChainchainId is a query parameter on quote and swap, and a {chain} path segment on sources — which also accepts a name (ethereum, base, arbitrum, bsc, avalanche, xlayer), a common alias (eth, bnb, avax) or the numeric id.
[ quotes are stateless ]

Nothing is reserved and there is no quote-id handshake — /v1/swap re-solves at its own block. If you intend to execute, call /v1/swap directly rather than pairing a /v1/quote with a later swap and expecting the same numbers.

Authentication and API keys

Every route is served without credentials. Sending a partner key in the x-api-key header unlocks the partner configuration held against it:

  • A rate budget of your own, instead of sharing the anonymous per-IP one with everyone else on your network.
  • Attribution. Your partner id is hashed into the on-chain tag of every settlement, so fills you routed are identifiable on-chain and reconcilable to your responses.
  • A negotiated fee policy, which overrides any fee in the request.
  • Access to gated venues. A few venues price only for allowlisted parties (tokenised real-world assets, for instance) and are invisible without an approved key.
[ getting a key ]

Email admin@wraxyn.io with what you are building, the chains you need, and a rough request rate. Keys are free; we issue them to know who to contact when something changes. Treat the key as a secret — keep it on a server, never in a browser bundle or a mobile app, and tell us if it leaks so we can rotate it.

Rate limits

Limits are applied at the edge, before a request reaches the engine.

CallerBudget
No keyMetered per IP, default 3 requests per second. Fine for a wallet, a dashboard or a spreadsheet; not enough for a quoting loop.
With a keyA budget agreed with you and attached to the key. Tell us the shape of your load — steady polling, bursty, or latency-critical — and it is set accordingly.
  • Over-budget requests are rejected at the edge. Back off and retry rather than retrying immediately — a tight retry loop spends the next second's budget too.
  • Cache nothing that prices. A quote is a point in time; a cached one is not a cheaper quote, it is a wrong one.
  • If you need a lot of quotes, timeLimitMs is the lever that matters more than the rate — see latency.

GET /v1/quote

Solves the best route for a pair and size at the current block and returns exact integer amounts, the per-hop route breakdown, gas and USD values.

curl -s "https://exen.wraxyn.io/v1/quote\
?chainId=8453&mode=sell\
&sellToken=0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee\
&buyToken=0x833589fcd6edb6e08f4c7c32d4f71b54bda02913\
&sellAmount=1000000000000000000\
&slippagePct=0.5" \
  -H "x-api-key: $EXEN_KEY"

Parameters

ParameterRequiredMeaning
chainIdyesNumeric EIP-155 id. A chain this instance does not host answers 404.
modeyessell — exact input, requires sellAmount. buy — exact output, requires buyAmount.
sellTokenyesAddress, or the native sentinel. Must differ from buyToken.
buyTokenyesAddress, or the native sentinel.
sellAmounton sellInput amount, decimal wei string.
buyAmounton buyTarget output, decimal wei string.
slippagePctnoPercent (0.5 = 0.50%), default 0.5, clamped to [0.00001, 50]. Sets minBuyAmount on a sell. Ignored on a buy — the response echoes slippagePct: 0.
timeLimitMsnoSolve budget in milliseconds, 50–5000. Unset applies the maximum. Below 300 also excludes RFQ venues — see latency.
maxHopsnoHop-depth override, 1–7. The default scales with trade size in USD (2 below $25k, then 3, 4 and 6 at $25k / $250k / $10M).
gasPriceGweinoOverrides the gas price used to value routes and to compute gasUsd. Affects route selection, never execution.
takerno hereThe wallet holding the input. Optional on quote (echoed back), required on swap.
recipientnoWho receives the output. Defaults to taker.
excludeRfqnotrue drops RFQ market-maker venues and attestation-gated hooks at any budget. Use it when you want a firm, on-chain-only quote. Does not drop real-world-asset venues.
excludeRwanotrue drops real-world-asset venues, independently of excludeRfq.
excludeVenues
includeVenues
noComma-separated venue labels — the exact source strings the response reports, case-insensitive; list them with GET /v1/{chain}/sources. Exclude routes through everything else; include routes through only these. Mutually exclusive — sending both is a 400. Matching is hierarchical: a family label also matches its family:variant hook variants. The synthetic WRAP hop is not a venue and is never excludable, so native swaps keep working under any include list.
feeBps
feeRecipient
noYour integrator fee — see fees. Both or neither; feeBps alone is a 400.

Response

FieldMeaning
quoteIdUnique per response, and the high 16 bytes of the on-chain tagin the router's Swap event — so a settlement is attributable to the exact response that produced it.
sellAmountOn a sell, your input echoed. On a buy, the solved maximum input; unused input is swept back to the taker.
buyAmountOutput from exact integer re-simulation — the same math the settlement enforces. Net of any fee, gross of gas. On a buy it is exactly the amount you asked for.
minBuyAmountThe floor enforced on-chain. On a sell, buyAmount less your slippage tolerance. On a buy, equal to buyAmount.
priceImpactA ratio, not a percentage: amountOutUsd / amountInUsd, where 1.0 is lossless and 0.98means 2% lost. Null when either side is unpriced. Printing it raw understates a 1% impact as “0.99”.
gasOur estimate of how much gas this route will burn, in gas units — the plan's hops priced by venue class. Use it to compare routes and to show a cost; it is an estimate, not a gasLimit to send (see the swap response). L2 calldata / data-availability fees are not included — add your own on Base, Arbitrum and X Layer before comparing aggregators.
gasPriceWei
gasUsd
The price gas was valued at and the resulting USD figure. Priority tips are excluded — the tip is the submitter's choice. gasPriceWei can exceed 2⁵³; parse it as a big integer.
gasModeblind means no gas price was available and routes were not penalised for gas; guardrail / prune mean gas-aware solving. Worth reading before a cross-aggregator comparison.
blockNumberThe state block this solve was pinned to.
routeEvery hop of every split: source (the venue label), poolAddress, tokenIn / tokenOut, exact amountIn / amountOut, and bps — that hop's share of the token balance at that point, not of the trade. Group per-venue analytics by source. WRAP hops are the synthetic native⇄wrapped edge and carry a zero pool address.
allowanceTargetThe contract to approve before executing. Null for native sells, which fund via value.
feeFee breakdown; omitted when no fee applies. buyAmount is already net.
warningsAdvisories — see warnings. Omitted when empty.

GET /v1/swap

Identical pricing and identical parameters, plus a required taker. It then firms up any market-maker legs with their makers and encodes the route into a transaction. The response is the quote body flattened at the top level plus the execution fields.

Additional parameterMeaning
takerRequired.The wallet that holds and sends the input; the transaction's from.
userAddressThe real end user, when your taker is a shared settlement contract rather than the swapper. It is passed to the makers that meter per user, so one busy shared taker does not trip a limit for your whole flow. It never changes settlement, and defaults to taker.
deadlineUnix seconds; the transaction reverts after it. Default now + 20 minutes. When the route carries maker legs the effective deadline is capped to the earliest maker expiry.
fundingallowanceHolder (default) or permit2 — how the input is pulled. See below.

Three funding shapes

CaseWhat to do
Native selltransaction.to is the router and transaction.value is your input. No approval of any kind. Sign and send as returned.
ERC-20, allowance holderApprove allowanceTarget once per token from the taker, then send transaction as returned (value is 0). The default.
ERC-20, Permit2One-time approval to canonical Permit2, then sign the returned permit2.eip712 payload and write the 65 signature bytes into the 0x-decoded transaction.data at permit2.signatureOffset before sending. The permit nonce derives from quoteId and is single-use.

Response

Every field from the quote response is present at the top level — not nested under a quote key — with these added:

FieldMeaning
transactionWhat you send. to, data, value (decimal wei — your input for a native sell, otherwise "0"), and from echoing your taker. Where to points depends on the funding shape above.
permit2Present only with funding=permit2: eip712 (typed data ready for eth_signTypedData_v4), hash (the digest — check it equals what you compute), signatureOffset (byte offset into the 0x-decoded transaction.data) and signatureLength (always 65).
warningsDegradations hit while building this one — see warnings. Omitted when empty, and never a reason not to send the transaction.

Gas: an estimate, not a limit

gas is our estimate of how much gas the route will take, and it is returned on both endpoints — price it against gasPriceWei yourself, or read the gasUsd we already computed. That is what you show a user and what you subtract when comparing routes net of cost.

What is not in the response is a gasLimit on transaction, and that is deliberate: our number is a model of the plan, and the limit you sign has to come from the chain as it is at that second. Run eth_estimateGas against transaction before sending. It is also your final validity check — a pool that moved or a maker quote that expired surfaces there as a revert, instead of as a failed transaction you paid for.

The two should land close on an AMM route. A large gap usually means the route contains something the model prices generically, or the chain charges for calldata (Base, Arbitrum, X Layer) — where eth_estimateGas covers execution only and the data-availability component is still yours to add.

Execution semantics

  • The only on-chain guarantee is minBuyAmount. Settle below it and the transaction reverts.
  • On a buy, the recipient receives exactly the requested amount or the transaction reverts; unused input is swept back to the taker. Slippage does not apply — sending slippagePct will not soften that floor.
  • Output above the quote (“positive slippage”) is captured by default, so the recipient receives the quoted amount. Where it goes is a per-integrator setting held against your key — it can be left with the taker or shared instead. Ask if you want a different split.
  • The router's Swap event carries tag = quoteId ‖ hash of your partner id, so on-chain fills reconcile to API responses.

Latency — what to set timeLimitMs to

The budget is stamped when the request arrives, so queueing counts against it. Enforcement is best-effort: direct routes are always evaluated, and deeper search stops at the deadline.

BudgetBehaviour
< 300 msOn-chain venues only — market-maker venues are excluded because their firm-up cannot fit. Deterministic latency; the setting for a solver loop (e.g. 299).
300 msMarket makers participate, and /v1/swap spends the remaining budget firming their quotes (capped at 2 s). Better prices, variable latency.
UnsetThe maximum (5000 ms) is applied — no request runs unbounded, but you should still set your own.

Fees

  • Fees are taken on the buy token, and buyAmount / minBuyAmount are returned net of them — you can show the returned number to your user as-is.
  • Set your own with feeBps + feeRecipient (capped at 1500 bps = 15%). A request-supplied fee splits 85% to your recipient, 15% to the protocol.
  • A fee policy negotiated against your API key overrides anything in the request.
  • On a buy, the routing target is grossed up so your user still receives exactly what they asked for after the fee.

Warnings

warnings[] is omitted when empty. Every code leaves the transaction executable — a warning means the response degraded on the way, never that the calldata is unusable. A request that cannot produce an executable route returns an error instead.

CodeMeaning and handling
RFQ_FIRMUP_FAILEDA market-maker leg won pricing but could not be firmed up; the returned route is an on-chain-only re-solve. Also the usual reason for a slow response with no maker leg visible. Accept it, or set timeLimitMs under 300 to skip makers entirely.
ANGSTROM_ATTESTATION_UNAVAILABLEAn attestation-gated hook leg could not fetch its per-block attestation; same contract as above — an on-chain-only re-solve.
STALE_STATE_RESOLVEDBlocks landed while the transaction was being built and left the original plan below its own floor; it was re-solved against fresher state and certified. The returned numbers are the fresher plan's. Recurring on every call means the pair is moving faster than your firm-up budget.
FEE_ON_TRANSFERA token in the trade charges a transfer fee. The quote already models the measured tax plus a margin, and detail carries the modelled bps per side. Realised output often exceedsthe quote; a tax rate the token owner raises between quote and execution can still revert the fill at the floor — treat that as “re-quote”, not an outage.

Errors

The body is { "error": "…" }. There are no stable machine codes on errors — branch on the status, never on the message text. (Success-path degradations do have stable codes: the warnings above.)

StatusMeaning
400Bad parameters: a missing amount for the mode, identical tokens, a malformed wei string, timeLimitMs out of range, feeBps without feeRecipient, a missing taker on swap, or both venue filters at once.
404An unhosted chain, or no route for this pair and size — routine, usually the size, not an outage. Also returned with a token-specific message when a token fails an on-chain transfer-integrity check: a transfer-gated token (honeypot or blacklist), a tax above the routable cap, any fee-on-transfer token on a buy, or a universal-scope taxed token on the bought side. Those verdicts are re-measured on-chain and the owner can change the rate — key on the message and never cache the verdict.
500We could not serve this one — a chain still coming up after a restart, or an internal failure building the calldata. Retryable.
503We declined to price because our view of that chain is evidently stale. Retryable with backoff.
[ 503 is not 404 ]

A 503 on quote or swap does not mean “no liquidity” — the pair and size are probably fine and the same request is expected to succeed once the indexer catches up. Do not fall back to a cached quote for that pair: a price computed from stale state is wrong rather than merely old, and settling it loses the difference. That is the whole reason this status exists rather than serving you a number.

GET /v1/{chain}/sources

The venue labels routable on a chain — the exact strings route[].source reports and excludeVenues / includeVenues accept. It is the only place those strings are enumerated, so read them from here rather than hard-coding a list that grows without you.

curl -s "https://exen.wraxyn.io/v1/base/sources"

{ "chain_id": 8453, "chain": "base",
  "sources": ["aerodrome-v1", "uniswap-v3", "uniswap-v4:doppler", …] }

{chain} is a path segment here, not the chainId query parameter quote and swap use: a name (ethereum, base, arbitrum, bsc, avalanche, xlayer), a common alias (eth, bnb, avax) or the numeric id. This response is snake_case, unlike quote and swap — a known inconsistency, not a typo in your client.

OpenAPI specification

The API has a full OpenAPI 3.1 specification — every path, parameter, schema and error response, suitable for generating a typed client or loading into Swagger, Redoc, Postman or Insomnia. Everything in it is also written out on this page in prose, so you can integrate without it.

It is not yet published at a public URL. Ask admin@wraxyn.io and we will send you the current file. If the spec and this page ever disagree, the spec is right — it is generated alongside the wire contract and this page follows it.

Worked example — sell 1 ETH for USDT

# 1. Build the transaction (native sell ⇒ no approval needed)
curl -s "https://exen.wraxyn.io/v1/swap\
?chainId=1&mode=sell\
&sellToken=0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee\
&buyToken=0xdac17f958d2ee523a2206206994597c13d831ec7\
&sellAmount=1000000000000000000\
&taker=$TAKER&slippagePct=0.3&timeLimitMs=299" \
  -H "x-api-key: $EXEN_KEY" \
  | jq '{buyAmount, minBuyAmount, gasUsd,
         to: .transaction.to, value: .transaction.value,
         route: [.route[] | {source, bps, amountIn, amountOut}]}'

# 2. eth_estimateGas against .transaction  — the final validity check
# 3. Sign and send it from $TAKER

# ERC-20 sell instead? One extra step first:
#   approve(<allowanceTarget from the response>, <sellAmount>)  from $TAKER

timeLimitMs=299 above is the latency-critical profile: on-chain venues only, deterministic. Drop it (or raise it past 300) to let market makers compete for the fill.