Getting started

Quickstart

From no credentials to a signed, authenticated request. Everything below runs against your real account. There is no sandbox, so the first call is a read.

  1. Open Key Management

    Sign in to Vanta and go to Settings → Key Management. You will need a prop account already, because the API trades an account you have and cannot create one.

  2. Create a key for one account

    Pick the account the key should trade, give the key a name you will recognise later (the strategy or machine it runs on), and create it. The key is bound to that account permanently: it cannot read or trade any other account you own, and there is no way to re-point it later.

    You can hold up to 3 active keys per account. Revoke one to free a slot.

  3. Copy the secret, which is shown once

    Creating a key returns a key id (vk_…) and a secret (vks_…). The secret is displayed a single time and is stored encrypted; it is never shown again and support cannot recover it. If you lose it, revoke the key and create another.

    Treat the secret like a password

    Anyone holding it can place orders on that account. Keep it out of source control, out of shared notebooks, and out of your browser. Read it from an environment variable or a secret manager, and revoke it immediately if it leaks.
  4. Put the credentials in your environment

    .env / shell
    export VANTA_KEY_ID="vk_your_key_id"
    export VANTA_SECRET="vks_your_secret"
    # Only needed for placing orders. This is the account the key is bound to.
    export VANTA_ACCOUNT_ID="your-prop-account-uuid"

    The account id is the accountId returned by the account endpoint in step 5, and is also in the dashboard URL when that account is selected.

  5. Make your first signed request

    This reads your account snapshot: balance, equity, challenge progress and drawdown. It changes nothing, so it is the safe call to get your signing right on.

    # Replace with your key and secret (secret is shown only when you create the key).
    # The API key is bound to a single prop account, so no accountId is required.
    export VANTA_KEY_ID="YOUR_KEY_ID"
    export VANTA_SECRET="YOUR_SECRET"
    export BASE_URL="https://app.vantatrading.io"
    PATH_REQ="/api/v1/trading/account"
    
    TS=$(($(date +%s) * 1000))
    NONCE=$(openssl rand -hex 16)
    # GET has an empty body, so this is the sha256 of the empty string
    BODY_HASH=$(printf '' | openssl dgst -sha256 -binary | xxd -p -c 256)
    CANONICAL=$(printf 'v1\nGET\n%s\n%s\n%s\n%s' "$PATH_REQ" "$TS" "$NONCE" "$BODY_HASH")
    SIG=$(echo -n "$CANONICAL" | openssl dgst -sha256 -hmac "$VANTA_SECRET" -binary | base64 | tr -d '\n')
    
    curl -s "$BASE_URL$PATH_REQ" \
    -H "X-Vanta-Key-Id: $VANTA_KEY_ID" \
    -H "X-Vanta-Timestamp: $TS" \
    -H "X-Vanta-Nonce: $NONCE" \
    -H "X-Vanta-Signature: v1=$SIG"

    A successful call returns HTTP 200 and the standard envelope:

    200 OK
    {
      "success": true,
      "data": {
        "accountId": "6f1c2e34-9a4b-4c1d-8e2f-1a2b3c4d5e6f",
        "assetClass": "crypto",
        "marketName": "Crypto",
        "accountSize": 25000,
        "formattedAccountSize": "25K",
        "evaluation": {
          "title": "Crypto 25K Evaluation",
          "status": "evaluation",
          "isEliminated": false,
          "accountSize": "$25,000",
          "activeAccounts": "1/1",
          "effectiveAccountSizeNumeric": 25000
        },
        "account": {
          "currentBalance": 25120.5,
          "currentEquity": 25180.25,
          "balanceChange": 120.5,
          "balanceChangePercent": 0.48,
          "totalPnL": 180.25,
          "totalPnLPercent": 0.72,
          "openPnL": 59.75,
          "openPnLPercent": 0.24,
          "openPositions": 1,
          "portfolioBalance": 25120.5,
          "portfolioBalanceChangePercent": 0.48,
          "portfolioBalanceBreakdown": {
            "currentBalance": 25120.5,
            "marginLeverage": 5,
            "sumPositionValue": 7202.5
          },
          "leverage": "Current - 0.2870x / Max - 10x",
          "capitalUsed": 7202.5,
          "totalRealizedPnl": 120.5,
          "isPassed": false
        },
        "challenge": {
          "variant": "default",
          "bucket": "SUBACCOUNT_CHALLENGE",
          "drawdownCriteria": "trailing",
          "profitTarget": 2500,
          "profitTargetPercent": 10,
          "remaining": 2319.75,
          "maxLeverage": "10x",
          "trailingDrawdownPercent": 5,
          "maxDrawdown": -1250,
          "daysRemaining": 27,
          "totalDays": 90,
          "drawdownBreakdown": {
            "highWaterMark": 25180.25,
            "allowedDrawdown": 1259.01,
            "currentDrawdown": 0,
            "remainingDrawdown": 1259.01,
            "remainingDrawdownPercentHWM": 5
          }
        },
        "performance": {
          "totalTrades": 4,
          "winRate": 75,
          "totalWins": 3,
          "avgTradePnL": 45.06,
          "tradeDuration": "63h 12m",
          "challengeStartMs": 1717200000000,
          "dailyReturns": [
            {
              "date": "2026-06-28",
              "value": 0.31
            },
            {
              "date": "2026-06-29",
              "value": 0.17
            }
          ]
        },
        "meta": {
          "generatedAtMs": 1717718400000,
          "stale": false
        }
      }
    }

