> ## Documentation Index
> Fetch the complete documentation index at: https://docs.whitebit.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Convert

> Instantly convert between currencies at a quoted rate without placing orders on the orderbook.

export const RegionBaseUrl = ({className = "", showBaseUrl = true, protocol = "https"}) => {
  const {useState, useEffect, useRef} = React;
  const EU_ENABLED = false;
  const [region, setRegionState] = useState(() => {
    if (!EU_ENABLED) return "com";
    if (typeof window !== 'undefined') {
      return localStorage.getItem("api-region-preference") || "com";
    }
    return "com";
  });
  const [mounted, setMounted] = useState(false);
  const observerRef = useRef(null);
  const isSyncingRef = useRef(false);
  const updateAllContentOnPage = targetRegion => {
    try {
      const domainFrom = targetRegion === "eu" ? "whitebit.com" : "whitebit.eu";
      const domainTo = targetRegion === "eu" ? "whitebit.eu" : "whitebit.com";
      const links = document.querySelectorAll('a');
      links.forEach(link => {
        let href = link.getAttribute('href');
        if (href && href.includes(domainFrom)) {
          link.setAttribute('href', href.replace(domainFrom, domainTo));
        }
      });
      const walker = document.createTreeWalker(document.body, NodeFilter.SHOW_TEXT, {
        acceptNode: node => {
          if (node.parentElement?.closest('.region-toggle-component')) {
            return NodeFilter.FILTER_REJECT;
          }
          return node.textContent.includes(domainFrom) ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_REJECT;
        }
      });
      let currentNode;
      while (currentNode = walker.nextNode()) {
        currentNode.textContent = currentNode.textContent.replace(new RegExp(domainFrom, 'g'), domainTo);
      }
      console.log(`[RegionSync] Global content updated to ${domainTo}`);
    } catch (e) {
      console.error("[RegionSync] Error updating content:", e);
    }
  };
  const updateRegion = (newRegion, source) => {
    if (region === newRegion) return;
    console.log(`[RegionBaseUrl] Updating to "${newRegion}" (Source: ${source})`);
    if (source === 'observer') {
      isSyncingRef.current = true;
      setTimeout(() => isSyncingRef.current = false, 1000);
    }
    setRegionState(newRegion);
    localStorage.setItem("api-region-preference", newRegion);
    updateAllContentOnPage(newRegion);
    if (source === 'user-click') {
      window.dispatchEvent(new CustomEvent("regionChange", {
        detail: newRegion
      }));
      attemptToUpdateNativeDropdown(newRegion, 0);
      setTimeout(() => attemptToUpdateNativeDropdown(newRegion, 1), 500);
      setTimeout(() => attemptToUpdateNativeDropdown(newRegion, 2), 1500);
    }
  };
  const attemptToUpdateNativeDropdown = (targetRegion, attempt) => {
    if (isSyncingRef.current) return;
    try {
      const targetUrl = targetRegion === "eu" ? "https://whitebit.eu" : "https://whitebit.com";
      const targetDesc = targetRegion === "eu" ? "EU Server" : "Production Server";
      const selects = document.querySelectorAll('select');
      for (const select of selects) {
        if (select.innerHTML.includes('whitebit.com') || select.innerHTML.includes('whitebit.eu')) {
          select.value = targetUrl;
          select.dispatchEvent(new Event('change', {
            bubbles: true
          }));
          return;
        }
      }
      const buttons = Array.from(document.querySelectorAll('button, [role="combobox"]'));
      const serverSelector = buttons.find(btn => {
        if (btn.closest('a') || btn.closest('[class*="card"]') || btn.closest('nav')) {
          return false;
        }
        const txt = btn.textContent || "";
        const isServerDropdown = (txt.includes('Production Server') || txt.includes('EU Server') || txt.includes('WhiteBIT Global Server') || txt.includes('WhiteBIT EU Server')) && !txt.includes('Run') && !txt.includes('Send') || btn.getAttribute('role') === 'combobox';
        return isServerDropdown;
      });
      if (serverSelector) {
        const currentText = serverSelector.textContent || "";
        if (currentText.includes(targetDesc)) return;
        serverSelector.click();
        setTimeout(() => {
          const options = document.querySelectorAll('[role="option"], li, button');
          for (const opt of options) {
            const optText = opt.textContent || "";
            if (optText.includes(targetDesc) || optText.includes(targetUrl)) {
              opt.click();
              return;
            }
          }
        }, 100);
      }
    } catch (e) {
      console.error("[Sync] Error:", e);
    }
  };
  useEffect(() => {
    if (!EU_ENABLED) return;
    setMounted(true);
    updateAllContentOnPage(region);
    const handleStorageChange = e => {
      if (e.key === "api-region-preference" && e.newValue) {
        updateRegion(e.newValue, 'storage');
      }
    };
    const handleRegionChange = e => {
      if (e.detail !== region) {
        updateRegion(e.detail, 'event');
      }
    };
    window.addEventListener("storage", handleStorageChange);
    window.addEventListener("regionChange", handleRegionChange);
    observerRef.current = new MutationObserver(mutations => {
      if (isSyncingRef.current) return;
      updateAllContentOnPage(region);
      for (const mutation of mutations) {
        if (mutation.type !== 'childList' && mutation.type !== 'characterData') continue;
        const target = mutation.target;
        const el = target.nodeType === Node.TEXT_NODE ? target.parentElement : target;
        if (el && (el.getAttribute('role') === 'option' || el.closest('[role="listbox"]'))) continue;
        const text = target.textContent || "";
        if (text.includes('WhiteBIT EU Server') || text.includes('https://whitebit.eu') && text.includes('Server')) {
          if (el && el.tagName !== 'A' && !el.closest('.region-toggle-component')) {
            if (region !== 'eu') updateRegion('eu', 'observer');
          }
        } else if (text.includes('WhiteBIT Global Server') || text.includes('https://whitebit.com') && text.includes('Server')) {
          if (el && el.tagName !== 'A' && !el.closest('.region-toggle-component')) {
            if (region !== 'com') updateRegion('com', 'observer');
          }
        }
      }
    });
    observerRef.current.observe(document.body, {
      childList: true,
      subtree: true,
      characterData: true
    });
    if (typeof window !== 'undefined') {
      const current = localStorage.getItem("api-region-preference");
      if (current) attemptToUpdateNativeDropdown(current, 'init');
    }
    return () => {
      window.removeEventListener("storage", handleStorageChange);
      window.removeEventListener("regionChange", handleRegionChange);
      if (observerRef.current) observerRef.current.disconnect();
    };
  }, [region]);
  const apiBaseUrl = protocol === "wss" ? region === "eu" ? "wss://api.whitebit.eu/ws" : "wss://wss.whitebit.com/ws" : region === "eu" ? "https://whitebit.eu" : "https://whitebit.com";
  const baseUrlLabel = protocol === "wss" ? "WebSocket URL" : "Base URL";
  if (!EU_ENABLED) {
    if (!showBaseUrl) return null;
    return <div className={`flex items-center gap-2 flex-wrap my-4 region-toggle-component ${className}`}>
                <span className="text-sm text-gray-500 dark:text-gray-400 font-mono">
                    {baseUrlLabel}
                </span>
                <span className="text-sm text-gray-400">:</span>
                {protocol === "wss" ? <code className="text-sm font-mono text-primary dark:text-primary-light">
                        {apiBaseUrl}
                    </code> : <a href={apiBaseUrl} target="_blank" rel="noopener noreferrer" className="text-sm font-mono text-primary dark:text-primary-light hover:underline">
                        {apiBaseUrl}
                    </a>}
            </div>;
  }
  if (!mounted) return null;
  return <div className={`flex items-center gap-2 flex-wrap my-4 region-toggle-component ${className}`}>
            <span className="text-sm text-gray-500 dark:text-gray-400 font-mono">
                {baseUrlLabel}
            </span>
            <span className="text-sm text-gray-400">(</span>
            <div className="inline-flex bg-gray-100 dark:bg-gray-800 rounded-lg p-0.5 border border-gray-200 dark:border-gray-700">
                <button onClick={() => updateRegion("com", "user-click")} className={`px-2 py-0.5 text-xs font-medium rounded-md transition-all ${region === "com" ? "bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100 shadow-sm" : "text-gray-500 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-200"}`}>
                    .com
                </button>
                <button onClick={() => updateRegion("eu", "user-click")} className={`px-2 py-0.5 text-xs font-medium rounded-md transition-all ${region === "eu" ? "bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-100 shadow-sm" : "text-gray-500 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-200"}`}>
                    .eu
                </button>
            </div>
            <span className="text-sm text-gray-400">)</span>
            {showBaseUrl && <>
                    <span className="text-sm text-gray-400">:</span>
                    {protocol === "wss" ? <code className="text-sm font-mono text-primary dark:text-primary-light">
                            {apiBaseUrl}
                        </code> : <a href={apiBaseUrl} target="_blank" rel="noopener noreferrer" className="text-sm font-mono text-primary dark:text-primary-light hover:underline">
                            {apiBaseUrl}
                        </a>}
                </>}
        </div>;
};

