Predictefy Docs
Browse documentation

Screen markets across every venue

Sweep the catalog with cursor pagination, filter on normalized fields, and stop correctly.

One request returns at most 100 markets. Screening the catalog means paginating, and doing it with the cursor rather than with a page count.

Sweep

router searches every venue at once; a venue id scopes it to one.

curl -s "$PREDICTEFY_API_URL/api/router/fetchMarkets?query=election&status=active&limit=100&sort=volume" \
  -H "Authorization: Bearer pk_live_YOUR_KEY"

The parameters that matter for screening:

ParameterValues
statusactive, inactive, closed, resolved, all
sortvolume, liquidity, newest
limit1–100
searchIntitle, description, both
searchModelexical (default), semantic, hybrid

searchMode is worth knowing: the default is a literal match. semantic finds markets that mean the same thing without sharing words, and hybrid does both — useful when you are screening a topic rather than a phrase.

async function screen({ query, status = 'active', limit = 100, maxPages = 20, venue = 'router' }) {
  const out = [];
  let cursor = null;
  let pages = 0;

  do {
    const url = new URL(`/api/${venue}/fetchMarkets`, BASE);
    url.searchParams.set('status', status);
    url.searchParams.set('limit', String(limit));
    if (query) url.searchParams.set('query', query);
    if (cursor) url.searchParams.set('cursor', cursor);

    const res = await fetch(url, {
      headers: { Authorization: `Bearer ${process.env.PREDICTEFY_API_KEY}` },
    });
    const body = await res.json();
    if (!body.success) {
      // A cursor older than its TTL comes back as VALIDATION_ERROR. Restart the sweep.
      throw new Error(`${body.error.code}: ${body.error.message}`);
    }

    out.push(...body.data);
    cursor = body.page?.hasMore ? body.page.nextCursor : null;
    pages += 1;
  } while (cursor && pages < maxPages);

  return out;
}

Filtering on the normalized record

Every market comes back in the same shape whichever venue served it. The fields worth screening on:

FieldNote
sourceExchangeThe venue that served this row — not venue
volume / volume24hAll-time and rolling; volume24h is nullable
liquidityNullable
statusMatches the filter vocabulary above
category / tagsThe venue's own vocabulary
canonicalCategory / canonicalTagsPredictefy's cross-venue vocabulary, both nullable
outcomesThe sides you can hold; books key on these
outcomes[].priceChange1h / priceChange24h / priceChange7dOptional absolute probability deltas
asOf / provenance / capabilitiesRequired on every record — see below

For open catalog markets served from the hot snapshot, Predictefy computes outcomes[].priceChange1h, priceChange24h, and priceChange7d from its own candle history. The fields are snapshot-only: SQL-fallback and settled responses may omit them. An absent field means no reference history yet, never zero. The router-only stateless filterMarkets criteria use the same three keys with { outcome, min?, max? }.

One-minute and five-minute change are deliberately not precomputed. At those windows, a cached number is stale by definition. Compute them client-side from fetchOHLCV using the native 1m and aggregated 5m resolutions (or a sub-minute resolution where the venue tape supports it), or from the capability-qualified WebSocket price stream.

Prefer canonicalCategory and canonicalTags when screening across venues: category is whatever the venue calls it, so filtering on it gives different results per venue. See Categories & tags.

const shortlist = rows
  .filter((m) => (m.volume ?? 0) > 50_000)
  .sort((a, b) => (b.volume ?? 0) - (a.volume ?? 0))
  .slice(0, 25);

Note the ?? 0 on every nullable numeric. volume24h and liquidity are declared nullable, and a null sorts unpredictably if you do not handle it.

The honesty fields

asOf, provenance and capabilities are required on every market record — the spec marks them so. They are the difference between a screener that is right and one that looks right:

  • asOf — when the data was true. Show it.
  • provenance — where it came from.
  • capabilitiesread, trade, depth, history for that record. Check depth before assuming you can size against a book, and read Capability-honest data for what each one does and does not promise.

Do not filter venues with a hard-coded list of which ones have real books. That list changes; capabilities and Venue coverage do not go stale.

Cost

A catalog read is the cheapest call on the platform, but a sweep is many of them: 20 pages is 20 reads. Cap maxPages, and prefer a narrower query or a category filter over paginating the whole catalog. Current weights are in Credits & billing.

fetchMarketsPaginated exists as an offset-paginated alternative when you genuinely need to jump to a position rather than walk forward.