Welcome to API Academy
Module 04 · API basics · ~20 min
Markets.
By the end of this module, your bot will be able to scan every market on Limitless and pick out the few worth trading, the same filter a human trader runs in their head before risking a dollar.
To get there, you’ll list, filter, and inspect every market on Limitless, then map each response into a typed model. Point your bot at exactly the markets you care about instead of scanning thousands of rows you’ll never trade.
API basics tier · Reference cardHow do you list markets on the Limitless API?
GET /markets/active, wrapped by the SDK’s MarketFetcher.getActiveMarkets(), returns the catalogue a page at a time with typed params: limit, page, sortBy (newest, trending, or ending-soon), and tradeType (clob or amm). Push filters to the server instead of scanning everything: adding categoryId or tradeType to the query turns a 2,000-row scan into a 50-row page, and you walk pages until one comes back short. Once you have a slug, GET /markets/{slug} returns the full object, resolution source, outcome tokens, and the rest, to map into a typed model your bot can reason about. One trap: status “active” doesn’t mean tradeable, so filter on the explicit tradingEnabled field and refresh your cached list on a 60s cadence. Prefer the slug over the numeric id in most calls; slugs are stable and keep logs readable.
Endpoints verified 2026-06-09 against the OpenAPI spec + SDK.
Section 01
Listing markets.
GET /markets/active is the first endpoint any bot touches. Page through the catalogue, sort by newest or most-traded, and filter by the shape of market you care about. The SDK’s MarketFetcher wraps it with typed params.
How to run this
- Make sure LIMITLESS_API_KEY is set in your environment (same one from Module 03).
- Save the snippet as list-markets.ts, then run npx tsx list-markets.ts.
- Save the snippet as list_markets.py, then run python list_markets.py.
- Save the snippet as main.go inside a Go module, then run go run main.go.
- You see a count of fetched markets followed by one line per market showing its slug, volume, and liquidity.
// Module 04, Listing Markets
// Fetch the first page of active markets, sorted by newest.
import { HttpClient, MarketFetcher } from '@limitless-exchange/sdk';
const httpClient = new HttpClient({
baseURL: 'https://api.limitless.exchange',
apiKey: process.env.LIMITLESS_API_KEY,
});
async function listMarkets() {
const marketFetcher = new MarketFetcher(httpClient);
const markets = await marketFetcher.getActiveMarkets({
limit: 25,
page: 1,
sortBy: 'newest', // 'newest' | 'trending' | 'ending-soon'
tradeType: 'clob', // 'clob' | 'amm'
});
console.log(`Fetched ${markets.length} active markets`);
for (const m of markets) {
console.log(`${m.slug.padEnd(40)} vol=${m.volume} liq=${m.liquidity}`);
}
}
listMarkets().catch(console.error);
# Module 04, Listing Markets
# Fetch the first page of active markets, sorted by newest.
import asyncio
from limitless_sdk import HttpClient
from limitless_sdk.markets import MarketFetcher
http_client = HttpClient() # auto-loads LIMITLESS_API_KEY
async def list_markets() -> None:
market_fetcher = MarketFetcher(http_client)
response = await market_fetcher.get_active_markets(
limit=25,
page=1,
sort_by="newest", # "newest" | "trending" | "ending-soon"
trade_type="clob",
)
markets = response["data"]
print(f"Fetched {len(markets)} of {response['totalMarketsCount']} markets")
for m in markets:
print(f"{m['slug']:<40} vol={m['volume']} liq={m['liquidity']}")
await http_client.close()
if __name__ == "__main__":
asyncio.run(list_markets())
// Module 04, Listing Markets
// Fetch the first page of active markets, sorted by newest.
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()
marketFetcher := limitless.NewMarketFetcher(client)
result, err := marketFetcher.GetActiveMarkets(ctx, &limitless.ActiveMarketsParams{
Limit: 25,
Page: 1,
SortBy: "newest",
TradeType: "clob",
})
if err != nil {
log.Fatal(err)
}
fmt.Printf("Fetched %d of %d markets\n", len(result.Data), result.TotalMarketsCount)
for _, m := range result.Data {
fmt.Printf("%-40s vol=%v liq=%v\n", m.Slug, m.Volume, m.Liquidity)
}
}
Section 02
Market metadata.
Every market object you get back carries the same shape. Understand these fields once and you can write code against any endpoint without guessing.
Identity
- id · numeric primary key
- slug · human URL (use this in most calls)
- address · on-chain contract address
- conditionId · CTF condition hash
- title · the human-readable question
State & economics
- status · active · closed · resolved
- expired · boolean past-deadline flag
- tradeType · clob · amm
- marketType · binary · categorical · group
- volume · lifetime traded USDC
- openInterest · outstanding notional
- liquidity · quoted depth at touch
Prefer slug over id in most calls.
Slugs are stable and human-readable; logs and bug reports stay sane. The id field is fine for local caches or when you need a compact key.
Section 03
Filtering & pagination.
Don’t fetch the whole catalogue just to find one subset. Push filters to the server and walk pages with page + limit until the last page comes back short, or until you’ve got enough.
// Module 04, Filtering & Pagination
// Page through every active CLOB market in a category.
import { HttpClient, MarketFetcher } from '@limitless-exchange/sdk';
const httpClient = new HttpClient({ apiKey: process.env.LIMITLESS_API_KEY });
const marketFetcher = new MarketFetcher(httpClient);
async function* iterateMarkets(categoryId: number) {
let page = 1;
const limit = 50;
while (true) {
const markets = await marketFetcher.getActiveMarkets({
page,
limit,
categoryId,
tradeType: 'clob',
sortBy: 'trending',
});
if (markets.length === 0) return;
for (const m of markets) yield m;
if (markets.length < limit) return; // last page
page += 1;
}
}
async function main() {
let seen = 0;
for await (const m of iterateMarkets(7 /* crypto */)) {
seen += 1;
if (m.liquidity > 10_000) console.log(m.slug, m.liquidity);
}
console.log(`Scanned ${seen} markets`);
}
main().catch(console.error);
# Module 04, Filtering & Pagination
# Page through every active CLOB market in a category.
import asyncio
from limitless_sdk import HttpClient
from limitless_sdk.markets import MarketFetcher
http_client = HttpClient()
market_fetcher = MarketFetcher(http_client)
async def iterate_markets(category_id: int):
page, limit = 1, 50
while True:
response = await market_fetcher.get_active_markets(
page=page,
limit=limit,
category_id=category_id,
trade_type="clob",
sort_by="trending",
)
markets = response["data"]
if not markets:
return
for m in markets:
yield m
if len(markets) < limit:
return
page += 1
async def main() -> None:
seen = 0
async for m in iterate_markets(category_id=7):
seen += 1
if m["liquidity"] > 10_000:
print(m["slug"], m["liquidity"])
print(f"Scanned {seen} markets")
await http_client.close()
if __name__ == "__main__":
asyncio.run(main())
// Module 04, Filtering & Pagination
// Page through every active CLOB market in a category.
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()
marketFetcher := limitless.NewMarketFetcher(client)
seen := 0
page := 1
limit := 50
for {
result, err := marketFetcher.GetActiveMarkets(ctx, &limitless.ActiveMarketsParams{
Page: page,
Limit: limit,
CategoryID: 7, // crypto
TradeType: "clob",
SortBy: "trending",
})
if err != nil {
log.Fatal(err)
}
for _, m := range result.Data {
seen++
if m.Liquidity > 10000 {
fmt.Println(m.Slug, m.Liquidity)
}
}
if len(result.Data) < limit {
break
}
page++
}
fmt.Printf("Scanned %d markets\n", seen)
}
How to run this
- Keep LIMITLESS_API_KEY in your environment. Pick a real categoryId, 7 is the illustrative crypto value; check GET /markets/categories/count for live ids.
- Save the snippet above as iterate-markets.ts, then run npx tsx iterate-markets.ts.
- Save the snippet above as iterate_markets.py, then run python iterate_markets.py.
- Save the snippet above as main.go inside a Go module, then run go run main.go.
- Any market with liquidity above $10k prints its slug and depth, followed by a final Scanned N markets count.
Don’t scan everything when you want one subset.
Pushing categoryId, tradeType, or automationType into the query turns a 2,000-row scan into a 50-row page. Module 09 covers the rate-limit math; push filters now and you won’t hit them later.
Section 04
Market detail.
Once you have a slug, GET /markets/{slug} returns the full object: resolution source, outcome tokens, and everything you need to build a typed model. Map it into your domain so the rest of your bot doesn’t touch raw JSON.
// Module 04, Market Detail
// Fetch a single market and map it into a typed model.
import { HttpClient, MarketFetcher } from '@limitless-exchange/sdk';
const httpClient = new HttpClient({ apiKey: process.env.LIMITLESS_API_KEY });
const marketFetcher = new MarketFetcher(httpClient);
interface Market {
slug: string;
title: string;
tradeType: 'clob' | 'amm';
isLive: boolean;
volume: number;
liquidity: number;
}
function toMarket(raw: any): Market {
return {
slug: raw.slug,
title: raw.title,
tradeType: raw.tradeType,
isLive: raw.status === 'active' && !raw.expired,
volume: Number(raw.volume ?? 0),
liquidity: Number(raw.liquidity ?? 0),
};
}
async function main() {
const raw = await marketFetcher.getMarket('will-btc-close-above-100k-eoy');
const market = toMarket(raw);
console.log(market);
}
main().catch(console.error);
# Module 04, Market Detail
# Fetch a single market and map it into a dataclass.
import asyncio
from dataclasses import dataclass
from limitless_sdk import HttpClient
from limitless_sdk.markets import MarketFetcher
http_client = HttpClient()
market_fetcher = MarketFetcher(http_client)
@dataclass
class Market:
slug: str
title: str
trade_type: str
is_live: bool
volume: float
liquidity: float
def to_market(raw: dict) -> Market:
return Market(
slug=raw["slug"],
title=raw["title"],
trade_type=raw["tradeType"],
is_live=raw["status"] == "active" and not raw["expired"],
volume=float(raw.get("volume", 0)),
liquidity=float(raw.get("liquidity", 0)),
)
async def main() -> None:
raw = await market_fetcher.get_market("will-btc-close-above-100k-eoy")
print(to_market(raw))
await http_client.close()
if __name__ == "__main__":
asyncio.run(main())
// Module 04, Market Detail
// Fetch a single market and map it into a typed struct.
package main
import (
"context"
"fmt"
"log"
limitless "github.com/limitless-labs-group/limitless-exchange-go-sdk/limitless"
)
type Market struct {
Slug string
Title string
TradeType string
IsLive bool
Volume float64
Liquidity float64
}
func toMarket(raw *limitless.Market) Market {
return Market{
Slug: raw.Slug,
Title: raw.Title,
TradeType: raw.TradeType,
IsLive: raw.Status == "active" && !raw.Expired,
Volume: raw.Volume,
Liquidity: raw.Liquidity,
}
}
func main() {
ctx := context.Background()
client := limitless.NewHttpClient()
marketFetcher := limitless.NewMarketFetcher(client)
raw, err := marketFetcher.GetMarket(ctx, "will-btc-close-above-100k-eoy")
if err != nil {
log.Fatal(err)
}
fmt.Printf("%+v\n", toMarket(raw))
}
How to run this
- Keep LIMITLESS_API_KEY set. Swap will-btc-close-above-100k-eoy for a real slug you grabbed in Section 01.
- Save the snippet above as market-detail.ts, then run npx tsx market-detail.ts.
- Save the snippet above as market_detail.py, then run python market_detail.py.
- Save the snippet above as main.go inside a Go module, then run go run main.go.
- A typed Market object prints with slug, title, tradeType, isLive, volume, and liquidity, all strongly typed, ready to pass into the rest of your bot.
Limitless markets endpoints: 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 fields are on a Limitless market object?
Identity:id(numeric primary key),slug(human URL, use this in most calls),address(on-chain contract),conditionId(CTF condition hash), andtitle(the human-readable question). State and economics:status(active, closed, resolved),expired,tradeType(clob or amm),marketType(binary, categorical, group),volume(lifetime traded USDC),openInterest, andliquidity(quoted depth at touch). Learn the shape once and every endpoint stops being guesswork. -
Does status “active” mean a Limitless market is tradeable?
No. A market can be listed-but-inactive: pre-launch, post-resolution, or paused for compliance. Filter onstatusalone and your bot still quotes on rows wheretradingEnabled = false; the order is rejected, the loop logs an error, and the bot keeps trying. Filter on the explicittradingEnabledfield, cache the markets-list response, and refresh on a 60s cadence, markets transition mid-session more often than you’d expect. -
How does pagination work on /markets/active?
Walkpage+limituntil the last page comes back short (fewer rows thanlimit), or until you’ve got enough. PushcategoryId,tradeType, orautomationTypeinto the query so the server does the filtering; that’s what turns a 2,000-row scan into a 50-row page. CheckGET /markets/categories/countfor live category ids instead of hard-coding one. -
Should your bot use the market slug or the numeric id?
Preferslugin most calls. Slugs are stable and human-readable, so logs and bug reports stay sane, andGET /markets/{slug}takes the slug directly. The numericidis fine for local caches or when you need a compact key, but it tells you nothing when it shows up in a stack trace. -
How do you turn a raw market response into a typed model?
Fetch the full object withgetMarket(slug)and map it once into your own type, so the rest of the bot never touches raw JSON. The module’s example keepsslug,title,tradeType,volume,liquidity, and anisLiveflag that collapses status-is-active and not-expiredinto one check. One mapping function per endpoint keeps schema drift in one place.
Section 05
Module checklist.
Tick each item once you’ve actually done it. The Continue button unlocks at 5/5.
I fetched active markets with MarketFetcher.getActiveMarkets() and saw real slugs in the output
I can name the fields on a market object: slug · status · tradeType · volume · liquidity
I paginate with server-side filters (categoryId, tradeType) instead of scanning the whole catalogue
I fetched a single market via getMarket(slug) and mapped the response into a typed model
I understand why slug is the preferred identifier over the numeric id
Module 04 complete
Markets mapped.
Your bot is no longer flying blind. It can ask the exchange what’s open, what’s liquid, and what category each market falls in, the cheapest possible step to stop it from chasing markets it has no edge in.
Concretely, you can find any market on Limitless and turn its JSON into something your bot can reason about. Three things you walk away with:
A typed Market model you can import into any future bot, slug, tradeType, volume, liquidity, and an isLive flag that collapses status + expired into one check.
A paginated scanner that pushes categoryId, tradeType, and sortBy to the server instead of downloading the full catalogue every time.
A concrete answer to “which markets should my bot touch?”, filter by depth, category, or trade type and hand the result to your strategy.
Next up: teaching your bot to actually trade. Module 05 is limit orders that rest, FOK orders that cross, and the cancel paths your bot needs when the book moves against it.
Complete the checklist above to unlock