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

# Last Price

> Subscribe to real-time last trade price updates for trading pairs via WhiteBIT WebSocket.

export const exLastpriceUnsubscribe = {
  "id": 7,
  "method": "lastprice_unsubscribe",
  "params": []
};

export const exLastpriceUpdate = {
  "id": null,
  "method": "lastprice_update",
  "params": ["ETH_BTC", "0.020683"]
};

export const exLastpriceSubscribeResponse = {
  "id": 6,
  "result": {
    "status": "success"
  },
  "error": null
};

export const exLastpriceSubscribe = {
  "id": 6,
  "method": "lastprice_subscribe",
  "params": ["ETH_BTC", "BTC_USDT"]
};

export const exLastpriceResponse = {
  "id": 5,
  "result": "0.020553",
  "error": null
};

export const exLastpriceRequest = {
  "id": 5,
  "method": "lastprice_request",
  "params": ["ETH_BTC"]
};

export const lastpriceRequestParamsTupleFields = [{
  index: 0,
  field: "market",
  type: "string",
  description: "Market name (e.g., ETH_BTC)",
  required: true,
  example: "ETH_BTC"
}];

export const lastpriceUpdate = [{
  name: "id",
  type: "null",
  required: true,
  description: ""
}, {
  name: "method",
  type: "string",
  required: true,
  description: "Method name. Fixed value: `lastprice_update`."
}, {
  name: "params",
  type: "array",
  required: true,
  description: "Update event parameters:\n- [0] Market name\n- [1] Last price"
}];

export const channelMeta = {
  "authRequired": false,
  "rateLimits": {
    "connectionsPerMinute": 1000,
    "requestsPerMinute": 200
  },
  "errorCodes": "standard"
};

export const channelOperations = [{
  name: "Query",
  send: "lastprice_request",
  receive: "Current last price for the market",
  push: null
}, {
  name: "Subscribe",
  send: "lastprice_subscribe",
  receive: "Confirmation (status: success)",
  push: "lastprice_update — periodic last price update (every 1 second)"
}, {
  name: "Unsubscribe",
  send: "lastprice_unsubscribe",
  receive: "Confirmation (status: success)",
  push: null
}];

export const WsErrorCodes = ({errorCodes}) => {
  if (Array.isArray(errorCodes)) {
    return <div>
        <table>
          <thead>
            <tr>
              <th>Code</th>
              <th>Message</th>
              <th>Description</th>
            </tr>
          </thead>
          <tbody>
            {errorCodes.map((err, i) => <tr key={i}>
                <td><code>{err.code}</code></td>
                <td>{err.message}</td>
                <td>{err.description}</td>
              </tr>)}
          </tbody>
        </table>
        <p>
          Standard WebSocket error codes apply. See <a href="/websocket/overview">WebSocket overview</a> for error code reference.
        </p>
      </div>;
  }
  return <p>
      Standard WebSocket error codes apply. See <a href="/websocket/overview">WebSocket overview</a> for error code reference.
    </p>;
};

export const WsRateLimits = ({connectionsPerMinute, requestsPerMinute}) => {
  return <p>
      Standard connection-level rate limits apply. See{' '}
      <a href="/websocket/rate-limits">WebSocket Rate Limits</a> for details.
    </p>;
};

export const WsAuthBadge = ({required}) => {
  if (!required) {
    return <p><strong>Authentication:</strong> None. No authentication required.</p>;
  }
  return <div>
      <Warning>
        This is a private channel.{' '}
        <a href="/websocket/authentication">Authorize</a> the WebSocket
        connection before subscribing.
      </Warning>
      <p>
        <strong>Authentication:</strong> Required.{' '}
        <a href="/websocket/authentication">Authorize</a> the WebSocket
        connection before subscribing.
      </p>
    </div>;
};