<RegionBaseUrl />

Convert provides instant currency conversion at a quoted rate. Unlike [spot trading](/products/spot/overview), Convert requires no orderbook interaction — request a quote and confirm it before it expires, or do both in a single call, and the conversion executes immediately.

## When to use Convert

| Feature | Convert | Spot Trading |
| - | - | - |
| Execution | Instant at quoted rate | Depends on orderbook liquidity |
| Orderbook | No orderbook interaction | Orders placed on the orderbook |
| Order types | Single quote-and-confirm flow | 6 order types (limit, market, stop-limit, stop-market, OCO, bulk) |
| Best for | Quick conversions, price estimation | Advanced trading strategies, precise price control |

<Note>
  Convert also serves as a price estimation tool. Call the estimate endpoint before placing a market order to preview the approximate execution price.
</Note>

## Conversion flow

The quoted-rate flow has two steps: estimate and confirm.

<Steps>
  <Step title="Request a quote">
    Send a `POST` request to [`/api/v4/convert/estimate`](/api-reference/convert/convert-estimate) with the following parameters: `from` (source ticker), `to` (destination ticker), `direction` (`"from"` or `"to"`), and `amount`. The response includes `id` (quote identifier), `give`, `receive`, `rate`, and `expireAt`.
  </Step>

  <Step title="Confirm the quote">
    Send a `POST` request to [`/api/v4/convert/confirm`](/api-reference/convert/convert-confirm) with `quoteId` set to the `id` returned by the estimate. The response returns `finalGive` and `finalReceive`.
  </Step>
