> ## Documentation Index
> Fetch the complete documentation index at: https://docs.limitless.exchange/llms.txt
> Use this file to discover all available pages before exploring further.

# Authentication

> Scoped token authentication for the Limitless Exchange API

## Overview

All programmatic access to the Limitless Exchange API requires authentication via **scoped API tokens** with HMAC-SHA256 request signing.

<Warning>
  API keys are **deprecated** and no longer available for new users. Existing API key users should migrate to scoped API tokens. See [Legacy API Keys](#legacy-api-keys) below.
</Warning>

## Scoped API Tokens (HMAC)

Scoped API tokens are the authentication method for the Limitless Exchange API. Every request is authenticated by signing it with HMAC-SHA256 using your token's secret. Tokens are scoped to control what actions they can perform.

### Scopes

| Scope               | Description                                                                                                                                               |
| ------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `trading`           | Place and cancel orders. Required base scope for `delegated_signing`.                                                                                     |
| `account_creation`  | Create sub-account profiles under your partner account.                                                                                                   |
| `delegated_signing` | Server signs orders on behalf of sub-accounts (requires `trading` scope). Enables Web2 partners to submit orders without end users managing private keys. |
| `withdrawal`        | Withdraw ERC20 balances from managed server-wallet sub-accounts to partner-owned addresses.                                                               |

### HMAC Request Signing

**Required headers:**

| Header           | Description                                                   |
| ---------------- | ------------------------------------------------------------- |
| `lmts-api-key`   | Your token ID (received at token creation)                    |
| `lmts-timestamp` | ISO-8601 timestamp (must be within 30 seconds of server time) |
| `lmts-signature` | Base64-encoded HMAC-SHA256 signature                          |

<Info>
  SDK users should pass HMAC credentials to the SDK client (`hmacCredentials` / `hmac_credentials` / `WithHMACCredentials`) and let the SDK build these headers automatically. Manual header signing is only needed when you call the REST API directly.
</Info>

**Canonical message format:**

```
{ISO-8601 timestamp}\n{HTTP METHOD}\n{request path with query string}\n{request body}
```

<Warning>
  The path component must include the **full request URL path and query string** (e.g., `/orders/all/btc-100k?onBehalfOf=42`), not just the pathname. For requests without a query string, use the plain path (e.g., `/orders`). For GET requests, the body component is an empty string.
</Warning>

**Example (TypeScript):**

```typescript theme={null}
import { createHmac } from 'crypto';

function signRequest(
  tokenId: string,
  secret: string,     // base64-encoded, received at token creation
  method: string,
  path: string,
  body: string = '',
): Record<string, string> {
  const timestamp = new Date().toISOString();
  const message = `${timestamp}\n${method}\n${path}\n${body}`;

  const signature = createHmac('sha256', Buffer.from(secret, 'base64'))
    .update(message)
    .digest('base64');

  return {
    'lmts-api-key': tokenId,
    'lmts-timestamp': timestamp,
    'lmts-signature': signature,
  };
}

// Usage
const headers = signRequest(tokenId, secret, 'POST', '/orders', JSON.stringify(orderPayload));
const res = await fetch('https://api.limitless.exchange/orders', {
  method: 'POST',
  headers: { ...headers, 'Content-Type': 'application/json' },
  body: JSON.stringify(orderPayload),
});
```

**Example (Python):**

```python theme={null}
import hmac, hashlib, base64
from datetime import datetime, timezone

def sign_request(token_id: str, secret: str, method: str, path: str, body: str = "") -> dict:
    timestamp = datetime.now(timezone.utc).isoformat()
    message = f"{timestamp}\n{method}\n{path}\n{body}"

    signature = base64.b64encode(
        hmac.new(
            base64.b64decode(secret),
            message.encode("utf-8"),
            hashlib.sha256,
        ).digest()
    ).decode("utf-8")

    return {
        "lmts-api-key": token_id,
        "lmts-timestamp": timestamp,
        "lmts-signature": signature,
    }
```

### Getting a Scoped API Token

Most builders should generate a token from the UI. The programmatic flow below it is for headless / CI environments where you can't open a browser.

#### From the UI (recommended)

<Steps>
  <Step title="Connect your wallet">
    Go to [limitless.exchange](https://limitless.exchange) and connect your wallet. No smart wallet required.
  </Step>

  <Step title="Derive a token">
    Open the API token modal → **API Tokens** tab → **Derive**. Copy the `tokenId` and `secret`. The browser handles the login session for you, so there's no identity token to copy by hand.
  </Step>

  <Step title="Use the credentials">
    Pass them to the SDK as `hmacCredentials` (TypeScript), `hmac_credentials` (Python), or `WithHMACCredentials` (Go) — the SDK signs every request automatically. This is a one-time setup.
  </Step>
</Steps>

#### Programmatically (headless / CI)

When you can't use a browser, call the same endpoint the UI calls:

<Steps>
  <Step title="Obtain a Privy identity token">
    Authenticate with Privy and capture the `token` field from the authenticate response (not `privy_access_token`).
  </Step>

  <Step title="Derive a token">
    `POST /auth/api-tokens/derive` with the identity token in the `identity` header as `Bearer <token>`. The response includes a `tokenId` and `secret`. Use these HMAC credentials for all subsequent requests.
  </Step>
</Steps>

The `trading` scope is available to all users — no application required. For partner-level scopes (`account_creation`, `delegated_signing`), [apply for programmatic API access](/developers/programmatic-api) to get your account enabled.

<Note>
  The token secret is returned **once** at creation time. Store it securely — it cannot be retrieved again.
</Note>

### Delegated Signing

With the `delegated_signing` scope, partners can submit orders **without** providing `signature` and `signatureType` in the order payload. The server signs the order using a Privy server wallet linked to the target sub-account.

Use the `onBehalfOf` field in the order payload to specify which sub-account the order is for. The target profile must be linked to your partner account.

## Checksummed Addresses

All Ethereum addresses in API requests must use **checksummed format** (EIP-55 mixed-case):

* `x-account` header
* `maker` and `signer` fields in orders

**Example:** `0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed`

<Note>
  Your private key is still required for **EIP-712 order signing** (unless using delegated signing), but the scoped token handles request authentication.
</Note>

## Legacy API Keys

<Warning>
  API keys are **deprecated** and are no longer issued to new users. If you have an existing API key, it will continue to work, but we recommend migrating to scoped API tokens for improved security and access control.
</Warning>

If you hold a legacy static API key, it still works for now, but migrate to scoped API tokens (HMAC) above — they add request signing, scopes, and expiry. New keys are no longer issued, and all examples in these docs use HMAC.
