> ## 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.

# Private HTTP API Authentication

> Authenticate with WhiteBIT private HTTP API endpoints using API keys and HMAC-SHA512 signatures.

export const authErrors = [{
  message: "Payload not provided.",
  cause: "X-TXC-PAYLOAD header missing or empty",
  resolution: "Include the base64-encoded request body as X-TXC-PAYLOAD"
}, {
  message: "Too many requests.",
  cause: "Nonce value is not greater than previous request",
  resolution: "Use incrementing nonce values"
}, {
  message: "This action is unauthorized. Enable your key in API settings",
  cause: "Using a disabled API key",
  resolution: "Enable the key in API settings or check IP restrictions"
}, {
  message: "You don't have permission to use this endpoint.",
  cause: "Endpoint access is restricted",
  resolution: "Update endpoint access in API key settings"
}, {
  message: "Invalid payload",
  cause: "Payload does not match the decoded value",
  resolution: "Ensure proper base64 encoding of the request body"
}, {
  message: "Unauthorized request.",
  cause: "Request signed incorrectly",
  resolution: "Verify the signature creation process"
}, {
  message: "Nonce not provided.",
  cause: "Missing nonce in request body",
  resolution: "Include nonce in all requests"
}, {
  message: "Your nonce is more than 5 seconds lesser than the current nonce",
  cause: "Invalid timestamp when using nonceWindow",
  resolution: "Use current Unix timestamp in milliseconds"
}, {
  message: "Invalid nonceWindow.",
  cause: "nonceWindow is not a boolean",
  resolution: "Set nonceWindow to `true` or `false`"
}, {
  message: "Request not provided.",
  cause: "Missing request path in body",
  resolution: "Include the request path in all requests"
}];

export const requiredHeaders = [{
  header: "Content-type",
  value: "application/json",
  description: "Specifies JSON format"
}, {
  header: "X-TXC-APIKEY",
  value: "YOUR_API_KEY",
  description: "The public WhiteBIT API key"
}, {
  header: "X-TXC-PAYLOAD",
  value: "base64_encoded_payload",
  description: "Base64-encoded request body"
}, {
  header: "X-TXC-SIGNATURE",
  value: "signature",
  description: "HMAC-SHA512 signature (hex encoded)"
}];

export const ConceptTable = ({title, columns, rows, codeColumns = []}) => {
  const [isDark, setIsDark] = useState(typeof document !== 'undefined' ? document.documentElement.classList.contains('dark') : true);
  useEffect(() => {
    const check = () => setIsDark(document.documentElement.classList.contains('dark'));
    check();
    const observer = new MutationObserver(check);
    observer.observe(document.documentElement, {
      attributes: true,
      attributeFilter: ['class']
    });
    return () => observer.disconnect();
  }, []);
  const T = isDark ? {
    border: '#374151',
    borderSubtle: '#1f2937',
    headerBg: '#1f2937',
    headerText: '#9ca3af',
    titleBg: '#1f2937',
    titleText: '#d1d5db',
    codeBg: '#374151',
    codeText: '#e5e7eb',
    cellText: '#d1d5db'
  } : {
    border: '#e5e7eb',
    borderSubtle: '#f3f4f6',
    headerBg: '#f9fafb',
    headerText: '#6b7280',
    titleBg: '#f9fafb',
    titleText: '#374151',
    codeBg: '#f3f4f6',
    codeText: '#1f2937',
    cellText: '#374151'
  };
  const MONO = 'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace';
  const codeSet = new Set(codeColumns);
  const gridTemplateColumns = columns.map(col => col.width || '1fr').join(' ');
  const HEADER = {
    padding: '0.5rem 1rem',
    fontSize: '0.6875rem',
    fontWeight: 600,
    textTransform: 'uppercase',
    letterSpacing: '0.05em',
    whiteSpace: 'nowrap',
    color: T.headerText,
    backgroundColor: T.headerBg,
    borderBottom: `1px solid ${T.border}`
  };
  const CELL = {
    padding: '0.625rem 1rem',
    fontSize: '0.8125rem',
    display: 'flex',
    alignItems: 'center',
    minWidth: 0,
    color: T.cellText
  };
  return <div style={{
    margin: '1.25rem 0',
    borderRadius: '0.5rem',
    border: `1px solid ${T.border}`,
    overflow: 'hidden',
    fontSize: '0.8125rem'
  }}>
      {title && <div style={{
    padding: '0.5rem 1rem',
    fontSize: '0.75rem',
    fontWeight: 600,
    letterSpacing: '0.02em',
    backgroundColor: T.titleBg,
    borderBottom: `1px solid ${T.border}`,
    color: T.titleText
  }}>
          {title}
        </div>}
      <div style={{
    display: 'grid',
    gridTemplateColumns,
    width: '100%',
    overflowX: 'auto'
  }}>

        {columns.map(col => <div key={col.key} style={HEADER}>{col.header}</div>)}

        {rows.map((row, i) => {
    const borderTop = `1px solid ${i === 0 ? T.border : T.borderSubtle}`;
    return columns.map(col => {
      const value = row[col.key];
      const isCode = codeSet.has(col.key);
      const display = value != null ? String(value) : '—';
      return <div key={`${i}-${col.key}`} style={{
        ...CELL,
        borderTop
      }}>
                {isCode ? <span style={{
        padding: '0.125rem 0.375rem',
        borderRadius: '0.25rem',
        fontSize: '0.75rem',
        fontFamily: MONO,
        backgroundColor: T.codeBg,
        color: T.codeText,
        whiteSpace: 'nowrap'
      }}>
                    {display}
                  </span> : display}
              </div>;
    });
  })}

      </div>
    </div>;
};

