Skip to content

Endpoints

GET /v1/quote

Parameters

ParameterTypeRequiredDefaultNotes
chainIdintegeryesn/aChain to price on
tokenInaddressyesn/aToken sold; zero address for native
tokenOutaddressyesn/aToken bought; zero address for native
amountdecimal stringyesn/aSmallest unit of tokenIn for exactIn, of tokenOut for exactOut
tradeTypeexactIn or exactOutnoexactInDirection the amount applies to
sourcesauto, agg, pcsx or agg,pcsxnoautoEngines to consult
recipientaddressconditionaln/aRequired unless sources=agg; also required with includeCalldata
slippageTolerancenumberno0.005Fraction, strictly greater than 0 and strictly less than 0.5
includeCalldatabooleannofalseReturn the encoded transaction inline
maxWaitMsintegernofull budgetUpper bound on how long to wait for engines
swapFeeBpsnumberno0Swap fee to charge on the trade, in basis points, 0–500

Rejections are returned as 400 INVALID_REQUEST with a message naming the offending field:

  • amount that is empty, non-digit, zero, or wider than 256 bits.
  • tokenIn or tokenOut that is not a 20-byte address, or the two being equal.
  • tradeType spelled anything other than exactIn or exactOut. The EXACT_INPUT / EXACT_OUTPUT spelling used inside PancakeSwap X is not accepted here.
  • sources naming an unknown engine. Only agg and pcsx are engine names; aggregator, x, and amm are rejected.
  • slippageTolerance at or outside the open interval (0, 0.5). Both 0 and 0.5 are refused.
  • recipient absent while the selection can reach PancakeSwap X, or absent with includeCalldata=true.
  • tradeType=exactOut combined with sources=agg.
  • swapFeeBps outside the closed interval [0, 500].

Why recipient is conditional. A PancakeSwap X quote is a signed order owned by an address, so the engine cannot price one without knowing whose intent it is. The gateway therefore requires recipient for any selection that could reach PancakeSwap X, which includes the default auto. Under sources=agg the parameter is optional, because the aggregator needs a destination only at encoding time which means that you can supply it then, or ask for it up front with includeCalldata=true. The value is used as both the order's signer and its output recipient, so it must be the wallet you will sign with. Paying out to a third address is not expressible through this API.

Why exactOut excludes the aggregator. The aggregator prices exactIn only. Requesting exactOut with sources=agg leaves no engine able to answer, so it is refused at the edge rather than producing an empty race. With auto, pcsx, or agg,pcsx, an exactOut request is accepted and the aggregator leg is skipped.

maxWaitMs narrows the wait. The gateway clamps the value to its own configured ceiling, so a large number is silently reduced rather than honoured.

Source selection

sourcesEngines consultedWaiting policy
auto (default)every available engineoptimises for latency
aggaggregator onlyfull budget
pcsxPancakeSwap X onlyfull budget
agg,pcsxbothfull budget; waits for both

Naming a single engine is a hard constraint, never a preference. If sources=pcsx and PancakeSwap X is unavailable, the response is 503 NO_ENGINE_AVAILABLE; the aggregator is not substituted.

Response

GET /v1/quote?chainId=56
  &tokenIn=0x0E09FaBB73Bd3Ade0a17ECC321fD13a19e81cE82
  &tokenOut=0x55d398326f99059fF775485246999027B3197955
  &amount=5000000000000000000
  &recipient=0x1111111111111111111111111111111111111111
  &slippageTolerance=0.005
  &sources=agg,pcsx
{
  "candidates": [ { "...": "one entry per engine that answered" } ],
  "best": { "...": "a 1:1 copy of the winning entry" }
}

The top level carries exactly these two keys. quoteId and expiresAt live on each candidate rather than being restated at the top, and best is a byte-for-byte copy of one element of candidates, not a reference and not a reduced projection. Post best onward without reaching back into candidates.

Candidate fields

