Problem connecting to Bybit API – 401 Unauthorized despite correct HMAC signature

Hi everyone,

I’m trying to connect to Bybit’s API (v5 /position/list) using an HTTP Request node in n8n (self-hosted, version 1.89.2), but I keep getting a 401 Unauthorized - please check your credentials error.

Here’s a quick overview of my setup:

  • I manually build the HMAC SHA256 signature using the Crypto node.
  • The prehash_string is generated like this:
    {{ $json.timestamp + $json.api_key + $json.recv_window + 'category=' + $json.category }}
    
  • Signature is computed with:
    • Value: {{ $json.prehash_string }}
    • Secret: {{ $json.api_secret }}
    • Encoding: HEX
  • HTTP Request:
    • Method: GET

    • URL: https://api.bybit.com/v5/position/list?category={{$json.category}}

    • Headers:

      • X-BAPI-API-KEY: {{ $json.api_key }}
      • X-BAPI-TIMESTAMP: {{ $json.timestamp }}
      • X-BAPI-SIGN: {{ $json.signature }}
      • X-BAPI-RECV-WINDOW: 5000

Despite everything looking good, I still get a 401 response. I’ve double-checked the API key and secret… and retried with new keys… (I’ve deactivated my API keys… I can generate new ones if needed).

Any ideas what could be wrong? Is there something wrong with the prehash_string format or headers?
Thanks a lot for your help!


{
  "name": "Bybit - open positions",
  "nodes": [
    {
      "parameters": {},
      "type": "n8n-nodes-base.manualTrigger",
      "typeVersion": 1,
      "position": [
        -380,
        -260
      ],
      "id": "6d89e884-41c5-495d-84b0-e6b7041db02d",
      "name": "When clicking ‘Test workflow’"
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "1dd03f20-7a91-4878-b025-9157a9b2edf5",
              "name": "=timestamp",
              "value": "={{$now.toMillis()}}",
              "type": "string"
            },
            {
              "id": "5ef39a4b-bc0d-4bd7-89a4-d72765e5f1af",
              "name": "recv_window",
              "value": "5000",
              "type": "string"
            },
            {
              "id": "86437147-98a7-4893-976b-2acd86960afa",
              "name": "api_key",
              "value": "UY6COIxxRdYH1K60Ka",
              "type": "string"
            },
            {
              "id": "537d80e9-576d-4124-a239-a95c28d8e799",
              "name": "category",
              "value": "unified",
              "type": "string"
            },
            {
              "id": "ebc16101-1838-419e-a3f9-2081521b481b",
              "name": "api_secret",
              "value": "IKPzrjZ7qzDEIITsMQWUgeIUjFul21Ga8wGX",
              "type": "string"
            },
            {
              "id": "b48c578b-d94c-4d8b-92da-c98420f82142",
              "name": "=timestamp_iso",
              "value": "={{$now.toISO()}}",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        -160,
        -260
      ],
      "id": "fabf0c87-f91d-4d66-a008-1f012b1357f9",
      "name": "Edit Fields"
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "752bbb17-7c67-4d21-ae0c-d84f704a6bb4",
              "name": "=prehash_string",
              "value": "={{ $json.timestamp + $json.api_key + $json.recv_window + 'category=' + $json.category}}",
              "type": "string"
            }
          ]
        },
        "includeOtherFields": true,
        "options": {}
      },
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        60,
        -260
      ],
      "id": "3cab4d44-5e26-4654-b7ba-da1771d71f91",
      "name": "Edit Fields1"
    },
    {
      "parameters": {
        "action": "hmac",
        "type": "SHA256",
        "value": "={{ $json.prehash_string }}",
        "dataPropertyName": "signature",
        "secret": "={{ $json.api_secret }}"
      },
      "type": "n8n-nodes-base.crypto",
      "typeVersion": 1,
      "position": [
        280,
        -260
      ],
      "id": "647c446b-0b05-41dc-a0d7-6ee87d266120",
      "name": "Crypto"
    },
    {
      "parameters": {
        "url": "=https://api.bybit.com/v5/position/list?category={{$json.category}}",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "X-BAPI-API-KEY",
              "value": "={{ $json.api_key }}"
            },
            {
              "name": "X-BAPI-TIMESTAMP",
              "value": "={{ $json.timestamp }}"
            },
            {
              "name": "X-BAPI-RECV-WINDOW",
              "value": "={{ $json.recv_window }}"
            },
            {
              "name": "X-BAPI-SIGN",
              "value": "={{ $json.signature }}"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        500,
        -260
      ],
      "id": "a25f1f8e-07d8-4856-9338-105e68ab7308",
      "name": "Get Balance"
    }
  ],
  "pinData": {},
  "connections": {
    "When clicking ‘Test workflow’": {
      "main": [
        [
          {
            "node": "Edit Fields",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Edit Fields": {
      "main": [
        [
          {
            "node": "Edit Fields1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Edit Fields1": {
      "main": [
        [
          {
            "node": "Crypto",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Crypto": {
      "main": [
        [
          {
            "node": "Get Balance",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "ecb40889-cb5c-4e1c-a319-f698c03f1b8d",
  "meta": {
    "instanceId": "f0812617b4a1134034f0a08be021c42a664e76b9450939d338d05573765d8e81"
  },
  "id": "aqAonBkx2hgkYijl",
  "tags": []
}```

Hi @thibs Welcome to n8n :n8n: community :tada:


Your workflow needs a few adjustments. I’m not entirely sure if the unified category is supported, as it may depend on the type of account (Classic accounts typically support linear and inverse).

However, that doesn’t seem to be the core issue here.

The main problems are:

  • Some required parameters, such as symbol, are missing.
  • Most importantly, the sign node is not properly configured.
  • The API key on your Bybit account likely lacks the necessary permissions, which is why you’re getting a 401 error.

I’ll share a working version of the workflow from my end, tested with my Bybit API keys, feel free to adapt it to your use case:

thank you :pray:

I gave all the API permissions …; now I have this but I think it s a step forward :slight_smile:

This is exactly what I explained:

So, you need to change unified to either linear or inverse, according to the official Bybit documentation: Get Position Info | Bybit API Documentation

ok thank you very much. I will try.

linear is working :slight_smile: so now I can do some tests… thank you :pray:

2 Likes

You’re welcome :saluting_face:

1 Like

Hello again, sorry new in n8n…
why did you add the limit variable (20) ? is it related to get the positions of max 20 different coins in the same flow ?

It’s the default value

Limit for data size per page. [1 , 200 ]. Default: 20

Hello Anan,
I try to “post” an order. I add the Required Parameters (category, symbol, side, orderType, qty, isLeverage and the same headers than for the Get but I get an 10004 error sign … (sorry because I m a beginner thanks for your help)

Hi, I think you need to edit prehash_string to include all parameters in the Post request…

You are right : POST requests: timestamp + API key + recv_window + jsonBodyString

rule:

timestamp+api_key+recv_window+jsonBodyString

example values:

timestamp = 1658385579423
api_key = XXXXXXXXXX
recv_window = 5000
jsonBodyString = {“category”: “option”}

I think my problem is in the syntax in the post : => H ‘Content-Type: application/json’ \