export const WsTupleTable = ({title, fields}) => {
  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',
    indexText: '#60a5fa',
    fieldText: '#e5e7eb',
    fieldBg: '#374151',
    descText: '#9ca3af',
    exampleText: '#d1d5db',
    exampleBg: 'rgb(55 65 81 / 0.4)',
    enumText: '#fbbf24',
    reqYes: '#f3f4f6',
    reqNo: '#4b5563'
  } : {
    border: '#e5e7eb',
    borderSubtle: '#f3f4f6',
    headerBg: '#f9fafb',
    headerText: '#6b7280',
    titleBg: '#f9fafb',
    titleText: '#374151',
    indexText: '#2563eb',
    fieldText: '#1f2937',
    fieldBg: '#f3f4f6',
    descText: '#6b7280',
    exampleText: '#374151',
    exampleBg: '#f3f4f6',
    enumText: '#92400e',
    reqYes: '#111827',
    reqNo: '#d1d5db'
  };
  const MONO = 'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace';
  const HEADER = {
    padding: '0.5rem 0.75rem',
    fontSize: '0.6875rem',
    fontWeight: 600,
    textTransform: 'uppercase',
    letterSpacing: '0.05em',
    whiteSpace: 'nowrap',
    color: T.headerText,
    backgroundColor: T.headerBg
  };
  const CELL = {
    padding: '0.5rem 0.75rem',
    fontSize: '0.8125rem',
    display: 'flex',
    alignItems: 'center',
    minWidth: 0
  };
  const hasExample = fields.some(f => f.example !== undefined);
  const hasRequired = fields.some(f => f.required === true);
  const gridTemplateColumns = ['minmax(60px, auto)', 'minmax(100px, auto)', ...hasRequired ? ['minmax(70px, auto)'] : [], ...hasExample ? ['minmax(140px, auto)'] : [], '1fr'].join(' ');
  function formatDesc(f) {
    if (f.enumLabels && typeof f.enumLabels === 'object') {
      const mapping = Object.entries(f.enumLabels).map(([v, label]) => `${v} = ${label}`).join(', ');
      return f.description ? `${f.description}. ${mapping}` : mapping;
    }
    if (f.enum && f.enum.length > 0 && f.description) {
      const hasMapping = f.enum.some(v => f.description.includes(`${v}=`) || f.description.includes(`${v} =`));
      if (hasMapping) return f.description;
    }
    return f.description || '';
  }
  return <div style={{
    width: '100%',
    margin: '0.75rem 0',
    borderRadius: '0.5rem',
    border: `1px solid ${T.border}`,
    overflow: 'hidden',
    fontSize: '0.8125rem'
  }}>
      {title && <div style={{
    padding: '0.5rem 0.75rem',
    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'
  }}>

        <div style={{
    ...HEADER,
    borderBottom: `1px solid ${T.border}`
  }}>Index</div>
        <div style={{
    ...HEADER,
    borderBottom: `1px solid ${T.border}`
  }}>Field</div>
        {hasRequired && <div style={{
    ...HEADER,
    borderBottom: `1px solid ${T.border}`
  }}>Required</div>}
        {hasExample && <div style={{
    ...HEADER,
    borderBottom: `1px solid ${T.border}`
  }}>Example</div>}
        <div style={{
    ...HEADER,
    borderBottom: `1px solid ${T.border}`
  }}>Description</div>

        {fields.map((f, i) => {
    const borderTop = i > 0 ? `1px solid ${T.borderSubtle}` : undefined;
    const desc = formatDesc(f);
    return [<div key={`${i}-i`} style={{
      ...CELL,
      whiteSpace: 'nowrap',
      borderTop
    }}>
              <code style={{
      fontFamily: MONO,
      fontSize: '0.75rem',
      color: T.indexText
    }}>[{f.index}]</code>
            </div>, <div key={`${i}-f`} style={{
      ...CELL,
      whiteSpace: 'nowrap',
      borderTop
    }}>
              <span style={{
      padding: '0.125rem 0.375rem',
      borderRadius: '0.25rem',
      fontSize: '0.75rem',
      fontFamily: MONO,
      backgroundColor: T.fieldBg,
      color: T.fieldText
    }}>
                {f.field}
              </span>
            </div>, ...hasRequired ? [<div key={`${i}-r`} style={{
      ...CELL,
      borderTop,
      fontWeight: f.required ? 500 : 400,
      color: f.required ? T.reqYes : T.reqNo
    }}>
                {f.required ? 'Yes' : '—'}
              </div>] : [], ...hasExample ? [<div key={`${i}-e`} style={{
      ...CELL,
      whiteSpace: 'nowrap',
      borderTop
    }}>
                {f.example !== undefined ? <code style={{
      fontFamily: MONO,
      fontSize: '0.75rem',
      backgroundColor: T.exampleBg,
      color: T.exampleText,
      padding: '0.125rem 0.375rem',
      borderRadius: '0.25rem'
    }}>
                    {f.example}
                  </code> : null}
              </div>] : [], <div key={`${i}-d`} style={{
      ...CELL,
      borderTop,
      color: T.descText
    }}>
              {desc}
            </div>];
  })}

      </div>
    </div>;
};