</Steps>

<Note>
  Each estimate response includes an `expireAt` timestamp — confirm before that moment. Honour the value returned per quote rather than hard-coding a fixed window. If the quote expires before confirmation, request a new estimate.
</Note>

[Convert Auto Confirm](/api-reference/convert/convert-auto-confirm) collapses both steps into one request: the quote is requested and accepted in one call, so no expiry window applies and no `quoteId` round-trip is needed. The endpoint accepts an optional idempotency key that makes a retry safe.

## API endpoints

Convert uses 4 authenticated endpoints. All require HMAC-SHA512 signing.

| Endpoint | Purpose | Rate limit |
| - | - | - |
| [`POST /api/v4/convert/estimate`](/api-reference/convert/convert-estimate) | Request a conversion quote | 10,000 req / 10 sec |
| [`POST /api/v4/convert/confirm`](/api-reference/convert/convert-confirm) | Confirm and execute a quote | 10,000 req / 10 sec |
| [`POST /api/v4/convert/auto-confirm`](/api-reference/convert/convert-auto-confirm) | Request and accept a quote in a single call | 10,000 req / 10 sec |
| [`POST /api/v4/convert/history`](/api-reference/convert/convert-history) | Retrieve conversion history (max 6 months) | 10,000 req / 10 sec |

## Estimate parameters

| Parameter | Type | Required | Description |
| - | - | - | - |
| `from` | string | Yes | Source currency ticker (e.g., `BTC`) |
| `to` | string | Yes | Destination currency ticker (e.g., `USDT`) |
| `direction` | string | Yes | `"from"` = amount is in source currency; `"to"` = amount is in destination currency |
| `amount` | string | Yes | Amount to convert or receive |