FieldTypePresenceMeaning
chainIdnumberalwaysChain this quote was priced on
quoteIdstringalwaysGateway identifier, prefixed usq_; shared by every candidate of one request
expiresAtnumberalwaysUnix seconds after which the gateway refuses this quote
inputAmountstringalwaysAmount sold, smallest unit of tokenIn
outputAmountstringalwaysAmount bought, smallest unit of tokenOut
recipientstringwhen suppliedA read back of the request parameter; omitted if none was given
slippageTolerancenumberalwaysFraction this quote was priced with
gasUseEstimatestringaggregator onlyEstimated gas units
gasUseEstimateUsdstringaggregator onlyEstimated gas cost in USD, scaled by 1e18
priceImpactBpsnumberaggregator onlyPrice impact in basis points
aggobjectaggregator onlyThe aggregator's own quote, verbatim
pcsxobjectPancakeSwap X onlyThe signable order, verbatim
calldataobjectwhen requestedEncoded transaction, aggregator candidates only
swapFeeBpsnumberalwaysSwap fee charged on this trade, in basis points; echoes the request parameter. Defaults to 0 if no swapFeeBps was requested
swapFeestringalwaysSwap fee amount for this trade, in the same units as outputAmount. All amounts are rounded down; an amount below 1 unit rounds down to 0. Defaults to 0 if no swapFeeBps was requested

Optional fields are omitted rather than emitted as null.

inputAmount, outputAmount, and the three gas and impact fields are the gateway's normalisation of the underlying payload, and they exist so that two engines with incompatible response shapes can be compared on one axis. They are derived rather than authoritative, i.e. the artefact you sign or send is the agg or pcsx object.

For a PancakeSwap X candidate, outputAmount is the sum of the order's outputs. Gas and price-impact fields are absent because settlement gas is borne by the filler rather than the swapper, and the engine does not report an impact figure (it is an RFQ model).

quoteId is shared across every candidate of a single request, which ties the legs of one fan-out together. It also means the identifier you send to /v1/submit is the one the gateway minted, never the engine's internal identifier, which is visible at pcsx.quoteId and is a different string.

Winner selection

The winner is the quote with the best gross amount for the trade direction:

  • exactIn: the largest outputAmount.
  • exactOut: the smallest inputAmount.

Validity window

expiresAt is the gateway's own deadline, and it is the one that governs whether /v1/calldata and /v1/submit accept the quote. Presenting an expired candidate to either endpoint yields 409 QUOTE_EXPIRED before any upstream call is made.

PancakeSwap X candidates carry a shorter window than aggregator ones, because a signed order has its own on-chain deadline that the gateway will not outlive. That deadline is separate and lives at pcsx.orderInfo.deadline. Both must hold: expiresAt gates admission to the gateway, orderInfo.deadline gates settlement on chain. In practice the order's deadline is the tighter of the two, often by minutes.

There is no renewal endpoint, so expect to re-quote after expiry.

Inline calldata

includeCalldata=true folds the /v1/calldata round trip into the quote. It is honoured only when the winner is an aggregator candidate; a PancakeSwap X winner is never encoded, because such an order is signed and submitted rather than sent. When includeCalldata is used the parameter recipient must be used too.

When honoured, the calldata object is attached to best and to every aggregator entry in candidates, so best remains a true copy. When encoding fails, the quote is still served with calldata absent: a failure to encode does not become a failure to quote. Callers must therefore treat best.calldata as optional even when they asked for it, and fall back to an explicit POST /v1/calldata.

POST /v1/calldata

Encodes an aggregator candidate into sendable transaction calldata.

Request

The body is the candidate itself. Simply post best from a quote response directly (assuming agg field is populated).

Four fields are read:

FieldRequiredRole
aggyesThe route to encode
expiresAtyesChecked against the current time
recipientyesDestination of the output tokens
slippageToleranceyesFraction applied to the minimum received