export const RegionBaseUrl = ({className = "", showBaseUrl = true}) => {
  const [region, setRegionState] = useState(() => {
    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(() => {
    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 = region === "eu" ? "https://whitebit.eu" : "https://whitebit.com";
  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">
                Base URL
            </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>
                    <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 />

## Overview

The Authentication guide explains how to authenticate with WhiteBIT’s private HTTP API endpoints, which require authentication for security purposes.

## Getting Started

### Setting Up API Keys

<Steps>
  <Step>
    Navigate to [WhiteBIT API Settings](https://whitebit.com/settings/api)
  </Step>

  <Step>
    Select the appropriate configuration tab for API keys

    <Info>
      Different API keys provide access to different API endpoints
    </Info>
  </Step>

  <Step>
    Generate a new API key
  </Step>

  <Step>
    **Recommended Security Measures:**

    * Enable IP restrictions (specify up to 50 trusted IPs)
    * Enable Endpoint access restrictions (select only necessary endpoints)
  </Step>
</Steps>

<Note>
  Keys auto-deactivate after 14 days of inactivity. 2FA must be enabled before key creation.
  Use separate keys per application with minimal permissions.
</Note>

## Authentication Requirements

All authenticated requests must:

1. Use the `POST` HTTP method
2. Include specific body data
3. Contain required headers

### Body Data Format

The request body must be a JSON object containing:

| Field                       | Description                                            | Example                           |
| --------------------------- | ------------------------------------------------------ | --------------------------------- |
| `request`                   | Request path without domain name                       | `'/api/v4/trade-account/balance'` |
| `nonce`                     | An incrementing number larger than previous requests   | `1594297865000`                   |
| `nonceWindow`               | Optional boolean to enable time-based nonce validation | `true`                            |
| Request-specific parameters | Additional parameters required by the endpoint         | `"ticker": "BTC"`                 |

**Example Request Body:**

```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
    "request": "/api/v4/trade-account/balance",
    "nonce": 1594297865000,
    "nonceWindow": true,
    "ticker": "BTC"
}
```

#### About Nonce Values

* Use the Unix timestamp in milliseconds for nonce values
* Ensure each nonce is larger than previous requests
* When `nonceWindow` is enabled:
  * Provide Unix timestamp in milliseconds as the nonce
  * Timestamp must be within ±5 seconds of server time
  * Each nonce must be unique to prevent double processing
  * Useful for high-frequency trading systems with concurrent requests

### Required Headers

Every authenticated request requires these headers:

<ConceptTable
  columns={[
{ key: 'header', header: 'Header', width: 'minmax(140px, auto)' },
{ key: 'value', header: 'Value', width: 'minmax(160px, auto)' },
{ key: 'description', header: 'Description', width: '1fr' },
]}
  rows={requiredHeaders}
  codeColumns={['header']}
/>

Create the signature using: `hex(HMAC_SHA512(payload, key=api_secret))`

## Implementation Examples

WhiteBIT provides the [API Quick Start Helper](https://github.com/whitebit-exchange/api-quickstart) library with examples in multiple languages:

* Python
* PHP
* NodeJS
* Go
* JavaScript
* Kotlin
* DotNet
* Ruby
* C++
* Rust

## Common Errors

<ConceptTable
  columns={[
{ key: 'message', header: 'Error message', width: 'minmax(200px, 1.5fr)' },
{ key: 'cause', header: 'Cause', width: '1fr' },
{ key: 'resolution', header: 'Resolution', width: '1fr' },
]}
  rows={authErrors}
/>

For rate limits and REST error format, see [Rate limits and error codes](/api-reference/rate-limits).

## Testing in the API playground

<Snippet file="private-api-playground-testing.mdx" />

## Related resources

* [API Reference Overview](/api-reference/overview) — Base URL, rate limits, and error format
* [Market Data overview](/api-reference/market-data/overview) — Public endpoints (no authentication)
* [Spot Trading overview](/api-reference/spot-trading/overview) — Private trading endpoints
* [OAuth Overview](/platform/oauth/overview) — Third-party application authorization for ecosystem partners
