Upgrading to Swap API v2
0x Swap API has undergone a major upgrade that introduces new features that require a new integration. This new version offers:
- improved swap pricing, especially on large trades
- a new set of 0x smart contracts that offer secure upgradability
- increased security via the Permit2 contract or AllowanceHolder contract for allowances
- improved monetization features
- enhanced quote validation to provide accurate gas estimates without a user balance or allowance set
- improved error handling
- new buy/sell tax support
- expanded token and chain coverage
β‘οΈ Quicklinks
- Demo app code
- Permit2 headless example
- AllowanceHolder headless example
- Try the new Swap TypeScript SDK which supports 0x API v2 Swap and Gasless endpoints
API Referencesβ
Find the latest API references here.
Summary of Design Changesβ
This section showcases example requests and responses for both the Swap API v1 and v2 endpoints to illustrate updates in the shape of the query and responses made during the API upgrade:
Swap API v1
Swap API v1/quote
Request
curl --request GET
--url https://api.0x.org/swap/v1/quote?buyToken=0x6B175474E89094C44Da98b954EedeAC495271d0F&sellToken=0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE&sellAmount=100000&takerAddress=0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045&slippagePercentage=0.03&feeRecipient=0xa8aac589a67ecfade31efde49a062cc21d68a64e&buyTokenFeePercentage=0.1&feeRecipientTradeSurplus=0xa8aac589a67ecfade31efde49a062cc21d68a64e
--header '0x-api-key: YOUR_API_KEY'
Response
{
"chainId": 1,
"price": "3004.35342",
"grossPrice": "3008.73112",
"estimatedPriceImpact": "0.4902",
"value": "100000",
"gasPrice": "2457320000",
"gas": "358839",
"estimatedGas": "358839",
"protocolFee": "0",
"minimumProtocolFee": "0",
"buyTokenAddress": "0x6b175474e89094c44da98b954eedeac495271d0f",
"buyAmount": "300435342",
"grossBuyAmount": "300873112",
"sellTokenAddress": "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
"sellAmount": "100000",
"grossSellAmount": "100000",
"sources": [
{
"name": "0x",
"proportion": "0"
},
{
"name": "Uniswap",
"proportion": "0"
},
{"..."}
],
"allowanceTarget": "0x0000000000000000000000000000000000000000",
"sellTokenToEthRate": "1",
"buyTokenToEthRate": "3023.5504768532695617",
"to": "0xdef1c0ded9bec7f1a1670819833240f027b25eff",
"from": "0xd8da6bf26964af9d7eed9e03e53415d37aa96045",
"data": "0x415565b0000000000000000000000000eeeeeeeee....000000000000000000000000000000e7be0bf798e96f437b0d6e8",
"decodedUniqueId": "0x0e7be0bf798e96f437b0d6e8",
"guaranteedPrice": "2914.09148",
"orders": [
{
"type": 0,
"source": "Uniswap_V2",
"makerToken": "0x6b175474e89094c44da98b954eedeac495271d0f",
"takerToken": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2",
"makerAmount": "300873112",
"takerAmount": "100000",
"fillData": {
"tokenAddressPath": [
"0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2",
"0x6b175474e89094c44da98b954eedeac495271d0f"
],
"router": "0xf164fc0ec4e93095b804a4795bbe1e041497b92a"
},
"fill": {
"input": "100000",
"output": "300873112",
"adjustedOutput": "1",
"gas": 115000
}
}
],
"fees": {
"zeroExFee": {
"feeType": "volume",
"feeToken": "0x6b175474e89094c44da98b954eedeac495271d0f",
"feeAmount": "437770",
"billingType": "on-chain"
}
},
"auxiliaryChainData": {}
}
Swap API v2
Swap API v2 /quote
(Permit2)
Request
curl --request GET
--url https://api.0x.org/swap/permit2/quote?chainId=1&buyToken=0x6B175474E89094C44Da98b954EedeAC495271d0F&sellToken=0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE&sellAmount=100000&taker=0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045&swapFeeBps=10&swapFeeRecipient=0xa8aac589a67ecfade31efde49a062cc21d68a64e&swapFeeToken=0x6B175474E89094C44Da98b954EedeAC495271d0F&tradeSurplusRecipient=0xa8aac589a67ecfade31efde49a062cc21d68a64e&slippageBps=100
--header '0x-api-key: YOUR_API_KEY'
--header '0x-version: v2'
Response
{
"blockNumber": "20264686",
"buyAmount": "300409869",
"buyToken": "0x6b175474e89094c44da98b954eedeac495271d0f",
"fees": {
"integratorFee": {
"amount": "301163",
"token": "0x6b175474e89094c44da98b954eedeac495271d0f",
"type": "volume"
},
"zeroExFee": {
"amount": "451744",
"token": "0x6b175474e89094c44da98b954eedeac495271d0f",
"type": "volume"
},
"gasFee": null
},
"issues": {
"allowance": null,
"balance": null,
"simulationIncomplete": false,
"invalidSourcesPassed": []
},
"liquidityAvailable": true,
"minBuyAmount": "297405770",
"permit2": null,
"route": {
"fills": [
{
"from": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2",
"to": "0x6b175474e89094c44da98b954eedeac495271d0f",
"source": "SushiSwap",
"proportionBps": "10000"
}
],
"tokens": [
{
"address": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2",
"symbol": "WETH"
},
{
"address": "0x6b175474e89094c44da98b954eedeac495271d0f",
"symbol": "DAI"
}
]
},
"sellAmount": "100000",
"sellToken": "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
"totalNetworkFee": "415281807360000",
"transaction": {
"to": "0x7f6cee965959295cc64d0e6c00d99d6532d8e86b",
"data": "0x1fff991f000000000000000000000...000000000000",
"gas": "221184",
"gasPrice": "1877540000",
"value": "100000"
}
}
Swap API v2/quote
(AllowanceHolder)
Request
curl --request GET
--url https://api.0x.org/swap/allowance-holder/quote?chainId=1&buyToken=0x6B175474E89094C44Da98b954EedeAC495271d0F&sellToken=0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE&sellAmount=100000&taker=0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045&swapFeeBps=10&swapFeeRecipient=0xa8aac589a67ecfade31efde49a062cc21d68a64e&swapFeeToken=0x6B175474E89094C44Da98b954EedeAC495271d0F&tradeSurplusRecipient=0xa8aac589a67ecfade31efde49a062cc21d68a64e&slippageBps=100
--header '0x-api-key: YOUR_API_KEY'
--header '0x-version: v2'
Response
{
"blockNumber": "20264713",
"buyAmount": "300433569",
"buyToken": "0x6b175474e89094c44da98b954eedeac495271d0f",
"fees": {
"integratorFee": {
"amount": "301187",
"token": "0x6b175474e89094c44da98b954eedeac495271d0f",
"type": "volume"
},
"zeroExFee": {
"amount": "451780",
"token": "0x6b175474e89094c44da98b954eedeac495271d0f",
"type": "volume"
},
"gasFee": null
},
"issues": {
"allowance": null,
"balance": null,
"simulationIncomplete": false,
"invalidSourcesPassed": []
},
"liquidityAvailable": true,
"minBuyAmount": "297429233",
"route": {
"fills": [
{
"from": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2",
"to": "0x6b175474e89094c44da98b954eedeac495271d0f",
"source": "Uniswap_V2",
"proportionBps": "10000"
}
],
"tokens": [
{
"address": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2",
"symbol": "WETH"
},
{
"address": "0x6b175474e89094c44da98b954eedeac495271d0f",
"symbol": "DAI"
}
]
},
"sellAmount": "100000",
"sellToken": "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
"totalNetworkFee": "783821981250000",
"transaction": {
"to": "0x0000000000001ff3684f28c67538d4d072c22734",
"data": "0x2213bc0b0000000000000000000...00000000",
"gas": "233541",
"gasPrice": "3356250000",
"value": "100000"
}
}
Things to Noteβ
Old (v1):
curl --request GET
https://polygon.api.0x.org/swap/v1/quote&buyTokenAddress=0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270&sellTokenAddress=0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174&sellAmount=10000000&takerAddress=0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045
--header '0x-api-key: YOUR_API_KEY'
New (v2):
curl --request GET
https://api.0x.org/swap/permit2/quote?chainId=137&buyToken=0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270&sellToken=0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174&sellAmount=10000000&taker=0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045
--header '0x-api-key: YOUR_API_KEY'
--header '0x-version: v2'
Updated URLsβ
Unified Endpoint and Chain Designationβ
The chain is no longer designated by the endpoint URL. All chains now use the same api.0x.org
endpoint, with the chain set via the new chainId
query parameter:
- Old:
https://polygon.api.0x.org/swap/v1/quote
- New:
https://api.0x.org/swap/permit2/quote?chainId=137
API Version Updateβ
The API version is no longer set in the endpoint URL. In v2, the API version is a required header parameter.
const headers = {
'0x-api-key': '[api-key]',
'0x-version': 'v2', // API version is a required header parameter
};
Updated Parameter Namesβ
Note the following parameter names have been updated in v2. See the API references for more parameter details:
takerAddress
has been renamed totaker
sellTokenAddress
has been renamed tosellToken
buyTokenAddress
has been renamed tobuyToken
Deprecated Parameterβ
The buyAmount
parameter has been deprecated in favor of purely sellAmount
for more deterministic and user-friendly behavior. Using sellAmount
ensures any unused tokens are refunded, while buyAmount
can result in over-purchasing due to slippage and inconsistencies across liquidity sources. Additionally, some sources (e.g., Kyber) do not support quoting by buyAmount
, reducing available liquidity. By transitioning to sellAmount
, we align with industry trends and offer more predictable execution for users.
Enhanced Securityβ
0x V2 enhances security by moving away from unlimited approvals and now sets allowances per trade. You can choose between using Permit2 (/swap/permit2
) or AllowanceHolder (/swap/allowance-holder
) when making a swap request. Read more about the difference between using Permit2 and AllowanceHolder for Swap API.
- Old:
https://api.0x.org/swap/v1/quote
- New:
https://api.0x.org/swap/permit2/quote
orhttps://api.0x.org/swap/allowance-holder/quote
Token Allowancesβ
For token allowances, the allowance target will no longer be the 0x Exchange Proxy, but rather either the Permit2 contract or AllowanceHolder contracts, depending on your preferred token allowance path.
Permit2 Transactionsβ
For transactions using Permit2, you will need to sign a Permit2 EIP-712 message from the /quote
response and append the signature length and signature data to calldata before submitting the transaction to the network. You can find a guide to do so here.
Changes in API Responsesβ
Liquidity Availabilityβ
There is a new liquidityAvailable
field that validates the availability of liquidity for a price or quote request. All other parameters will only be returned when this is returned as true
.
Validation Changesβ
We have removed the skipValidation
field and will always validate the quote. We will always attempt to return a valid quote even when the taker has an insufficient balance or doesnβt have a token allowance set in the Swap API.
Issues Objectβ
To help provide developers a smooth build experience, 0x API v2 will do as much validation as it can and report all issues it finds in the new issues
object. This object returns a list of potential validation issues detected with the quote. In rare cases where we are unable to validate the quote, weβll return true
in issues.simulationIncomplete
. Learn more about the new issues
object and how you can use it to provide a better experience for your users.
Note the following about the issues
object for Price and Quote:
Price
allowance
: If this field is notnull
, prompt the user to set the allowance onissues.allowance.spender
balance
: If this field is notnull
, do not proceed to get aquote
.simulationIncomplete
: This field can be ignored forprice
since when calling price means aren't close to submitting a transaction (versus calling quote). TypicallysimulationIncomplete: true
won't occur if thetaker
address is set and thetaker
has a sufficient balance of the sell token.
Quote
allowance
: This field should not appear if thequote
is sent after the token allowance is set.balance
: This field will not be returned if the taker has sufficient balance when thequote
request is sent.simulationIncomplete: true
: This field will not be returned if the taker has sufficient balance when thequote
request is sent.
Swap TypeScript SDKβ
Starting with v2, we offer a TypeScript client for interacting with the 0x API. The @0x/swap-ts-sdk currently supports the "Swap" and "Gasless" endpoints of the 0x API v2. Learn more about how to use it here.
Detailed Migration Guideβ
The following section overviews of how to update Swap API price and quote to handle the new v2 endpoints whether you are using Permit2 or using AllowanceHolder
Using Permit2β
β‘οΈ See these steps in action in the Permit2 headless example
Step 0. Get 0x API keyβ
Step 1. Get indicative priceβ
- Call
/swap/permit2/price
(code)- Build required price params
- Add
chainId
as new param -
takerAddress
has changed totaker
- Add
- In the response:
sellTokenAddress
changed tosellToken
,buyTokenAddress
changed tobuyToken
- Build required price params
Step 2. Set token allowanceβ
- Check if
taker
needs to set an allowance for Permit2. If needed, set token allowance (code)- Use the info returned by
issues.allowance
returned by/price
(iftaker
is set) and/quote
which contains details of allowances that thetaker
must set for the order to execute successfully
- Use the info returned by
Step 3. Get firm quoteβ
- Call
/swap/permit2/quote
(code)- Build required quote params (price params +
taker
address)- Add
chainId
as new param -
takerAddress
has changed totaker
- Add
- In the response:
sellTokenAddress
changed tosellToken
,buyTokenAddress
changed tobuyToken
(code)
- Build required quote params (price params +
Step 4. Sign Permit2 EIP-712 messageβ
- Sign the Permit2 EIP-712 message from
/quote
response-
signature = await signTypedDataAsync(quote.permit2.eip712);
(code) - Make sure
PriceResponse
andQuoteResponse
types are up-to-date
-
Step 5. Append signature length and signature data to transaction.dataβ
- Append the signature length and signature data to the
transaction.data
(code)
Step 6. Submit transaction with Permit2 signatureβ
- Submit the transaction with Permit2 signature
- Use
sendTransaction({account, gas, txOptions, data, value, chain)
(code) - Note that the
/quote
response structure has changed from v1 to v2, specificallyto
,gas
,data
,value
,gasPrice
β have moved underquoteResponse.transaction.to
,quoteResponse.transaction.gas
,quoteResponse.transaction.data
,quoteResponse.transaction.value
,quoteResponse.transaction.gasPrice
(code)
- Use
Using AllowanceHolderβ
β‘οΈ See these steps in action in the AllowanceHolder headless example
Step 0. Get 0x API keyβ
Step 1. Get indicative priceβ
- Call
/swap/allowance-holder/price
(code)- Build required price params
- Add
chainId
as new param -
takerAddress
has changed totaker
- Add
- In the response:
sellTokenAddress
changed tosellToken
,buyTokenAddress
changed tobuyToken
- Build required price params
Step 2. Set token allowanceβ
- Check if
taker
needs to set an allowance for AllowanceHolder. If needed, set token allowance (code)- Use the info returned by
issues.allowance
returned by/price
(iftaker
is set) and/quote
which contains details of allowances that thetaker
must set for the order to execute successfully
- Use the info returned by
Step 3. Get firm quoteβ
- Call
/swap/allowance-holder/quote
(code)- Build required quote params (price params +
taker
address)- Add
chainId
as new param -
takerAddress
has changed totaker
- Add
- In the response:
sellTokenAddress
changed tosellToken
,buyTokenAddress
changed tobuyToken
(code)
- Build required quote params (price params +
Step 4. Submit transactionβ
- Submit the transaction using
sendTransaction()
(code)- Note that the
/quote
response structure has changed from v1 to v2, specificallyto
,gas
,data
,value
,gasPrice
β have moved underquoteResponse.transaction.to
,quoteResponse.transaction.gas
,quoteResponse.transaction.data
,quoteResponse.transaction.value
,quoteResponse.transaction.gasPrice
(code)
- Note that the
Migration FAQβ
Should I use AllowanceHolder or Permit2 for my meta-aggregator project?
The decision when choosing between Permit2 or AllowanceHolder boils down to mainly UX and integration type.
AllowanceHolder is ideal for single-signature use cases and provides a consistent user experience when aggregating across multiple sources. It also closely resembles the 0x Swap v1 integration, making it quicker to implement.
Permit2 is a universal standard that allows users to share token approvals across smart contracts (i.e. if users have an infinite allowance via another app, no reset is needed). However, Permit2 requires two signatures per trade (one for approval, one for the trade), which may impact user experience.
Evaluate your projectβs needs and the desired user experience to make the best decision.
How can I find the latest address for the AllowanceHolder contract, and how often will it be updated?
The AllowanceHolder contract is deployed at different addresses based on the chainβs latest supported EVM hardfork. You can find the most recent addresses in the list of deployed addresses..
The AllowanceHolder address only changes when a chain adopts a higher hardfork.
In the AllowanceHolder flow, will I ever approve a different contract than where the swap transaction occurs?
No, in the AllowanceHolder flow, the allowance will be set on the AllowanceHolder contract, and the transaction.to
field will also point to the AllowanceHolder. The only exception is when the sell token is ETH, as no allowance is needed for ETH transactions. In that case, the transaction.to
field will be the address of the most recent Settler contract.
You will never need to approve one contract and send swap calldata to another. For more information, refer to the 0x Settler documentation.
If an allowance is needed when using AllowanceHolder, which contract will issues.allowance.spender
return back?
The AllowanceHolder contract will be returned.
Can I skip validation for the /quote
endpoint by leaving the taker
parameter blank?
No, the taker
parameter is required in v2 for /quote
. It enables us to return calldata and any issues that may have caused a failure during quote validation.
Will the /price
endpoint include RFQ if I leave the taker
parameter blank?
Yes, RFQ will be included in the response from the /price
endpoint even if the taker
parameter is left blank. The taker
parameter is optional for this endpoint.