Coingate

Accept crypto with CoinGate

Accept crypto with confidence using everything you need in one platform.

Tutorials

Auto-Converting Crypto Balances With the Convert API

This guide walks through the auto-converting flow, which has one wrinkle worth understanding before you write any code.
Auto-Converting Crypto Balances With the Convert API
Last updated: August 3, 2026 7 min read
VB
Vilius Barbaravičius

Say you accept a range of cryptocurrencies but you think in euros. Or you take payments in various coins and want them all sitting in USDC by the end of the day. Doing that by hand, coin by coin, through a dashboard is exactly the kind of repetitive task an API should be doing for you.

The Convert API moves value between the currency balances on your CoinGate account. Bitcoin balance to euro, euro to USDC, whatever pair is supported. This guide walks through the flow, which has one wrinkle worth understanding before you write any code.

If you are new to the API, get oriented with our crypto payment API integration guide first.


Converting balances by hand in the dashboard? Automate it with the API instead.


The one thing to understand: convert is a two-step flow

Most endpoints do their work in a single call. Conversion does not, and for a good reason.

Crypto prices move. So when you ask to convert, CoinGate first gives you a quote, a locked rate that is valid for a short window. You look at it, and if you like it, you confirm. If you are too slow or change your mind, it expires or you cancel. Nothing moves until you confirm.

So the flow is:

  • Create the conversion. You get back a pending conversion with a quoted amount and an expiry.
  • Confirm it before it expires. Now the balances actually move and the fee is applied.
  • Or cancel it, or just let it expire.

That design protects you. You never get surprised by a rate that shifted between decision and execution.

All calls use the standard header and base URL, with sandbox at https://api-sandbox.coingate.com/v2:

Authorization: Token YOUR_API_TOKEN

Step zero: check the rate

Before converting, you can look up supported pairs and current rates. Three levels of granularity:

# everything
curl "https://api.coingate.com/v2/ledger/conversions/rates" -H "Authorization: Token YOUR_API_TOKEN"

# one base currency
curl "https://api.coingate.com/v2/ledger/conversions/rates/BTC" -H "Authorization: Token YOUR_API_TOKEN"

# a specific pair
curl "https://api.coingate.com/v2/ledger/conversions/rates/BTC/EUR" -H "Authorization: Token YOUR_API_TOKEN"

The response nests rates by currency:

{ "rates": { "BTC": { "EUR": "96048.7" } } }

Not every pair exists, and availability can differ between sandbox and live, so treat the rates response as the source of truth for what is convertible rather than assuming.

Step one: create the conversion

You POST to /v2/ledger/conversions with three things: which account to convert from, which currency to convert to, and how much.

curl --request POST "https://api.coingate.com/v2/ledger/conversions" 
  -H "Authorization: Token YOUR_API_TOKEN" 
  -H "Content-Type: application/x-www-form-urlencoded" 
  --data-urlencode "ledger_account_id=01JRFQJ16HTQTT969F3G81F185" 
  --data-urlencode "quote_currency_id=2" 
  --data-urlencode "base_amount=0.001"

The ledger_account_id is the account you are converting from, which you get from the Ledger API. The quote_currency_id is the numeric ID of the currency you want out, pulled from the currencies endpoint. And base_amount is how much of the source currency to convert, as a string.

Back comes a pending conversion:

{
  "id": "01JWDJNDPFYC04ZCPC9VC568SP",
  "status": "pending",
  "base_amount": "0.001",
  "quote_amount": "81.67",
  "fees": { "conversion_fee": { "amount": "0.00001", "currency": { "symbol": "BTC" } } },
  "actions_required": {
    "confirm": "https://api.coingate.com/v2/ledger/conversions/01JWDJNDPFYC04ZCPC9VC568SP/confirm",
    "cancel":  "https://api.coingate.com/v2/ledger/conversions/01JWDJNDPFYC04ZCPC9VC568SP/cancel"
  },
  "created_at": "2026-05-29T08:31:13.622Z",
  "expires_at": "2026-05-29T08:32:18.615Z"
}