The informational keys a quote emits (chainId, quoteId, inputAmount, outputAmount, gasUseEstimate, gasUseEstimateUsd, priceImpactBps, and calldata) are accepted and discarded. A candidate pastes in unmodified; nothing needs stripping. Naturally stripping the unread fields will yield a lower byte size in your transmission.

Two of the four fields are meant to be edited between the quote and the encode. Rewriting recipient redirects the swap's output. Rewriting slippageTolerance changes the minimum-received the transaction enforces without re-quoting. Both are honoured as given but still subject to validation.

Integrity guards

The body is validated before anything is encoded, and each refusal names its cause:

  • agg.tradeType must be exactIn. An exactOut payload, or an unrecognised spelling such as EXACT_INPUT, is refused at this stage.
  • The inputAmount values of agg.routes must sum exactly to agg.inputAmount. This catches an edited split, a duplicated route, and an edited total, all of which would otherwise encode a transaction that does not match the quoted price.
  • recipient must be present and parse as an address.
  • slippageTolerance must lie in (0, 0.5).
  • expiresAt must be strictly in the future, else 409 QUOTE_EXPIRED.

Response

{
  "value": "0xde0b6b3a7640000",
  "calldata": "0x2c57e884000000000000000000000000000000000000000000000000068155a43676e000",
  "to": "0x40A1Fe393A7F566F27dF6acE18e6773be844dAfc"
}

The response carries exactly three keys. calldata is ABI-encoded call data for the router at to, and value is the native amount to attach as a hex quantity ("0x0" for an ERC-20 sale). Build the transaction as { to, data: calldata, value }, set gas from gasUseEstimate plus your own headroom, and send it through your own RPC.

to is normally the same address as agg.aggregatorAddress. Treat to as authoritative. To verify that it is a PancakeSwap contract, compare it with the aggregator addresses.

Before sending, ensure the swapper has approved to to spend agg.inputAmount of agg.srcToken. The API performs no allowance check, and an unapproved sale will likely revert on chain.

POST /v1/submit

Submits a signed PancakeSwap X order.

Request

The body is the PancakeSwap X candidate with a signature field set on it:

{
  "chainId": 56,
  "quoteId": "usq_723faa05a80d4fc9a75bb8efdb94db34",
  "pcsx": { "...": "the candidate's pcsx payload, unmodified" },
  "signature": "0xabab…"
}

Three fields are read: chainId, quoteId, and pcsx (specifically pcsx.encodedOrder). expiresAt, inputAmount, outputAmount, recipient, and slippageTolerance are accepted and discarded, so a served candidate pastes in as-is.

quoteId is mandatory here even though the underlying engine treats its own equivalent as optional. It must be the usq_-prefixed identifier from the quote response; absent, null, or empty yields 400 INVALID_REQUEST naming the field.

signature is a 65-byte secp256k1 signature in hex. The 0x prefix is optional and is normalised on the way through. Malformed hex (odd length, non-hex characters, a doubled prefix) is 400 INVALID_REQUEST before any upstream call; a well-formed hex string of the wrong length passes that check and is refused during submission as INVALID_ORDER_BODY.

pcsx.encodedOrder is relayed byte-exact. Do not re-encode or reorder the payload: the order hash, and therefore the validity of your signature, depends on those exact bytes.

Query parameters

ParameterTypeDefaultEffect
strictFirmQuotebooleanfalseEnforce strict firm-quote binding on cosigned orders

strictFirmQuote applies to cosigned (v3, v4) orders only, and v1 orders ignore it. When absent or false, a firm quote that comes back worse than the indicative price falls back to the amounts you signed. When true, a worse-but-within-envelope quote binds at the firm price, and one outside the signed slippage envelope is rejected as QUOTE_EXPIRED rather than silently filled at the indicative price. Set it when you would rather re-quote than accept a price you did not see.

Response