export const WsSubscribeSteps = ({subscribe, confirmation, update, unsubscribe, subscribeNote, updateNote, updateExtra, unsubscribeNote}) => {
  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 TOKEN_COLORS = isDark ? {
    key: '#79c0ff',
    string: '#a5d6ff',
    number: '#e3b341',
    boolean: '#ff7b72',
    null: '#8b949e',
    punctuation: '#c9d1d9',
    ws: '#c9d1d9'
  } : {
    key: '#0550ae',
    string: '#0a3069',
    number: '#953800',
    boolean: '#cf222e',
    null: '#6e7781',
    punctuation: '#24292f',
    ws: '#24292f'
  };
  const tokenize = json => {
    const tokens = [];
    let i = 0;
    while (i < json.length) {
      if ((/\s/).test(json[i])) {
        let ws = '';
        while (i < json.length && (/\s/).test(json[i])) ws += json[i++];
        tokens.push({
          type: 'ws',
          value: ws
        });
        continue;
      }
      if (json[i] === '"') {
        let str = '"';
        i++;
        while (i < json.length) {
          if (json[i] === '\\') {
            str += json[i] + json[i + 1];
            i += 2;
          } else if (json[i] === '"') {
            str += '"';
            i++;
            break;
          } else str += json[i++];
        }
        let j = i;
        while (j < json.length && (/[ \t]/).test(json[j])) j++;
        tokens.push({
          type: json[j] === ':' ? 'key' : 'string',
          value: str
        });
        continue;
      }
      if (json[i] === '-' || (/\d/).test(json[i])) {
        let num = '';
        if (json[i] === '-') num += json[i++];
        while (i < json.length && (/[\d.eE+\-]/).test(json[i])) num += json[i++];
        tokens.push({
          type: 'number',
          value: num
        });
        continue;
      }
      if (json.slice(i, i + 4) === 'true') {
        tokens.push({
          type: 'boolean',
          value: 'true'
        });
        i += 4;
        continue;
      }
      if (json.slice(i, i + 5) === 'false') {
        tokens.push({
          type: 'boolean',
          value: 'false'
        });
        i += 5;
        continue;
      }
      if (json.slice(i, i + 4) === 'null') {
        tokens.push({
          type: 'null',
          value: 'null'
        });
        i += 4;
        continue;
      }
      tokens.push({
        type: 'punctuation',
        value: json[i++]
      });
    }
    return tokens;
  };
  const JsonBlock = ({data}) => {
    const json = JSON.stringify(data, null, 2);
    const tokens = tokenize(json);
    return <div className="border border-gray-200 dark:border-[#30363d]" style={{
      borderRadius: '0.5rem',
      overflow: 'hidden',
      marginBottom: '0.75rem'
    }}>
        <div className="bg-gray-50 dark:bg-[#161b22] border-b border-gray-200 dark:border-[#30363d] text-gray-500 dark:text-[#8b949e]" style={{
      padding: '0.25rem 1rem',
      fontSize: '0.7rem',
      fontFamily: 'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace'
    }}>
          json
        </div>
        <div className="bg-white dark:bg-[#0d1117]" style={{
      margin: 0,
      padding: '0.875rem 1.125rem',
      overflowX: 'auto',
      fontSize: '0.8125rem',
      lineHeight: '1.65',
      fontFamily: 'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace',
      whiteSpace: 'pre'
    }}>
          {tokens.map((t, idx) => <span key={idx} style={{
      color: TOKEN_COLORS[t.type]
    }}>{t.value}</span>)}
        </div>
      </div>;
  };
  const CIRCLE = 32;
  const WsStep = ({number, title, isLast, children}) => <div style={{
    display: 'flex',
    gap: '1rem'
  }}>
      <div style={{
    display: 'flex',
    flexDirection: 'column',
    alignItems: 'center',
    flexShrink: 0,
    width: CIRCLE
  }}>
        <div className="bg-gray-100 dark:bg-gray-700 border-gray-300 dark:border-gray-600 text-gray-900 dark:text-gray-100" style={{
    width: CIRCLE,
    height: CIRCLE,
    borderRadius: '50%',
    border: '1.5px solid',
    display: 'flex',
    alignItems: 'center',
    justifyContent: 'center',
    fontSize: '0.8125rem',
    fontWeight: 600,
    flexShrink: 0
  }}>
          {number}
        </div>
        {!isLast && <div className="bg-gray-200 dark:bg-gray-700" style={{
    width: 2,
    flex: 1,
    minHeight: '1.5rem'
  }} />}
      </div>
      <div style={{
    flex: 1,
    paddingBottom: isLast ? '0.5rem' : '1.75rem',
    paddingTop: '4px',
    minWidth: 0
  }}>
        <p style={{
    fontWeight: 600,
    fontSize: '1rem',
    margin: '0 0 0.75rem 0',
    lineHeight: `${CIRCLE}px`
  }}>
          {title}
        </p>
        {children}
      </div>
    </div>;
  return <div style={{
    margin: '1.25rem 0'
  }}>
      <WsStep number={1} title="Send subscription request">
        {subscribeNote && <p style={{
    margin: '0 0 0.75rem 0'
  }}>{subscribeNote}</p>}
        <JsonBlock data={subscribe} />
      </WsStep>

      <WsStep number={2} title="Receive confirmation">
        <JsonBlock data={confirmation} />
      </WsStep>

      <WsStep number={3} title="Receive real-time updates">
        {updateNote && <p style={{
    margin: '0 0 0.75rem 0'
  }}>{updateNote}</p>}
        <JsonBlock data={update} />
        {updateExtra}
      </WsStep>

      <WsStep number={4} title="Unsubscribe" isLast>
        {unsubscribeNote && <p style={{
    margin: '0 0 0.75rem 0'
  }}>{unsubscribeNote}</p>}
        <JsonBlock data={unsubscribe} />
      </WsStep>
    </div>;
};

