# HMAC signature generator: debug exchange API signing

URL: https://fincodelabs.com/en/tools/hmac-signature-generator · Publisher: Fincode (Fincode Yazılım ve Teknoloji A.Ş.), Istanbul, Turkey · Updated: 2026-10-15

Build the exact string an exchange signs, see it byte by byte, get the HMAC in hex and Base64 and compare it with yours. Never paste a live secret into any website, this one included: the tool loads a published sample key. Free, no sign-up required.

## Calculator

The calculation runs in the browser in the form on this page: https://fincodelabs.com/en/tools/hmac-signature-generator

**Inputs**
- Exchange preset: Picking a preset loads its sample into the fields below
- Secret key: Sample, testnet or throwaway keys only. Never paste a live secret into any website, this one included
- Secret format: How the secret is turned into bytes
- Hash: The hash inside the HMAC
- API key: Part of the string to sign
- Timestamp: As sent in the request
- recv_window (ms): Part of the string to sign
- Method: Uppercase
- Request path: Exactly as sent
- Query string: Without the leading ? and without signature=. Non-ASCII characters percent-encoded first
- Request body: Optional; added after the query string with no separator
- Signature to compare: Optional; paste what your code produced
- String your code signed: Optional; paste it to find the first byte that differs
- Server time (ms): Optional; from the exchange's time endpoint, typed by you (default: 1499827320000)

**Results:** Signature to send, Against your signature, Against your string, String signed, Bytes signed, Recipe, HMAC in hex, HMAC in Base64, Inner SHA-256, Timestamp in UTC, Against server time

> Never paste a live secret into any website, this one included: use the published sample, a testnet key or a throwaway key. The signing runs in your browser, nothing is stored or sent, and your browser's Network tab stays empty while you type. Presets describe each exchange's documents as read on 5 November 2026 and imply no affiliation.

## Do not paste a live secret here

Do not paste a live API secret into this page or any other website. A secret in a web page is within reach of that page's scripts, your browser extensions and anything recording your screen, and you cannot check those afterward. Use the published sample that loads with the page, a testnet key, or a throwaway key you delete when you finish. If a live secret has already gone into a website, replace the key on the exchange.

- **Nothing is sent.** The HMAC is computed in your browser with the Web Crypto API (`SubtleCrypto.sign`, which browsers offer only on HTTPS pages). The tool makes no network call.
- **Nothing you type is stored.** It goes into no cookie, no `localStorage` and no URL parameter. Autocomplete is off on every field, and the values are gone when you close the page.

**How to confirm it in the Network tab:** open your browser's developer tools (F12, or Cmd+Option+I on a Mac), choose Network, clear the list, then type in the fields and change the preset. No request appears. The requests you see while the page loads are its own files and the Cloudflare Web Analytics beacon described in the [privacy notice](https://fincodelabs.com/en/privacy), which carries page and performance data, not form values; it can report once more when you switch away from the page or close it. You can also read the `connect-src` part of the page's `Content-Security-Policy` response header: it allows connections only to this site and to Cloudflare Insights.

## How each exchange builds the string it signs

Each exchange signs a different string, and the tool builds the one you pick so you can see every byte.

**Official documents read on 5 November 2026, in alphabetical order. Recheck them: signing rules change.**

| Preset | String signed | Secret used as | Hash | Sent as |
| --- | --- | --- | --- | --- |
| Binance spot REST | query string + body, no separator, non-ASCII characters percent-encoded first | Text | SHA-256 | Hex, `signature` |
| BtcTurk | public key + nonce | Base64-decoded | SHA-256 | Base64, `X-Signature` |
| Bybit V5 | timestamp + API key + recv_window + query string or JSON body | Text | SHA-256 | Lowercase hex, `X-BAPI-SIGN` |
| Kraken spot REST | URI path + SHA-256 of (nonce + POST data) | Base64-decoded | SHA-512 | Base64, `API-Sign` |
| OKX v5 | timestamp + method + request path + body | Text | SHA-256 | Base64, `OK-ACCESS-SIGN` |

How far each preset could be tested on 5 November 2026:

- **Published key and signature, reproduced:** Binance (both samples in `rest-api.md`), Kraken (its authentication guide) and the plain HMAC mode (RFC 4231).
- **Published string reproduced, signature not testable:** Bybit prints the string to sign and an example signature but not the secret behind it. The preset rebuilds that string byte for byte and uses the placeholder key from Bybit's demo script, so its result differs from the printed example.
- **Recipe only:** OKX prints an example secret, timestamp and path, which the preset loads, but no signature made with them. BtcTurk prints code samples with no test values, so its preset uses a made-up key.

