Quickstart: Algorithms DSL

This example shows how to use the Algorithms DSL (algo-dsl) to build a Polymarket prediction market feed. It demonstrates the full pipeline: search → features → scoring → ranking.

The guide starts with a basic unpersonalized feed (no wallet required), then shows how to add personalization with a Polymarket wallet.


Install

npm install algo-dsl dotenv

This installs:

  • algo-dsl — Embed Studio backend client for search, features, scoring, and ranking
  • dotenv — Loads environment variables from .env

Configure

Create a .env file with your API key:

EMBED_API_KEY=your-mbd-api-key
VariableDescription
EMBED_API_KEYYour Embed API key from the Console (starts with mbd-)

That's all you need to get started. A Polymarket wallet address is only needed if you want personalization (see Adding Personalization below).


Basic Feed (No Wallet Required)

This builds a ranked feed of active Polymarket markets using search, features, and ranking — no wallet needed.

1. Initialize the SDK

import 'dotenv/config';
import { StudioConfig, StudioV1 } from 'algo-dsl';

const config = new StudioConfig({ apiKey: process.env.EMBED_API_KEY });
const mbd = new StudioV1({ config });

2. Search (candidate retrieval)

const candidates = await mbd.search()
  .index("polymarket-items")
  .size(100)
  .include()
    .numeric("volume_1wk", ">=", 10000)
  .exclude()
    .term("closed", true)
    .term("price_under05_or_over95", true)
  .execute()

mbd.addCandidates(candidates);
  • Index: polymarket-items — prediction markets
  • Include filter: weekly volume ≥ $10k
  • Exclude filters: closed markets and extreme prices (< 5% or > 95%)

3. Features (enrichment)

const features = await mbd.features("v1").execute()
mbd.addFeatures(features);

Computes ML signals like topic_score for each candidate. Without a user context set, user-specific features (like user_affinity_score) will be zero — that's fine for an unpersonalized feed.

4. Ranking (final ordering)

const ranking = await mbd.ranking()
  .sortingMethod('sort')
  .sortBy('topic_score', 'desc')
  .execute()

mbd.addRanking(ranking);

Sorts candidates by topic relevance. For an unpersonalized feed, a simple sort works well.

5. Output

const feed = mbd.getFeed();
for (const item of feed.slice(0, 10)) {
  console.log(item._id, item._source.question, item._ranking_score);
}

Run it:

node polymarket.js

You should see the top 10 Polymarket markets ranked by topic relevance.


Adding Personalization

To personalize results for a specific user, add a wallet address and three extra steps: forUser, boost search, and scoring.

Add the wallet to your .env:

EMBED_API_KEY=your-mbd-api-key
POLYMARKET_WALLET=0xYourPolymarketWalletAddress

1. Set user context

const polymarketWallet = process.env.POLYMARKET_WALLET?.toLowerCase().trim();

const config = new StudioConfig({ apiKey: process.env.EMBED_API_KEY });
const mbd = new StudioV1({ config });

// Tell the SDK which user to personalize for
mbd.forUser("polymarket-wallets", polymarketWallet);

Important: forUser takes two arguments:

  1. User profile index — the index where the user's profile lives (polymarket-wallets for Polymarket, farcaster-items for Farcaster). This is NOT the items index.
  2. User ID — the user's identifier within that index (e.g. a wallet address for Polymarket, a FID for Farcaster).

If you use the wrong index (e.g. polymarket-items instead of polymarket-wallets), features like user_affinity_score will return empty or zero.

2. Search with personalized boosts

const candidates = await mbd.search()
  .index("polymarket-items")
  .includeVectors(true)
  .include()
    .numeric("volume_1wk", ">=", 10000)
  .exclude()
    .term("closed", true)
    .term("price_under05_or_over95", true)
  .boost()
    .groupBoost("polymarket-wallets", "ai_labels_med", polymarketWallet, "label", 1, 5, 10)
    .groupBoost("polymarket-wallets", "tags", polymarketWallet, "tag", 1, 5, 10)
  .execute()

mbd.addCandidates(candidates);

The .boost() section is what makes this personalized — it looks up the user's label and tag preferences from their wallet profile and boosts matching markets.

  • includeVectors(true) — needed for semantic diversity in ranking
  • groupBoost — dynamically boosts items matching the user's top labels and tags

3. Features (enrichment)

const features = await mbd.features("v1").execute()
mbd.addFeatures(features);

With forUser set, features now include personalized signals like user_affinity_score alongside topic_score.

4. Scoring (reranking model)

const scores = await mbd.scoring()
  .model("/scoring/ranking_model/polymarket-rerank-v1")
  .execute()
mbd.addScores(scores, "ranking_model_polymarket_rerank_v1");

The Polymarket rerank model understands user-market relationships and produces a holistic relevance score.

5. Ranking (final ordering)

const ranking = await mbd.ranking()
  .sortingMethod('mix')
  .mix("topic_score", 'desc', 40)
  .mix("user_affinity_score", 'desc', 40)
  .mix("rerank_polymkt1", 'desc', 20)
  .diversity('semantic')
  .lambda(0.5)
  .horizon(20)
  .limitByField()
  .every(10)
  .limit("cluster_1", 1)
  .execute()
mbd.addRanking(ranking);
  • Mix weights: topic (40%), user affinity (40%), rerank model (20%)
  • Semantic diversity: spreads similar items apart (lambda 0.5, horizon 20)
  • Field limits: max 1 item per cluster_1 value per 10 results

6. Output

const feed = mbd.getFeed();
for (const item of feed.slice(0, 10)) {
  console.log(item._id, item._source.question, item._ranking_score);
}

Pipeline Overview

                    ┌─────────────────────────────────────┐
                    │         Basic (no wallet)            │
                    │                                     │
API key ──→ [Search] ──→ [Features] ──→ [Ranking: sort] ──→ Feed
                    │                                     │
                    └─────────────────────────────────────┘

                    ┌─────────────────────────────────────────────────┐
                    │         Personalized (with wallet)               │
                    │                                                 │
API key ──→ forUser ──→ [Search + boost] ──→ [Features] ──→ [Scoring] ──→ [Ranking: mix + diversity] ──→ Feed
  +                 │                                                 │
wallet              └─────────────────────────────────────────────────┘

What's Next

This quickstart builds a feed pipeline in code. To deploy it for production serving:

  1. Save as an algorithm — use the Console or POST /deploy/algos to save your pipeline code
  2. Create a feed config — bundle your algorithm with cache settings, weights, and fallbacks. See Feed Configs
  3. Serve in production — call POST /deploy/serve with your config_id. See Serving

Or explore the pipeline stages in depth:

  • [Search](../Building Algorithms/search) — filters, boost, semantic search, all 4 indices
  • [Features](../Building Algorithms/algo-features) — ML enrichment and personalization signals
  • [Scoring & Ranking](../Building Algorithms/algo-scoring-ranking) — reranking models, sort methods, diversity
  • [Vibecode a Polymarket Feed](../Getting Started/vibecoding-with-embed) — full Next.js app with charts and filtering