export const WsMessageExample = ({data, title}) => {
  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 COLORS = isDark ? {
    key: '#79c0ff',
    string: '#a5d6ff',
    number: '#e3b341',
    boolean: '#ff7b72',
    null: '#8b949e',
    punctuation: '#c9d1d9',
    ws: '#c9d1d9'
  } : {
    key: '#0550ae',
    string: '#0a3069',
    number: '#953800',
    boolean: '#cf222e',
    null: '#6e7781',
    punctuation: '#24292f',
    ws: '#24292f'
  };
  const tokenize = json => {
    const tokens = [];
    let i = 0;
    while (i < json.length) {
      if ((/\s/).test(json[i])) {
        let ws = '';
        while (i < json.length && (/\s/).test(json[i])) ws += json[i++];
        tokens.push({
          type: 'ws',
          value: ws
        });
        continue;
      }
      if (json[i] === '"') {
        let str = '"';
        i++;
        while (i < json.length) {
          if (json[i] === '\\') {
            str += json[i] + json[i + 1];
            i += 2;
          } else if (json[i] === '"') {
            str += '"';
            i++;
            break;
          } else {
            str += json[i++];
          }
        }
        let j = i;
        while (j < json.length && (/[ \t]/).test(json[j])) j++;
        const isKey = json[j] === ':';
        tokens.push({
          type: isKey ? 'key' : 'string',
          value: str
        });
        continue;
      }
      if (json[i] === '-' || (/\d/).test(json[i])) {
        let num = '';
        if (json[i] === '-') num += json[i++];
        while (i < json.length && (/[\d.eE+\-]/).test(json[i])) num += json[i++];
        tokens.push({
          type: 'number',
          value: num
        });
        continue;
      }
      if (json.slice(i, i + 4) === 'true') {
        tokens.push({
          type: 'boolean',
          value: 'true'
        });
        i += 4;
        continue;
      }
      if (json.slice(i, i + 5) === 'false') {
        tokens.push({
          type: 'boolean',
          value: 'false'
        });
        i += 5;
        continue;
      }
      if (json.slice(i, i + 4) === 'null') {
        tokens.push({
          type: 'null',
          value: 'null'
        });
        i += 4;
        continue;
      }
      tokens.push({
        type: 'punctuation',
        value: json[i++]
      });
    }
    return tokens;
  };
  const json = JSON.stringify(data, null, 2);
  const tokens = tokenize(json);
  return <div style={{
    marginBottom: '1rem'
  }}>
      {title && <p style={{
    fontWeight: 600,
    fontSize: '0.875rem',
    marginTop: '1rem',
    marginBottom: '0.5rem'
  }}>
          {title}
        </p>}
      <div className="border border-gray-200 dark:border-[#30363d]" style={{
    position: 'relative',
    borderRadius: '0.5rem',
    overflow: 'hidden'
  }}>
        <div className="bg-gray-50 dark:bg-[#161b22] border-b border-gray-200 dark:border-[#30363d]" style={{
    display: 'flex',
    alignItems: 'center',
    justifyContent: 'space-between',
    padding: '0.375rem 1rem'
  }}>
          <span className="text-gray-500 dark:text-[#8b949e]" style={{
    fontSize: '0.75rem',
    fontFamily: 'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace'
  }}>
            json
          </span>
        </div>
        <div className="bg-white dark:bg-[#0d1117]" style={{
    margin: 0,
    padding: '1rem 1.25rem',
    overflowX: 'auto',
    fontSize: '0.8125rem',
    lineHeight: '1.7',
    fontFamily: 'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace',
    whiteSpace: 'pre'
  }}>
          {tokens.map((t, idx) => <span key={idx} style={{
    color: COLORS[t.type]
  }}>{t.value}</span>)}
        </div>
      </div>
    </div>;
};