Placing your first order

Once a read works, the signing is correct and writes are the same mechanism with a body.

This places a real market order on your account. Size it accordingly the first time, and be ready to close it from the terminal.

# Replace with your key and secret (secret is shown only when you create the key).
export VANTA_KEY_ID="YOUR_KEY_ID"
export VANTA_SECRET="YOUR_SECRET"
export ACCOUNT_ID="YOUR_PROP_ACCOUNT_UUID"
export BASE_URL="https://app.vantatrading.io"

BODY="{\"accountId\":\"YOUR_PROP_ACCOUNT_UUID\",\"trade\":{\"execution_type\":\"MARKET\",\"trade_pair\":\"BTCUSDC\",\"order_type\":\"LONG\",\"value\":1000}}"
PATH_REQ="/api/v1/trading/orders"

TS=$(($(date +%s) * 1000))
NONCE=$(openssl rand -hex 16)
BODY_HASH=$(echo -n "$BODY" | openssl dgst -sha256 -binary | xxd -p -c 256)
CANONICAL=$(printf 'v1\nPOST\n%s\n%s\n%s\n%s' "$PATH_REQ" "$TS" "$NONCE" "$BODY_HASH")
SIG=$(echo -n "$CANONICAL" | openssl dgst -sha256 -hmac "$VANTA_SECRET" -binary | base64 | tr -d '\n')

curl -s -X POST "$BASE_URL$PATH_REQ" \
-H "Content-Type: application/json" \
-H "X-Vanta-Key-Id: $VANTA_KEY_ID" \
-H "X-Vanta-Timestamp: $TS" \
-H "X-Vanta-Nonce: $NONCE" \
-H "X-Vanta-Signature: v1=$SIG" \
-d "$BODY"

Every execution type, sizing field and bracket option is covered on Placing orders.

If the first call fails

Almost every first-attempt failure is one of these three.

  • 401 Bad signature means the credential was recognised but the HMAC did not match, so the mistake is in what you signed. Check your signer against the worked vectors on Authentication; they tell you which of the canonical string, the body hash or the secret is wrong.
  • 401 Timestamp invalid means your clock is more than five minutes off, or you signed the request well before sending it.
  • 409 Nonce means the nonce was reused. Generate a fresh random one per request, and never resend a failed request with the same nonce.

The complete list is on Errors & rate limits.