Complete guide to authenticating with the Limitless Exchange API.
Use an official SDK? Both the Python SDK (
pip install limitless-sdk) and TypeScript SDK (npm install @limitless-exchange/sdk) handle authentication automatically — just set theLIMITLESS_API_KEYenvironment variable.
Limitless Exchange supports two authentication methods:
| Method | Header | Status |
|---|---|---|
| API Key | X-API-Key: lmts_... |
Required for programmatic access |
| Cookie Session | Cookie: limitless_session=... |
Deprecated (removal imminent) |
DEPRECATION NOTICE: Cookie-based session authentication is deprecated and will be removed within weeks. Please migrate to API keys immediately.
API keys are the simplest and recommended way to authenticate with the Limitless Exchange API.
- Log in to limitless.exchange using your wallet
- Click your profile menu (top right)
- Select "Api keys"
- Generate a new key
Include the X-API-Key header in all requests:
import requests
API_URL = "https://api.limitless.exchange"
# All authenticated requests use the X-API-Key header
headers = {"X-API-Key": "lmts_your_key_here"}
# Example: Get positions
response = requests.get(f"{API_URL}/portfolio/positions", headers=headers)
positions = response.json()
# Example: Submit an order
response = requests.post(f"{API_URL}/orders", json=order_payload, headers=headers)// JavaScript/TypeScript
const headers = { 'X-API-Key': 'lmts_your_key_here' };
const res = await fetch('https://api.limitless.exchange/portfolio/positions', { headers });
const positions = await res.json();┌─────────┐ ┌─────────┐
│ Client │ │ API │
└────┬────┘ └────┬────┘
│ │
│ Any request │
│ Header: X-API-Key: lmts_... │
│ ─────────────────────────────> │
│ │
│ Response data │
│ <───────────────────────────── │
│ │
No login flow, no session management, no cookie handling needed.
WARNING: Cookie-based session authentication is deprecated and will be removed within weeks. Migrate to API keys immediately.
- Request a signing message with unique nonce
- Sign the message with your Ethereum wallet
- Submit the signature to login
- Use the session cookie for authenticated requests
┌─────────┐ ┌─────────┐
│ Client │ │ API │
└────┬────┘ └────┬────┘
│ │
│ GET /auth/signing-message │
│ ─────────────────────────────> │
│ │
│ "Welcome to Limitless... │
│ Nonce: 0xa1b2c3..." │
│ <───────────────────────────── │
│ │
│ [Sign message with wallet] │
│ │
│ POST /auth/login │
│ Headers: x-account, │
│ x-signing-message, │
│ x-signature │
│ ─────────────────────────────> │
│ │
│ Set-Cookie: limitless_session │
│ { account, id, ... } │
│ <───────────────────────────── │
│ │
│ [Use cookie for requests] │
│ │
Request a unique signing message from the API.
GET /auth/signing-messageWelcome to Limitless.exchange! Please sign this message to verify your identity.
Nonce: 0xa1b2c3d4e5f67890abcdef1234567890abcdef1234567890abcdef1234567890
import requests
def get_signing_message():
response = requests.get("https://api.limitless.exchange/auth/signing-message")
response.raise_for_status()
return response.textSign the message using your Ethereum wallet.
from eth_account import Account
from eth_account.messages import encode_defunct
def sign_message(private_key, message):
account = Account.from_key(private_key)
# Encode as personal message
signable = encode_defunct(text=message)
# Sign
signed = account.sign_message(signable)
return signed.signature.hex()
# Usage
signing_message = get_signing_message()
signature = sign_message(private_key, signing_message)import { Wallet } from 'ethers';
async function signMessage(privateKey, message) {
const wallet = new Wallet(privateKey);
const signature = await wallet.signMessage(message);
return signature;
}import { createWalletClient, http } from 'viem';
import { privateKeyToAccount } from 'viem/accounts';
async function signMessage(privateKey, message) {
const account = privateKeyToAccount(privateKey);
const client = createWalletClient({
account,
transport: http()
});
return await client.signMessage({ message });
}Submit the signature to authenticate.
POST /auth/login
Content-Type: application/json
Headers:
x-account: 0x742d35Cc6634C0532925a3b844Bc454e4438f44e
x-signing-message: 0x57656c636f6d6520746f204c696d69746c6573732e657863...
x-signature: 0x123abc456def789...
Body:
{
"client": "eoa"
}| Header | Format | Example |
|---|---|---|
x-account |
Checksummed address | 0x742d35Cc6634C0532925a3b844Bc454e4438f44e |
x-signing-message |
Hex-encoded message | 0x57656c636f6d65... |
x-signature |
Hex signature with 0x prefix | 0xabc123... |
def login(private_key, signing_message):
account = Account.from_key(private_key)
# Sign message
message = encode_defunct(text=signing_message)
signed = account.sign_message(message)
# Prepare headers
headers = {
'x-account': account.address,
'x-signing-message': '0x' + signing_message.encode('utf-8').hex(),
'x-signature': '0x' + signed.signature.hex(),
'Content-Type': 'application/json'
}
# Login
response = requests.post(
"https://api.limitless.exchange/auth/login",
headers=headers,
json={"client": "eoa"}
)
response.raise_for_status()
# Extract session
session_cookie = response.cookies.get('limitless_session')
user_data = response.json()
return session_cookie, user_data{
"account": "0x742d35Cc6634C0532925a3b844Bc454e4438f44e",
"displayName": "0x742d...f44e",
"smartWallet": null,
"client": "eoa",
"id": 12345
}The response includes a Set-Cookie header with limitless_session.
Deprecated: Use API key authentication instead. See API Key Authentication above.
Include the session cookie in authenticated requests.
# DEPRECATED - use API key instead
def get_positions(session_cookie):
response = requests.get(
"https://api.limitless.exchange/portfolio/positions",
cookies={"limitless_session": session_cookie}
)
return response.json()
# RECOMMENDED - use API key
def get_positions(api_key):
response = requests.get(
"https://api.limitless.exchange/portfolio/positions",
headers={"X-API-Key": api_key}
)
return response.json()import os
import requests
class LimitlessClient:
"""Simple API client using API key authentication."""
API_URL = "https://api.limitless.exchange"
def __init__(self, api_key=None):
self.api_key = api_key or os.environ.get('LIMITLESS_API_KEY')
if not self.api_key:
raise ValueError("API key required. Set LIMITLESS_API_KEY env var or pass api_key parameter.")
def _headers(self):
"""Get headers with API key."""
return {"X-API-Key": self.api_key}
def get_positions(self):
"""Get all portfolio positions."""
response = requests.get(f"{self.API_URL}/portfolio/positions", headers=self._headers())
response.raise_for_status()
return response.json()
def get_trades(self):
"""Get trade history."""
response = requests.get(f"{self.API_URL}/portfolio/trades", headers=self._headers())
response.raise_for_status()
return response.json()
def submit_order(self, order_payload):
"""Submit a signed order."""
response = requests.post(f"{self.API_URL}/orders", json=order_payload, headers=self._headers())
response.raise_for_status()
return response.json()
def cancel_order(self, order_id):
"""Cancel a specific order."""
response = requests.delete(f"{self.API_URL}/orders/{order_id}", headers=self._headers())
response.raise_for_status()
return response.json()
# Usage
client = LimitlessClient() # Uses LIMITLESS_API_KEY env var
positions = client.get_positions()WARNING: This method is deprecated. Migrate to API keys.
import os
import requests
from eth_account import Account
from eth_account.messages import encode_defunct
class LimitlessAuth:
"""DEPRECATED: Use LimitlessClient with API key instead."""
API_URL = "https://api.limitless.exchange"
def __init__(self, private_key=None):
self.private_key = private_key or os.environ.get('PRIVATE_KEY')
if not self.private_key:
raise ValueError("Private key required")
self.account = Account.from_key(self.private_key)
self.session = None
self.user = None
def get_signing_message(self):
"""Get signing message with nonce."""
response = requests.get(f"{self.API_URL}/auth/signing-message")
response.raise_for_status()
return response.text
def sign_message(self, message):
"""Sign message with wallet."""
signable = encode_defunct(text=message)
signed = self.account.sign_message(signable)
return '0x' + signed.signature.hex()
def login(self):
"""Authenticate and store session."""
# Get and sign message
message = self.get_signing_message()
signature = self.sign_message(message)
# Prepare headers
headers = {
'x-account': self.account.address,
'x-signing-message': '0x' + message.encode('utf-8').hex(),
'x-signature': signature,
'Content-Type': 'application/json'
}
# Login
response = requests.post(
f"{self.API_URL}/auth/login",
headers=headers,
json={"client": "eoa"}
)
response.raise_for_status()
self.session = response.cookies.get('limitless_session')
self.user = response.json()
return self.session, self.user
def verify(self):
"""Verify current session is valid."""
if not self.session:
return False
response = requests.get(
f"{self.API_URL}/auth/verify-auth",
cookies={"limitless_session": self.session}
)
return response.ok
def logout(self):
"""End session."""
if not self.session:
return
requests.post(
f"{self.API_URL}/auth/logout",
cookies={"limitless_session": self.session}
)
self.session = None
self.user = None
def get_cookies(self):
"""Get cookies dict for requests."""
return {"limitless_session": self.session} if self.session else {}For smart contract wallets, use client: "smart_wallet" and provide the smart wallet address.
headers = {
'x-account': signer_address, # EOA signer
'x-signing-message': hex_message,
'x-signature': signature,
}
response = requests.post(
f"{API_URL}/auth/login",
headers=headers,
json={
"client": "smart_wallet",
"smartWallet": smart_wallet_address # Contract address
}
)If you're currently using cookie-based authentication, migrate by:
- Generate an API key via the UI (profile menu → Api keys)
- Replace cookie header with API key header:
# Before (deprecated)
- Cookie: limitless_session=your_session_token
# After
+ X-API-Key: lmts_your_key_here- Remove session management code - no more login flow or cookie handling needed
# BEFORE (deprecated cookie auth)
session_cookie, user = authenticate(private_key)
response = requests.get(
f"{API_URL}/portfolio/positions",
cookies={"limitless_session": session_cookie}
)
# AFTER (API key auth)
response = requests.get(
f"{API_URL}/portfolio/positions",
headers={"X-API-Key": "lmts_your_key_here"}
)Deprecated: API key authentication does not require session management.
Sessions expire after inactivity. Handle 401 responses:
def authenticated_request(url, session, auth):
response = requests.get(url, cookies={"limitless_session": session})
if response.status_code == 401:
# Re-authenticate
session, user = auth.login()
response = requests.get(url, cookies={"limitless_session": session})
return responsedef ensure_authenticated(auth):
if not auth.verify():
auth.login()
return auth.session-
Protect API keys
- Store in environment variables (
LIMITLESS_API_KEY) - Never commit to version control
- Rotate keys periodically via the Limitless Exchange UI
- Store in environment variables (
-
Never expose private keys
- Use environment variables
- Never commit to version control
-
Validate addresses
- Use checksummed addresses (EIP-55)
- Verify address format before sending
-
Error handling
- Handle authentication failures gracefully
- Implement retry logic for network issues
- Verify the key starts with
lmts_ - Check the key was copied correctly without extra whitespace
- Ensure the key has not been revoked in the UI
- Verify message encoding matches API expectation
- Check signature format has 0x prefix
- Ensure address is checksummed
- Session may have expired
- Cookie may not be included in request
- Re-authenticate or migrate to API key authentication
- Using
smart_walletclient but missing address - Provide
smartWalletin request body