export const WsSchemaTable = ({title, fields}) => {
  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',
    fieldBg: '#374151',
    fieldText: '#e5e7eb',
    reqYes: '#f3f4f6',
    reqNo: '#4b5563',
    descText: '#9ca3af',
    emptyText: '#4b5563',
    enumBg: 'rgb(120 53 15 / 0.3)',
    enumText: '#fbbf24'
  } : {
    border: '#e5e7eb',
    borderSubtle: '#f3f4f6',
    headerBg: '#f9fafb',
    headerText: '#6b7280',
    titleBg: '#f9fafb',
    titleText: '#374151',
    fieldBg: '#f3f4f6',
    fieldText: '#1f2937',
    reqYes: '#111827',
    reqNo: '#d1d5db',
    descText: '#6b7280',
    emptyText: '#d1d5db',
    enumBg: '#fffbeb',
    enumText: '#92400e'
  };
  const TYPE_STYLE = isDark ? {
    string: {
      backgroundColor: 'rgb(88 28 135 / 0.25)',
      color: '#c084fc'
    },
    integer: {
      backgroundColor: 'rgb(154 52 18 / 0.25)',
      color: '#fb923c'
    },
    number: {
      backgroundColor: 'rgb(154 52 18 / 0.25)',
      color: '#fb923c'
    },
    boolean: {
      backgroundColor: 'rgb(30 64 175 / 0.25)',
      color: '#60a5fa'
    },
    array: {
      backgroundColor: 'rgb(17 94 89 / 0.25)',
      color: '#2dd4bf'
    },
    object: {
      backgroundColor: 'rgb(55 65 81 / 0.4)',
      color: '#9ca3af'
    },
    null: {
      backgroundColor: 'rgb(55 65 81 / 0.4)',
      color: '#9ca3af'
    }
  } : {
    string: {
      backgroundColor: '#faf5ff',
      color: '#6b21a8'
    },
    integer: {
      backgroundColor: '#fff7ed',
      color: '#9a3412'
    },
    number: {
      backgroundColor: '#fff7ed',
      color: '#9a3412'
    },
    boolean: {
      backgroundColor: '#eff6ff',
      color: '#1e40af'
    },
    array: {
      backgroundColor: '#f0fdfa',
      color: '#115e59'
    },
    object: {
      backgroundColor: '#f3f4f6',
      color: '#374151'
    },
    null: {
      backgroundColor: '#f3f4f6',
      color: '#374151'
    }
  };
  const MONO = 'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace';
  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
  };
  const hasRequired = fields.some(f => f.required === true);
  const hasEnum = fields.some(f => f.enum && f.enum.length > 0);
  const gridTemplateColumns = ['minmax(120px, auto)', 'minmax(70px, auto)', ...hasRequired ? ['minmax(70px, auto)'] : [], ...hasEnum ? ['minmax(120px, auto)'] : [], '1fr'].join(' ');
  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'
  }}>

        <div style={HEADER}>Field</div>
        <div style={HEADER}>Type</div>
        {hasRequired && <div style={HEADER}>Required</div>}
        {hasEnum && <div style={HEADER}>Values</div>}
        <div style={HEADER}>Description</div>

        {fields.map((f, i) => {
    const borderTop = `1px solid ${i === 0 ? T.border : T.borderSubtle}`;
    const typeStyle = TYPE_STYLE[f.type] || TYPE_STYLE.object;
    return [<div key={`${i}-n`} style={{
      ...CELL,
      whiteSpace: 'nowrap',
      borderTop
    }}>
              <span style={{
      padding: '0.125rem 0.375rem',
      borderRadius: '0.25rem',
      fontSize: '0.75rem',
      fontFamily: MONO,
      backgroundColor: T.fieldBg,
      color: T.fieldText
    }}>
                {f.name}
              </span>
            </div>, <div key={`${i}-t`} style={{
      ...CELL,
      whiteSpace: 'nowrap',
      borderTop
    }}>
              <span style={{
      padding: '0.125rem 0.375rem',
      borderRadius: '0.25rem',
      fontSize: '0.75rem',
      whiteSpace: 'nowrap',
      fontFamily: MONO,
      ...typeStyle
    }}>
                {f.type}
              </span>
            </div>, ...hasRequired ? [<div key={`${i}-r`} style={{
      ...CELL,
      borderTop,
      fontWeight: f.required ? 500 : 400,
      color: f.required ? T.reqYes : T.reqNo
    }}>
                {f.required ? 'Yes' : '—'}
              </div>] : [], ...hasEnum ? [<div key={`${i}-e`} style={{
      ...CELL,
      borderTop,
      flexWrap: 'wrap',
      gap: '0.25rem'
    }}>
                {f.enum && f.enum.length > 0 ? f.enum.map((v, j) => <span key={j} style={{
      padding: '0.125rem 0.375rem',
      borderRadius: '0.25rem',
      fontSize: '0.75rem',
      whiteSpace: 'nowrap',
      fontFamily: MONO,
      backgroundColor: T.enumBg,
      color: T.enumText
    }}>
                        {String(v)}
                      </span>) : <span style={{
      color: T.emptyText
    }}>—</span>}
              </div>] : [], <div key={`${i}-d`} style={{
      ...CELL,
      borderTop,
      color: T.descText
    }}>
              {f.description}
            </div>];
  })}

      </div>
    </div>;
};