{ "hash": "0x0d00cf2ac94a0f8a51de7005d0ff49739a4b3e7d89b600939b86946b2fa963ff" }

The single key is the order hash, which is the handle for /v1/status. It equals pcsx.orderHash from the quote, so it can be predicted before submission and used to reconcile a submission whose response was lost.

A successful submit means the order is persisted and advertised to fillers. It does not mean the swap has happened. Poll /v1/status until the order reaches a terminal state.

Submission-specific failures

CodeHTTPCause
QUOTE_NOT_FOUND404No quote is held for that quoteId
QUOTE_EXPIRED409The quote's own window closed before submission
NOT_A_PCSX_QUOTE400The quoteId belongs to an aggregator quote, which cannot become an order
STORE_UNAVAILABLE503The gateway's quote store is unreachable, so the identifier cannot be resolved

QUOTE_NOT_FOUND and QUOTE_EXPIRED describe different states. The first means the identifier was never recorded or has already aged out of the store; the second means the record exists and has lapsed. Both are resolved by re-quoting, and neither is retryable as-is.

GET /v1/status/{chainId}/{orderHash}

Reads the state of a submitted PancakeSwap X order. Aggregator swaps are ordinary transactions and are tracked through your own RPC, not here.

orderHash must be 64 hex digits, 0x prefix optional. Anything else is 400 INVALID_REQUEST and is refused.

Response

{
  "status": "FILLED",
  "orderHash": "0x0d00cf2ac94a0f8a51de7005d0ff49739a4b3e7d89b600939b86946b2fa963ff",
  "chainId": 42161,
  "offerer": "0x45f2a9b4c55dd94ec601746b7ea187d81b91215d",
  "input": {
    "token": "0x912ce59144191c1204e64559fe8253a0e49e6548",
    "startAmount": "9328194992407255580000",
    "endAmount": "9328194992407255580000"
  },
  "outputs": [
    {
      "token": "0xaf88d065e77c8cc2239327c5edb3a432268e5831",
      "startAmount": "727412056",
      "endAmount": "723774995",
      "takingAmount": "727381748",
      "recipient": "0x45f2a9b4c55dd94ec601746b7ea187d81b91215d"
    }
  ],
  "deadline": 1785330199,
  "decayStartTime": 1785330067,
  "decayEndTime": 1785330187,
  "filler": "0xf00000003d31d4ab730a8e269ae547f8f76996ba",
  "transactionHash": "0x450908eaacb747e5fc08dea3aca596a682a1a6433549166ebfb2b3442cda30d4",
  "blockNumber": 488954021,
  "createdAt": 1785330014,
  "updatedAt": 1785331040
}
FieldPresenceMeaning
statusalwaysSee the state table below
orderHashalwaysRead-back of the path parameter, lower-case
chainIdalwaysChain the order was submitted on
offereralwaysAddress that signed the order
inputalwaysToken sold with its decay bounds
outputsalwaysOne entry per output; takingAmount is the amount actually taken, present once known
deadlinealwaysUnix seconds after which the order can no longer be filled
decayStartTime, decayEndTimetime-anchored ordersBounds of the Dutch decay window; absent on block-anchored (v3, v4) orders
filleronce knownAddress that filled or is filling
transactionHash, blockNumberonce filledSettlement transaction
createdAt, updatedAtalwaysUnix seconds

States

statusTerminalMeaning
OPENnoFillable; no fill in flight
PENDINGnoA fill is in flight
FILLEDyesSettled; transactionHash and blockNumber are populated
EXPIREDyesThe deadline passed unfilled
CANCELLEDyesCancelled before fill

Stop polling on any terminal state. A reasonable cadence is one request every one to two seconds from submission until deadline plus a small margin; an order that is still OPEN past its deadline will transition to EXPIRED shortly after.

404 ORDER_NOT_FOUND is returned for a well-formed hash with no matching order. Immediately after a submit this is normally a propagation race rather than a lost order; retry briefly before treating it as final.