Every preset also agrees with a separate Python implementation on random inputs. Coinbase App API keys have no preset: they sign a JWT, not an HMAC. Exchange names here describe their documents only: no affiliation, no referral link, no recommendation, and no statement that Fincode has integrated any of them. For readers in Turkey, Article 99/A of Capital Markets Law No. 6362 treats activity by foreign crypto-asset platforms directed at Turkish residents as unauthorized.

## Worked example: Binance's published sample

> **Published sample, not a live key:** these are the values the tool loads, from Binance's `rest-api.md`. Secret: `NhqPtmdSJYdKjVHjA7PZj4Mge3R5YNiP1e3UZjInClVN65XAbvqqM6A7H5fATj0j`.
>
> String signed, 110 bytes: `symbol=LTCBTC&side=BUY&type=LIMIT&timeInForce=GTC&quantity=1&price=0.1&recvWindow=5000&timestamp=1499827319559`
>
> HMAC-SHA-256 in hex, the form Binance takes: **c8db56825ae71d6d79447849e617115f4a920fa2acdcab2b053c4b2838bd6b71**. The same bytes in Base64: `yNtWglrnHW15RHhJ5hcRX0qSD6Ks3KsrBTxLKDi9a3E=`. Pasted as the signature to compare, Binance's printed value gives Match.
>
> The timestamp in the string is 2017-07-12 02:41:59.559 UTC. With a hypothetical server time of 1499827320000, it is 441 ms behind, inside the 5,000 ms window.

Now change one byte. With `price=0.10` in place of `price=0.1`, the HMAC becomes `7f124c213f122930e71386ade5952185fcd4cee18ce97ebc3af221748c75cf81`, different from the first character on. Paste that string as the one your code signed, and the tool names the cause: first difference at byte 71, yours is 0x30 (0), this one 0x26 (&).

## Why a signature does not match

A different HMAC means a different input, so look at the inputs and not at the signature.

- **Stray bytes in the secret:** a space or line break copied with the key. The tool warns about this.
- **Secret format:** Kraken and BtcTurk sign with the Base64-decoded secret; the other presets use the text as it is.
- **What you sign is not what you send:** reordered parameters, values encoded again by an HTTP library, or JSON serialized twice with different spacing.
- **Output format:** hex and Base64 of one HMAC look unrelated, so the compare row names that case.
- **A line break from the shell:** `echo` without `-n` adds one byte to the message.
- **Time:** a correct signature is still refused when the timestamp falls outside the exchange's window. Type a server time you fetched yourself to see where it falls; this page requests nothing.