## Code example

Estimate a BTC-to-USDT conversion and confirm the quote.

<Tabs>
  <Tab title="cURL">
    ```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
    # Step 1: Request a conversion estimate
    curl -X POST https://whitebit.com/api/v4/convert/estimate \
      -H "X-TXC-APIKEY: YOUR_API_KEY" \
      -H "X-TXC-PAYLOAD: YOUR_PAYLOAD" \
      -H "X-TXC-SIGNATURE: YOUR_SIGNATURE" \
      -H "Content-Type: application/json" \
      -d '{
        "from": "BTC",
        "to": "USDT",
        "direction": "from",
        "amount": "1",
        "request": "/api/v4/convert/estimate",
        "nonce": 1
      }'
    ```

    ```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
    {
      "id": "123",
      "from": "BTC",
      "to": "USDT",
      "give": "1",
      "receive": "34299.76831549",
      "rate": "34299.76831549",
      "expireAt": 1699016476
    }
    ```

    ```bash theme={"theme":{"light":"github-light","dark":"github-dark"}}
    # Step 2: Confirm the quote using the id from the estimate response
    curl -X POST https://whitebit.com/api/v4/convert/confirm \
      -H "X-TXC-APIKEY: YOUR_API_KEY" \
      -H "X-TXC-PAYLOAD: YOUR_PAYLOAD" \
      -H "X-TXC-SIGNATURE: YOUR_SIGNATURE" \
      -H "Content-Type: application/json" \
      -d '{
        "quoteId": "123",
        "request": "/api/v4/convert/confirm",
        "nonce": 2
      }'
    ```

    ```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
    {
      "finalGive": "1",
      "finalReceive": "34299.76831549"
    }
    ```
  </Tab>

  <Tab title="Python">
    ```python theme={"theme":{"light":"github-light","dark":"github-dark"}}
    # Step 1: Request a conversion estimate.
    # Request body for POST /api/v4/convert/estimate:
    estimate_body = {
        "from": "BTC",
        "to": "USDT",
        "direction": "from",
        "amount": "1",
    }
    # The response returns id, give, receive, rate, and expireAt.

    # Step 2: Confirm before expireAt, quoting the estimate id.
    # Request body for POST /api/v4/convert/confirm:
    confirm_body = {
        "quoteId": "123",  # id from the estimate response
    }
    # To send: add "request" and "nonce" to the body, serialize with compact
    # separators (the server rejects spaced JSON), sign with HMAC-SHA512, and
    # send with the X-TXC-APIKEY / X-TXC-PAYLOAD / X-TXC-SIGNATURE headers —
    # implementation in the First API Call guide linked below.
    ```
  </Tab>
</Tabs>

[First API Call](/guides/first-api-call) documents the signing details (payload construction, nonce, and headers).

For Go and PHP examples, see [SDKs](/sdks).

## History filters

The [history endpoint](/api-reference/convert/convert-history) supports filtering by `fromTicker`, `toTicker`, `quoteId`, and time range (`from`/`to` timestamps). The default limit is 100 records. The maximum history depth is 6 months from the current month.

## Convert in payment flows

Convert is commonly used as part of the withdrawal flow when the currency held in Main balance differs from the currency an end user needs to withdraw. The pattern is: request an estimate, confirm the quote, then submit the withdrawal in the destination currency — balance routing is handled internally by the convert service.

For the full step-by-step withdrawal-with-conversion flow including code examples, see [Payment Integration](/guides/payment-integration).

## What's next

<CardGroup cols={2}>
  <Card title="Payment Integration" icon="money-bill" href="/guides/payment-integration">
    Deposit and withdrawal flows including conversion.
  </Card>

  <Card title="Spot Trading" icon="chart-line" href="/products/spot/overview">
    Place orders on the orderbook with 6 order types.
  </Card>

  <Card title="WhiteBIT Codes" icon="ticket" href="/platform/whitebit-codes">
    Transfer funds between accounts using fee-free digital vouchers.
  </Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.