curl --request POST \
--url https://api.portalhq.io/api/v3/clients/me/integrations/cctp/quote \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"sourceChain": "eip155:84532",
"destinationChain": "eip155:43113",
"amount": "0.09",
"speed": "auto"
}
'import requests
url = "https://api.portalhq.io/api/v3/clients/me/integrations/cctp/quote"
payload = {
"sourceChain": "eip155:84532",
"destinationChain": "eip155:43113",
"amount": "0.09",
"speed": "auto"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
sourceChain: 'eip155:84532',
destinationChain: 'eip155:43113',
amount: '0.09',
speed: 'auto'
})
};
fetch('https://api.portalhq.io/api/v3/clients/me/integrations/cctp/quote', 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.portalhq.io/api/v3/clients/me/integrations/cctp/quote",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'sourceChain' => 'eip155:84532',
'destinationChain' => 'eip155:43113',
'amount' => '0.09',
'speed' => 'auto'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.portalhq.io/api/v3/clients/me/integrations/cctp/quote"
payload := strings.NewReader("{\n \"sourceChain\": \"eip155:84532\",\n \"destinationChain\": \"eip155:43113\",\n \"amount\": \"0.09\",\n \"speed\": \"auto\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.portalhq.io/api/v3/clients/me/integrations/cctp/quote")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"sourceChain\": \"eip155:84532\",\n \"destinationChain\": \"eip155:43113\",\n \"amount\": \"0.09\",\n \"speed\": \"auto\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.portalhq.io/api/v3/clients/me/integrations/cctp/quote")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"sourceChain\": \"eip155:84532\",\n \"destinationChain\": \"eip155:43113\",\n \"amount\": \"0.09\",\n \"speed\": \"auto\"\n}"
response = http.request(request)
puts response.read_body{
"data": {
"sourceChain": "eip155:84532",
"destinationChain": "eip155:43113",
"amount": "0.09",
"amountRaw": "90000",
"speed": "fast",
"minFinalityThreshold": 1000,
"fees": {
"protocol": "0.000012",
"forwarding": "0.054181",
"maxFee": "0.054193",
"custodian": "0"
},
"totalDebit": "0.09",
"amountOut": "0.035807",
"minAmount": "0.05419",
"sourceGas": {
"payer": "user",
"estimatedWei": "1237440000000"
},
"estimatedSeconds": 8,
"sender": "0xa7f0c09f250b7f9cc73068c02ceb296a013c0c0c",
"recipient": "0xa7f0c09f250b7f9cc73068c02ceb296a013c0c0c",
"warnings": [],
"ready": true,
"checks": [
{
"id": "ROUTE_SUPPORTED",
"ok": true
},
{
"id": "AMOUNT_ABOVE_MINIMUM",
"ok": true
},
{
"id": "BALANCE",
"ok": true
},
{
"id": "RECIPIENT_ALLOWED",
"ok": true
},
{
"id": "SOURCE_GAS",
"ok": true
}
]
}
}{
"error": "Send exactly one of amount or amountOut",
"id": "INVALID_AMOUNT"
}{
"error": "<string>"
}{
"error": "<string>",
"details": {
"retryAfterSeconds": 123
},
"id": "CCTP_UPSTREAM_UNAVAILABLE"
}Quote a CCTP transfer
Returns Circle’s fees, your custodian fee, the exact amount the
recipient receives, the total that leaves the wallet (totalDebit) and the
preflight checks for a transfer. Nothing is stored. A quote that can’t go ahead
still returns 200, with ready: false and a message and fix on each failed
check.
For Account Abstraction clients on an EVM source, the SOURCE_GAS and
SPONSORSHIP checks are null on a quote, because the user operation is only
built when you create the transfer. For a Solana source, both are known on the
quote.
curl --request POST \
--url https://api.portalhq.io/api/v3/clients/me/integrations/cctp/quote \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"sourceChain": "eip155:84532",
"destinationChain": "eip155:43113",
"amount": "0.09",
"speed": "auto"
}
'import requests
url = "https://api.portalhq.io/api/v3/clients/me/integrations/cctp/quote"
payload = {
"sourceChain": "eip155:84532",
"destinationChain": "eip155:43113",
"amount": "0.09",
"speed": "auto"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
sourceChain: 'eip155:84532',
destinationChain: 'eip155:43113',
amount: '0.09',
speed: 'auto'
})
};
fetch('https://api.portalhq.io/api/v3/clients/me/integrations/cctp/quote', 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.portalhq.io/api/v3/clients/me/integrations/cctp/quote",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'sourceChain' => 'eip155:84532',
'destinationChain' => 'eip155:43113',
'amount' => '0.09',
'speed' => 'auto'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.portalhq.io/api/v3/clients/me/integrations/cctp/quote"
payload := strings.NewReader("{\n \"sourceChain\": \"eip155:84532\",\n \"destinationChain\": \"eip155:43113\",\n \"amount\": \"0.09\",\n \"speed\": \"auto\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.portalhq.io/api/v3/clients/me/integrations/cctp/quote")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"sourceChain\": \"eip155:84532\",\n \"destinationChain\": \"eip155:43113\",\n \"amount\": \"0.09\",\n \"speed\": \"auto\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.portalhq.io/api/v3/clients/me/integrations/cctp/quote")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"sourceChain\": \"eip155:84532\",\n \"destinationChain\": \"eip155:43113\",\n \"amount\": \"0.09\",\n \"speed\": \"auto\"\n}"
response = http.request(request)
puts response.read_body{
"data": {
"sourceChain": "eip155:84532",
"destinationChain": "eip155:43113",
"amount": "0.09",
"amountRaw": "90000",
"speed": "fast",
"minFinalityThreshold": 1000,
"fees": {
"protocol": "0.000012",
"forwarding": "0.054181",
"maxFee": "0.054193",
"custodian": "0"
},
"totalDebit": "0.09",
"amountOut": "0.035807",
"minAmount": "0.05419",
"sourceGas": {
"payer": "user",
"estimatedWei": "1237440000000"
},
"estimatedSeconds": 8,
"sender": "0xa7f0c09f250b7f9cc73068c02ceb296a013c0c0c",
"recipient": "0xa7f0c09f250b7f9cc73068c02ceb296a013c0c0c",
"warnings": [],
"ready": true,
"checks": [
{
"id": "ROUTE_SUPPORTED",
"ok": true
},
{
"id": "AMOUNT_ABOVE_MINIMUM",
"ok": true
},
{
"id": "BALANCE",
"ok": true
},
{
"id": "RECIPIENT_ALLOWED",
"ok": true
},
{
"id": "SOURCE_GAS",
"ok": true
}
]
}
}{
"error": "Send exactly one of amount or amountOut",
"id": "INVALID_AMOUNT"
}{
"error": "<string>"
}{
"error": "<string>",
"details": {
"retryAfterSeconds": 123
},
"id": "CCTP_UPSTREAM_UNAVAILABLE"
}Authorizations
Client API Key or Client Session Token (CST). Pass as a Bearer token in the Authorization header.
Body
- Option 1
- Option 2
Send exactly one of amount or amountOut. Unknown fields are rejected.
CAIP-2 id of the chain to send from, an EVM chain or Solana.
^(eip155:\d+|solana:[1-9A-HJ-NP-Za-km-z]{32})$"eip155:11155111"
CAIP-2 id of the chain to send to, an EVM chain or Solana.
^(eip155:\d+|solana:[1-9A-HJ-NP-Za-km-z]{32})$"eip155:84532"
USDC to burn on the source chain, as a decimal string with up to 6 decimals (not base units).
^\d+(\.\d{1,6})?$"0.1"
USDC the recipient must receive (exact-out). Portal works out the amount to burn so the recipient gets at least this after fees (exactly this when Circle charges the full maxFee, as it does in practice).
^\d+(\.\d{1,6})?$"0.045735"
auto and fast use Fast when the source chain offers it, otherwise Standard. standard always uses Standard.
auto, fast, standard Address to receive the USDC, validated against the destination chain. An EVM address for an EVM destination, or a Solana wallet address (not a token account) for a Solana destination, where Circle mints into the wallet's USDC token account. Defaults to the client's own address on the destination chain. Any other address must be on the environment's destination allowlist.
1 - 64Response
Quote
When ROUTE_SUPPORTED fails, only sourceChain, destinationChain, ready and checks are returned.
Show child attributes
Show child attributes
Was this page helpful?