What each refusal looks like is in [Binance API error codes](https://fincodelabs.com/en/blog/binance-api-error-codes), and how to keep keys safe is in [API key security for trading bots](https://fincodelabs.com/en/blog/api-key-security-trading-bots). The whole request is walked through in API signature generation. If you would rather have this part built and tested, see [exchange API integration](https://fincodelabs.com/en/services/exchange-api-integration).

## The same calculation in Python

This short script gives the same results with the standard library, so you can compare the tool with your own environment.

```python
import base64, hashlib, hmac

secret = "NhqPtmdSJYdKjVHjA7PZj4Mge3R5YNiP1e3UZjInClVN65XAbvqqM6A7H5fATj0j"
payload = "symbol=LTCBTC&side=BUY&type=LIMIT&timeInForce=GTC&quantity=1&price=0.1&recvWindow=5000&timestamp=1499827319559"
mac = hmac.new(secret.encode(), payload.encode(), hashlib.sha256)
print(mac.hexdigest())                          # hex: Binance, Bybit
print(base64.b64encode(mac.digest()).decode())  # Base64: BtcTurk, OKX

# Kraken: SHA-256 inside, HMAC-SHA512 outside, secret decoded from Base64
def kraken_sign(secret_b64, path, nonce, post_data):
    message = path.encode() + hashlib.sha256((nonce + post_data).encode()).digest()
    mac = hmac.new(base64.b64decode(secret_b64), message, hashlib.sha512)
    return base64.b64encode(mac.digest()).decode()
```

Run on Python 3.11 on 5 November 2026, the two print lines show the hex and Base64 values above, and `kraken_sign` with Kraken's published values returns its printed `API-Sign`.

## Which values to enter

- **Exchange preset:** picking one loads its sample and shows only the fields that exchange signs. The plain HMAC mode takes any message, with the secret as text, hex or Base64.
- **Secret:** a sample, testnet or throwaway key only.
- **Signature to compare:** what your code produced, in hex or Base64.
- **String your code signed:** log it just before signing and paste it here, the most useful field when the signatures differ.
- **Server time:** in milliseconds, from the exchange's time endpoint. The tool applies that exchange's documented window. Kraken uses an increasing nonce and BtcTurk's page states no window, so the field hides for them.

## Frequently asked questions

### Is it safe to paste my API secret into an online HMAC generator?

No, not a live one, on any website, this one included. Use a published sample, a testnet key or a throwaway key, and replace any live key that has been pasted into a web page.

### How can I check that this tool sends nothing?

Open your browser's developer tools, choose the Network tab, clear the list and type in the fields. No request appears. The page's Content-Security-Policy header also limits connections to this site and Cloudflare Insights.

### Can an HMAC be decoded or decrypted?

No. An HMAC is a one-way keyed digest, not encryption, so neither the key nor the message can be read back from it. RFC 2104 and NIST FIPS 198-1 define it as two nested hash runs with the key mixed in. You check one the way the exchange does: compute it again from the same key and message, then compare. A page that offers to decode an HMAC can only try guesses against it.

### Should an HMAC signature be hex or Base64?

The API decides. As read on 5 November 2026, Binance and Bybit take hex, and BtcTurk, Kraken and OKX take Base64. The tool prints both, and they are the same bytes.

### What is the difference between HMAC-SHA256 and HMAC-SHA512?

The hash inside. SHA-256 gives a 32-byte result and SHA-512 a 64-byte result, and an API accepts only the one its documents name. Kraken's spot REST uses HMAC-SHA512 over a message that already contains a SHA-256 digest.

### Why is my signature different in every character?

An HMAC changes completely when one input byte changes, so the first differing character only helps with a cut or re-encoded copy. To find the cause, paste the string your code signed, and the tool names the first byte that differs.

## Sources

- [Binance Spot API documentation: REST API, request security and signed examples (read 5 November 2026)](https://github.com/binance/binance-spot-api-docs/blob/master/rest-api.md)
- [Binance Spot API changelog, entry of 17 December 2025: payloads not percent-encoded before signing are rejected with -1022 (read 6 November 2026)](https://github.com/binance/binance-spot-api-docs/blob/master/CHANGELOG.md)
- [BtcTurk API documentation: Authentication V1 (read 5 November 2026)](https://docs.btcturk.com/docs/authentication/authentication-v1)
- [Bybit V5: Integration guidance, authentication (read 5 November 2026)](https://bybit-exchange.github.io/docs/v5/guide)
- [Bybit API usage examples: HMAC demo script](https://github.com/bybit-exchange/api-usage-examples/blob/master/V5_demo/api_demo/Encryption_HMAC.py)
- [Kraken: Spot REST authentication (read 5 November 2026)](https://docs.kraken.com/exchange/guides/rest/authentication)
- [OKX API v5: REST authentication (read 5 November 2026)](https://www.okx.com/docs-v5/en/#overview-rest-authentication)
- [Coinbase App API key authentication (JWT, read 5 November 2026)](https://docs.cdp.coinbase.com/coinbase-app/authentication-authorization/api-key-authentication)
- [RFC 2104: HMAC, keyed-hashing for message authentication](https://www.rfc-editor.org/rfc/rfc2104)
- [RFC 4231: Test vectors for HMAC-SHA-224, 256, 384 and 512](https://www.rfc-editor.org/rfc/rfc4231)
- [NIST FIPS 198-1: The Keyed-Hash Message Authentication Code](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.198-1.pdf)
- [MDN: SubtleCrypto.sign(), HMAC, secure contexts only](https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/sign)
- [Capital Markets Law No. 6362, consolidated text, Article 99/A (Turkish)](https://www.mevzuat.gov.tr/mevzuatmetin/1.5.6362.pdf)

> This calculator is general information, not tax, legal or investment advice. Results depend on the values you enter; fees, margin rules and tax rules differ by exchange and country and change over time, so check the official source and ask a licensed adviser about your own situation. Sources, checks and corrections: see the [editorial policy](https://fincodelabs.com/en/editorial-policy).

---

Published by Fincode (Fincode Yazılım ve Teknoloji A.Ş.), a software company in Istanbul, Turkey: https://fincodelabs.com/en/tools/hmac-signature-generator

Cite as: Fincode, "HMAC signature generator: debug exchange API signing", updated October 15, 2026, https://fincodelabs.com/en/tools/hmac-signature-generator
