Welcome to API Academy
Module 03 · API basics · ~18 min
API 101.
By the end of this module, your own code will be able to talk to Limitless. That’s the foundation a trading bot needs to read live prices, place orders, and watch your positions for you, without you sitting in front of the screen.
To get there, you’ll authenticate to the Limitless API and pull your first piece of live data. Every future request, orders, positions, websockets, sits on the same auth foundation you set up here.
API basics tier · Reference cardNew here? Install Node, Python, or Go, mint your API key, and set up a .env file before you dive in, Setup Guide →
How do you authenticate to the Limitless API?
Two credentials: an HMAC API key that signs every request (sent as the lmts-api-key + lmts-timestamp + lmts-signature headers), and an EIP-712 wallet signature added only when you place an order. Read-only calls (markets, positions, history) need just the API key; placing an order needs both. The lmts-signature is a base64 HMAC-SHA256 of the canonical request, computed locally by the SDK from your secret, which it loads from environment variables, so never hard-code keys. Every call hits the base URL https://api.limitless.exchange with a standard verb: GET reads, POST creates, DELETE removes. The legacy X-API-Key header is deprecated; you may still see it in older tutorials.
Verified 2026-06-10 against the OpenAPI spec + SDK.
Section 01
The anatomy of a request.
Every call you’ll make against Limitless is three things glued together: a URL telling the server which resource you want, a method telling it what to do with that resource, and headers carrying your credentials. The SDK hides most of this plumbing, but you still need to know what it’s sending. The two cards below show the URL/method pattern you’ll reuse for the next fifteen modules and the auth headers that unlock your own account data.
Authentication: every request is HMAC-signed.
Every authenticated call carries three headers, lmts-api-key + lmts-timestamp + lmts-signature, computed per the scheme at docs.limitless.exchange/developers/authentication. The legacy X-API-Key header is deprecated and no longer issued to new users, you may still see it referenced in older tutorials.
URL & method
Every request hits the base URL https://api.limitless.exchange with a standard HTTP verb. GET reads, POST creates, DELETE removes. We start with reads, placing orders comes in Module 05.
Headers
Every authenticated call carries three HMAC headers. The SDK handles the plumbing once you give it the token id and base64 secret, see docs.limitless.exchange/developers/authentication for the canonical signing scheme.
Section 02
Authentication.
Two separate credentials, two different jobs. The API key proves your application is who it says it is, it goes on the header of every authenticated request, reads and writes alike. The wallet signature proves you’re authorizing a specific order to move funds on-chain, it’s attached only to order placements. Read-only calls like “show me my positions” need the first but not the second. The cards below compare the two; the red strip underneath is the one rule that stops accidents from becoming incidents.
API key
What you put on every authenticated request. New tokens are HMAC scoped tokens: a token id plus a signature computed locally from your base64 secret. The signing scheme lives at docs.limitless.exchange/developers/authentication. The legacy bare-X-API-Key path is deprecated and no longer issued to new users.
- Required for every auth’d read (positions, profile, history)
- Required for placing orders too, the SDK signs internally
- Auto-loaded from LMTS_TOKEN_ID (token id) + LMTS_TOKEN_SECRET (base64 HMAC secret); the legacy LIMITLESS_API_KEY still works
- Secret is shown once at token creation, store it immediately
Wallet signature
When you place an order on the CLOB, the SDK signs an EIP-712 order payload with your wallet’s private key and submits the signature alongside your API-key headers. You don’t write this code, but you do need to give the SDK a private key.
- Only used for placing CLOB orders (covered in Module 05)
- Handled internally by every official SDK
- Set as PRIVATE_KEY alongside the API key
- Same private key as your on-chain wallet, guard it like one
The signing algorithm, end-to-end
The SDK does this for you, but it helps to read once. Build the canonical message {ISO-8601 ts}\n{METHOD}\n{path?query}\n{body}, HMAC-SHA256 it with the base64-decoded secret, base64-encode the result. Body is the empty string for GET / DELETE.
// TypeScript / Node
import { createHmac } from 'node:crypto';
const ts = new Date().toISOString();
const message = `${ts}\n${method}\n${pathWithQuery}\n${body}`;
const signature = createHmac('sha256', Buffer.from(secret, 'base64'))
.update(message)
.digest('base64');
// headers
// lmts-api-key: apiKey
// lmts-timestamp: ts
// lmts-signature: signature
# Python
import base64, datetime, hashlib, hmac
now = datetime.datetime.now(datetime.timezone.utc)
ts = now.strftime("%Y-%m-%dT%H:%M:%S.") + f"{now.microsecond // 1000:03d}Z"
message = f"{ts}\n{method}\n{path_with_query}\n{body}"
digest = hmac.new(base64.b64decode(secret), message.encode("utf-8"), hashlib.sha256).digest()
signature = base64.b64encode(digest).decode("ascii")
# headers
# lmts-api-key: apiKey
# lmts-timestamp: ts
# lmts-signature: signature
Server tolerates 30 s of clock skew, so keep your machine’s clock honest. Canonical reference: docs.limitless.exchange/developers/authentication.
Never commit a private key, an API token id, or an HMAC secret to git.
Use .env files (gitignored), a secrets manager, or your platform’s environment variable UI. Rotate any token that ever touches a public repository.
Section 03
Your first GET request.
Let’s fetch the list of currently active markets. /markets/active is a public endpoint, so this is the perfect smoke test that your SDK install is wired up correctly. We’ll still pass an API key to HttpClient now so you don’t have to redo it in Section 04.
How to run this
- Set LIMITLESS_API_KEY in your environment, or drop it into a .env file in the same folder as your script.
- Save as list-markets.ts, then run npx tsx list-markets.ts.
- Save as list_markets.py, then run python list_markets.py.
- Save as main.go in a Go module (go mod init example), then go run main.go.
- If you see market titles printed in your terminal, you’re alive on the wire.
// Module 03, Your First GET Request
// Fetch the most recent active markets on Limitless.
//
// $ npm install @limitless-exchange/sdk
import { HttpClient, MarketFetcher } from '@limitless-exchange/sdk';
const httpClient = new HttpClient({
baseURL: 'https://api.limitless.exchange',
apiKey: process.env.LIMITLESS_API_KEY,
});
async function listActiveMarkets() {
const marketFetcher = new MarketFetcher(httpClient);
const markets = await marketFetcher.getActiveMarkets({
limit: 10,
sortBy: 'newest',
});
for (const market of markets) {
console.log(market.slug, ', ', market.title);
}
}
listActiveMarkets().catch(console.error);
# Module 03, Your First GET Request
# Fetch the most recent active markets on Limitless.
#
# $ pip install limitless-sdk
import asyncio
from limitless_sdk import HttpClient
from limitless_sdk.markets import MarketFetcher
# HttpClient auto-loads LIMITLESS_API_KEY from your environment.
http_client = HttpClient()
async def list_active_markets() -> None:
market_fetcher = MarketFetcher(http_client)
markets = await market_fetcher.get_active_markets()
for market in markets["data"]:
print(market["slug"], ", ", market["title"])
await http_client.close()
if __name__ == "__main__":
asyncio.run(list_active_markets())
// Module 03, Your First GET Request
// Fetch the most recent active markets on Limitless.
//
// $ go get github.com/limitless-labs-group/limitless-exchange-go-sdk@v1.0.5
package main
import (
"context"
"fmt"
"log"
limitless "github.com/limitless-labs-group/limitless-exchange-go-sdk/limitless"
)
func main() {
ctx := context.Background()
// NewHttpClient reads LIMITLESS_API_KEY from the environment.
client := limitless.NewHttpClient()
marketFetcher := limitless.NewMarketFetcher(client)
result, err := marketFetcher.GetActiveMarkets(ctx, &limitless.ActiveMarketsParams{
Limit: 10,
Page: 1,
})
if err != nil {
log.Fatal(err)
}
for _, m := range result.Data {
fmt.Printf("%s, %s\n", m.Slug, m.Title)
}
}
Section 04
Your first authenticated request.
Now a private endpoint. Reading your own portfolio requires the SDK to attach your API key to every request, that’s the only auth needed. Wallet signing doesn’t kick in until you place orders in Module 05.
How to run this
- Make sure LIMITLESS_API_KEY is still in your environment from Section 03.
- Save as get-portfolio.ts, then run npx tsx get-portfolio.ts.
- Save as get_portfolio.py, then run python get_portfolio.py.
- Save as main.go in a Go module, then run go run main.go.
- Your point total, CLOB count, and AMM count print to the terminal. Empty counts are fine, you just haven’t opened any positions yet.
Token secrets are returned once.
Limitless shows the secret a single time when you mint the key. Copy it into your .env immediately. The Python SDK is async-first: every fetcher method needs await.
// Module 03, Your First Authenticated Request
// Fetch your own portfolio (CLOB + AMM positions).
// Requires LIMITLESS_API_KEY to be set in your environment.
import { HttpClient, PortfolioFetcher } from '@limitless-exchange/sdk';
const httpClient = new HttpClient({
baseURL: 'https://api.limitless.exchange',
apiKey: process.env.LIMITLESS_API_KEY,
});
async function getMyPortfolio() {
const portfolio = new PortfolioFetcher(httpClient);
const positions = await portfolio.getPositions();
console.log('Points:', positions.points);
console.log('CLOB positions:', positions.clob.length);
console.log('AMM positions: ', positions.amm.length);
for (const pos of positions.clob) {
console.log('•', pos.market.title);
}
}
getMyPortfolio().catch(console.error);
# Module 03, Your First Authenticated Request
# Fetch your own portfolio (CLOB + AMM positions).
# Requires LIMITLESS_API_KEY to be set in your environment.
import asyncio
from limitless_sdk import HttpClient
from limitless_sdk.portfolio import PortfolioFetcher
http_client = HttpClient() # auto-loads LIMITLESS_API_KEY
async def get_my_portfolio() -> None:
portfolio = PortfolioFetcher(http_client)
positions = await portfolio.get_positions()
print("Points:", positions.get("points"))
print("CLOB positions:", len(positions.get("clob", [])))
print("AMM positions: ", len(positions.get("amm", [])))
for pos in positions.get("clob", []):
print("•", pos["market"]["title"])
await http_client.close()
if __name__ == "__main__":
asyncio.run(get_my_portfolio())
// Module 03, Your First Authenticated Request
// Fetch your own portfolio (CLOB + AMM positions).
// Requires LIMITLESS_API_KEY to be set in your environment.
package main
import (
"context"
"fmt"
"log"
limitless "github.com/limitless-labs-group/limitless-exchange-go-sdk/limitless"
)
func main() {
ctx := context.Background()
client := limitless.NewHttpClient()
portfolio := limitless.NewPortfolioFetcher(client)
positions, err := portfolio.GetPositions(ctx)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Points: %v\n", positions.Points)
fmt.Printf("CLOB positions: %d\n", len(positions.Clob))
fmt.Printf("AMM positions: %d\n", len(positions.Amm))
for _, pos := range positions.Clob {
fmt.Printf("• %s\n", pos.Market.Title)
}
}
Limitless API auth: what people ask
Each answer also ships invisibly as schema.org FAQ data for search engines and AI assistants. Tap a question to expand.
-
What credentials do you need to call the Limitless API?
Two. A scoped API token (a token id plus a base64 HMAC secret) authenticates every request, and a wallet private key signs orders. Read-only calls like positions or profile need only the token; placing an order needs both. The SDK loads them fromLMTS_TOKEN_ID,LMTS_TOKEN_SECRET, andPRIVATE_KEY; the legacyLIMITLESS_API_KEYstill works too. -
What is the Limitless API base URL?
Every request hitshttps://api.limitless.exchangewith a standard HTTP verb: GET reads (markets, orderbook, positions), POST creates (orders), DELETE removes. Reads come first; order placement arrives in Module 05. -
How does Limitless API authentication work?
New keys are HMAC-scoped tokens. Each authenticated call carrieslmts-api-key(your token id) andlmts-signature, a base64 HMAC-SHA256 of the canonical request that the SDK computes from your base64 secret. The legacy bareX-API-Keypath is deprecated. The canonical signing scheme lives at docs.limitless.exchange/developers/authentication. -
What is the difference between the API key and the wallet signature?
The API key proves your application is who it says it is and goes on every authenticated request. The wallet signature proves you are authorizing a specific order to move funds on-chain, an EIP-712 payload signed with your private key, attached only to order placements. A read-only call like “show my positions” needs the first, not the second. -
How do you keep your Limitless API keys safe?
Read every secret from.envviaprocess.envoros.getenv, and add.envto.gitignorebefore your first commit. The token secret is shown once at creation, so store it immediately. Treat any key that lands in a screenshot, a Slack message, or a public log as compromised, and rotate it.
Section 05
Module checklist.
Tick each item once you’ve actually done it. The Continue button unlocks at 5/5.
I understand the base URL and that every authenticated request carries the HMAC trio (lmts-api-key + lmts-timestamp + lmts-signature) per docs.limitless.exchange/developers/authentication
I know that wallet signing is only used when placing CLOB orders, not for reading my own data
I installed the SDK and successfully fetched the active markets list in my preferred language
I successfully fetched my own portfolio (CLOB + AMM positions) using my API key
My API key lives in LIMITLESS_API_KEY, never in git, and I bookmarked docs.limitless.exchange
Module 03 complete
Wired up.
You just laid the rail your bot will run on. Every order it places, every position it watches, every price it reacts to flows through the connection you opened today, you only have to set it up once.
Concretely, you can now talk to Limitless, both its public markets feed and your own private account data. Three things you walk away with:
A working SDK install in your preferred language (TypeScript, Python, or Go) with an HttpClient you can reuse for every future module.
An LIMITLESS_API_KEY loaded safely from your environment, the same credential that unlocks orders, positions, and websockets from Module 04 onward.
Proof you can reach the exchange: live market titles printed from a public endpoint, and your own portfolio returned from an authenticated one.
Next up: picking the markets your bot will actually trade. Module 04 covers listing, filtering, and inspecting markets so your code knows which ones are worth its time.
Complete the checklist above to unlock