export const WsChannelOverview = ({operations}) => {
  const hasPush = operations.some(op => op.push);
  const Th = ({children}) => <th className="text-left text-gray-500 dark:text-gray-400 border-b border-gray-200 dark:border-gray-700" style={{
    padding: "0.5rem 1rem",
    fontSize: "0.6875rem",
    fontWeight: 600,
    textTransform: "uppercase",
    letterSpacing: "0.05em",
    whiteSpace: "nowrap"
  }}>
      {children}
    </th>;
  const PushBadge = ({value}) => {
    const dashIdx = value.indexOf(" — ");
    const method = dashIdx !== -1 ? value.slice(0, dashIdx) : value;
    const desc = dashIdx !== -1 ? value.slice(dashIdx + 3) : null;
    return <span style={{
      display: "inline-flex",
      alignItems: "center",
      gap: "0.375rem",
      flexWrap: "wrap"
    }}>
        <code className="bg-blue-50 dark:bg-blue-900/20 text-blue-800 dark:text-blue-400" style={{
      padding: "0.125rem 0.375rem",
      borderRadius: "0.25rem",
      fontSize: "0.75rem",
      whiteSpace: "nowrap"
    }}>
          {method}
        </code>
        {desc && <span className="text-gray-500 dark:text-gray-400" style={{
      fontSize: "0.75rem"
    }}>
            — {desc}
          </span>}
      </span>;
  };
  return <div style={{
    margin: "1.25rem 0",
    borderRadius: "0.5rem",
    border: "1px solid",
    overflow: "hidden",
    fontSize: "0.8125rem"
  }} className="border-gray-200 dark:border-gray-700">
      <table className="w-full" style={{
    margin: 0,
    borderCollapse: "collapse",
    tableLayout: "auto"
  }}>
        <thead>
          <tr className="bg-gray-50 dark:bg-gray-800/60 border-b border-gray-200 dark:border-gray-700">
            {Th({
    children: "Operation"
  })}
            {Th({
    children: <span><span style={{
      color: "#16a34a"
    }}>→</span> You send</span>
  })}
            {Th({
    children: "← Server responds"
  })}
            {hasPush && Th({
    children: <span><span style={{
      color: "#2563eb"
    }}>⟵</span> Server pushes</span>
  })}
          </tr>
        </thead>
        <tbody>
          {operations.map((op, i) => <tr key={i} className="border-b border-gray-100 dark:border-gray-800" style={{
    borderBottom: i === operations.length - 1 ? "none" : undefined
  }}>
              {}
              <td className="text-gray-900 dark:text-gray-100" style={{
    padding: "0.625rem 1rem",
    fontWeight: 500,
    whiteSpace: "nowrap"
  }}>
                {op.name}
              </td>

              {}
              <td style={{
    padding: "0.625rem 1rem",
    whiteSpace: "nowrap"
  }}>
                <code className="bg-green-50 dark:bg-green-900/20 text-green-800 dark:text-green-400" style={{
    padding: "0.125rem 0.375rem",
    borderRadius: "0.25rem",
    fontSize: "0.75rem"
  }}>
                  {op.send}
                </code>
              </td>

              {}
              <td className="text-gray-500 dark:text-gray-400" style={{
    padding: "0.625rem 1rem"
  }}>
                {op.receive}
              </td>

              {}
              {hasPush && <td style={{
    padding: "0.625rem 1rem"
  }}>
                  {op.push ? PushBadge({
    value: op.push
  }) : <span className="text-gray-300 dark:text-gray-600">—</span>}
                </td>}
            </tr>)}
        </tbody>
      </table>
    </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 />

Query or subscribe to real-time last traded price updates for one or more markets. The channel delivers the most recent executed trade price for each subscribed market. Updates arrive every second when the price changes, making this channel suitable for live price tickers and market data dashboards.

<Note>Supports both spot pairs (e.g., `BTC_USDT`) and futures markets (e.g., `BTC_PERP`). Mix spot and futures market names in a single subscribe request.</Note>

<WsAuthBadge required={channelMeta.authRequired} />

<Note>Connect to `wss://api.whitebit.com/ws` — see [WebSocket Overview](/websocket/overview) for protocol details and keepalive requirements.</Note>

## Rate limits

<WsRateLimits {...channelMeta.rateLimits} />

<WsChannelOverview operations={channelOperations} />

## Query last price (one-time)

Fetch the current last price for a single market without subscribing.

<WsTupleTable fields={lastpriceRequestParamsTupleFields} />

**Request**

<WsMessageExample data={exLastpriceRequest} />

**Response**

<WsMessageExample data={exLastpriceResponse} />

## Subscribe to last price

<WsSubscribeSteps subscribe={exLastpriceSubscribe} subscribeNote="params — array of market name strings (e.g., ETH_BTC, BTC_USDT). Pass one or more market names. Type: string[]. No default; at least one market name is required." confirmation={exLastpriceSubscribeResponse} update={exLastpriceUpdate} updateNote="params[0] (string) — market name. params[1] (string) — last traded price as a decimal string." unsubscribe={exLastpriceUnsubscribe} />

## Update frequency

The server pushes updates every **1 second** when the price changes.

## Error codes

<WsErrorCodes errorCodes={channelMeta.errorCodes} />

## Last price update object

<WsSchemaTable fields={lastpriceUpdate} />
