Skip to main content

Overview

The MarketPageFetcher class provides access to the Market Navigation API — a hierarchical system for browsing markets by category, applying dynamic filters, and paginating results. All endpoints are public and require no authentication.

Setup

import { HttpClient, MarketPageFetcher } from '@limitless-exchange/sdk';

const httpClient = new HttpClient({
  baseURL: 'https://api.limitless.exchange',
});

const pageFetcher = new MarketPageFetcher(httpClient);
Fetch the full navigation hierarchy. Each node represents a browseable category with a URL path.
const navigation = await pageFetcher.getNavigation();

for (const node of navigation) {
  console.log(`${node.name}${node.path}`);
  for (const child of node.children) {
    console.log(`  ${child.name}${child.path}`);
  }
}
FieldTypeDescription
idstringUnique identifier
namestringDisplay name
slugstringURL-friendly identifier
pathstringFull URL path (e.g. /crypto)
iconstring | undefinedOptional icon name
childrenNavigationNode[]Nested child nodes

Resolving a Page by Path

Convert a URL path into a MarketPage with its filters, metadata, and breadcrumb. The SDK handles 301 redirects internally.
const page = await pageFetcher.getMarketPageByPath('/crypto');

console.log(`Page: ${page.name}`);
console.log(`Filters: ${page.filterGroups.length} groups`);
console.log(`Breadcrumb: ${page.breadcrumb.map(b => b.name).join(' > ')}`);

MarketPage Fields

FieldTypeDescription
idstringPage identifier (used for getMarkets())
namestringDisplay name
slugstringURL-friendly identifier
fullPathstringFull URL path
descriptionstring | undefinedPage description
baseFilterRecord<string, any>Default filter applied to this page
filterGroupsFilterGroup[]Available filter groups
metadataRecord<string, unknown>Page metadata
breadcrumbBreadcrumbItem[]Navigation breadcrumb

Fetching Markets for a Page

Use getMarkets() with the page ID to fetch markets. Supports offset and cursor pagination, sorting, and dynamic filters.

Offset Pagination

const result = await pageFetcher.getMarkets(page.id, {
  page: 1,
  limit: 20,
  sort: '-updatedAt',
});

for (const market of result.data) {
  console.log(`${market.slug}${market.title}`);
}

if ('pagination' in result) {
  console.log(`Page ${result.pagination.page} of ${result.pagination.totalPages}`);
}

Cursor Pagination

const firstPage = await pageFetcher.getMarkets(page.id, {
  cursor: '',
  limit: 20,
  sort: '-updatedAt',
});

for (const market of firstPage.data) {
  console.log(market.slug);
}

if ('cursor' in firstPage && firstPage.cursor.nextCursor) {
  const nextPage = await pageFetcher.getMarkets(page.id, {
    cursor: firstPage.cursor.nextCursor,
    limit: 20,
  });
}
You cannot use cursor and page in the same request. Choose one pagination strategy.

Filtering

Pass a filters object. Values can be a single primitive or an array for multi-select filters.
// Single filter
const result = await pageFetcher.getMarkets(page.id, {
  filters: { ticker: 'btc' },
});

// Multiple values (OR logic)
const result = await pageFetcher.getMarkets(page.id, {
  filters: { ticker: ['btc', 'eth'] },
});

// Combined filters
const result = await pageFetcher.getMarkets(page.id, {
  limit: 10,
  sort: '-updatedAt',
  filters: {
    ticker: ['btc', 'eth'],
    duration: 'hourly',
  },
});

Parameters

ParameterTypeDescription
pagenumberPage number (offset pagination)
limitnumberResults per page
sortstringSort field with optional - prefix for descending
cursorstringCursor token (cursor pagination). Use "" for the first request.
filtersRecord<string, MarketPageFilterValue>Filter key-value pairs. Values can be primitives or arrays.

Sort Options

ValueDescription
createdAt / -createdAtSort by creation date (ascending / descending)
updatedAt / -updatedAtSort by last update
deadline / -deadlineSort by expiration deadline
id / -idSort by market ID

Property Keys

Property keys define the available filter dimensions (e.g. “ticker”, “duration”). Each key has a set of options.
const keys = await pageFetcher.getPropertyKeys();

for (const key of keys) {
  console.log(`${key.name} (${key.type})`);
  if (key.options) {
    for (const opt of key.options.slice(0, 3)) {
      console.log(`  ${opt.label}: ${opt.value}`);
    }
  }
}

Single Key and Options

const key = await pageFetcher.getPropertyKey(keys[0].id);
console.log(`${key.name}${key.options?.length ?? 0} options`);

// Fetch options separately (supports parent filtering for hierarchical keys)
const options = await pageFetcher.getPropertyOptions(key.id);

// Child options filtered by parent
const childOptions = await pageFetcher.getPropertyOptions(key.id, options[0]?.id);

PropertyKey Fields

FieldTypeDescription
idstringUnique identifier
namestringDisplay name
slugstringURL-friendly identifier
typestring"select" or "multi-select"
isSystembooleanWhether this is a system-defined key
optionsPropertyOption[] | undefinedAvailable options (when fetched inline)
metadataRecord<string, unknown>Key metadata
createdAtstringISO 8601 creation timestamp
updatedAtstringISO 8601 last update timestamp

Complete Example

import { HttpClient, MarketPageFetcher } from '@limitless-exchange/sdk';

async function main() {
  const httpClient = new HttpClient({
    baseURL: 'https://api.limitless.exchange',
  });
  const pageFetcher = new MarketPageFetcher(httpClient);

  // Browse the navigation tree
  const navigation = await pageFetcher.getNavigation();
  console.log(`Top-level categories: ${navigation.length}`);

  // Resolve a page
  const page = await pageFetcher.getMarketPageByPath('/crypto');
  console.log(`\nPage: ${page.name}`);
  console.log(`Available filters: ${page.filterGroups.map(fg => fg.name)}`);

  // Fetch markets with filters
  const result = await pageFetcher.getMarkets(page.id, {
    limit: 5,
    sort: '-updatedAt',
    filters: { ticker: ['btc', 'eth'] },
  });

  for (const market of result.data) {
    console.log(`  ${market.slug}${market.title}`);
  }

  // Explore property keys
  const keys = await pageFetcher.getPropertyKeys();
  for (const key of keys) {
    console.log(`\n${key.name} (${key.type}):`);
    const options = await pageFetcher.getPropertyOptions(key.id);
    for (const opt of options.slice(0, 5)) {
      console.log(`  ${opt.label}`);
    }
  }
}

main();