Two fields matter most here. quote_amount is what you will receive, and expires_at is your deadline. In the example that is about a 65-second window. Do not hardcode a timeout, read expires_at. The actions_required object hands you the exact confirm and cancel URLs, so you do not have to build them yourself.

Step two: confirm before it expires

Confirmation is a PATCH, not a POST. Easy to get wrong.

curl --request PATCH "https://api.coingate.com/v2/ledger/conversions/01JWDJNDPFYC04ZCPC9VC568SP/confirm" 
  -H "Authorization: Token YOUR_API_TOKEN"

Now the conversion executes. The status flips to completed, the fee is applied, and your balances update: the source account goes down, the target account goes up. Cancelling is the same call with /cancel, and it releases the quote without moving anything.

If you try to confirm something that already ran, you get a clear 422 telling you the conversion is not in a pending state anymore. Handle that, because in a retry scenario you might confirm twice.

The statuses you will handle

A conversion moves through a small set of states:

  • pending: created, quote locked, waiting on you.
  • completed: confirmed and executed, balances moved.
  • expired: you did not confirm in time.
  • canceled: you called cancel.
  • error: it could not complete.

Your automation only fulfills its purpose on completed. Everything else means the money did not move.

Putting it to work: auto-convert on a schedule

Here is the pattern that makes this useful. Suppose you settle payments with DO_NOT_CONVERT so you keep the original coins, then sweep everything into euro once a day.

import requests

BASE = "https://api.coingate.com/v2"
HEADERS = {"Authorization": "Token YOUR_API_TOKEN"}
EUR_CURRENCY_ID = 2

def convert_account_to_eur(ledger_account_id, amount):
    created = requests.post(f"{BASE}/ledger/conversions", headers=HEADERS, data={
        "ledger_account_id": ledger_account_id,
        "quote_currency_id": EUR_CURRENCY_ID,
        "base_amount": amount,
    }).json()

    # decide based on the locked quote, then confirm
    confirm_url = created["actions_required"]["confirm"]
    result = requests.patch(confirm_url, headers=HEADERS).json()
    return result["status"]  # "completed" if it went through

Run that across your crypto balances on a schedule and you have the working mechanics behind how crypto treasury management works in practice. Read balances with the Ledger API, convert each into your reporting currency, done. To list past conversions for your records, GET /v2/ledger/conversions returns them under an exchange_transactions array.

Two neighbouring pieces fit around this. If the balances you are sweeping arrive through dedicated deposit addresses, our payment channels quickstart covers that side of the setup. And when the money needs to leave the account rather than just change currency, automating crypto payouts via API uses the same confirm-before-anything-moves pattern.

Frequently asked questions

What does the CoinGate Convert API do?

It converts value between the currency balances on your CoinGate account, for example a Bitcoin balance into euro or euro into USDC. It works on your internal ledger balances, not as a public spot exchange.

Why is conversion a two-step create-then-confirm process?

Because crypto rates move. Creating a conversion locks a quote for a short window so you can review the exact amount you will receive before committing. Nothing moves until you confirm, which protects you from rate changes between decision and execution.

How long is a conversion quote valid?

The response includes an expires_at timestamp, roughly a minute in the examples. Read that field rather than assuming a fixed duration, and confirm before it passes or the conversion expires.

Are confirm and cancel POST or PATCH requests?

Both are PATCH requests to the URLs returned in the actions_required object of the created conversion. Using POST will not work.

How do I automate converting crypto to fiat daily?

Settle with DO_NOT_CONVERT to keep the original coins, then run a scheduled job that reads each balance from the Ledger API, creates a conversion to your fiat currency, and confirms it. That gives you automated treasury sweeps.

Wrapping up

The Convert API is simple once the two-step rhythm clicks. Create to lock a quote, confirm to execute, and respect the expiry. Feed it account IDs from the ledger and a target currency, and you can turn a pile of mixed coins into a single tidy balance on a schedule instead of by hand. Confirm and cancel are PATCH, amounts are strings, and only completed means the money actually moved.

Building automated crypto treasury into your stack? Open a CoinGate account.

VB
Vilius Barbaravičius Posted: August 3, 2026
Share article

Accept crypto with CoinGate

Accept crypto with confidence using everything you need in one platform.