Serving Feeds in Production

Once you've built and tested your algorithms in the Console, this guide shows you how to serve your feed config to users — with pagination, new item detection, and cache management.


How It Works

When you call the Serve endpoint, the platform:

  1. Runs each algorithm in your config
  2. Interleaves results according to the weights you set
  3. Caches the result
  4. Returns the first page of items plus a served_id for fetching more pages
Console                          Your App
┌──────────────┐
│ Algo A (0.6) │
│ Algo B (0.4) │──── config_id ───▶  POST /deploy/serve
│ Fallbacks    │                           │
│ Cache TTL    │                     ┌─────┴──────┐
└──────────────┘                     │  served_id  │
                                     │  num_pages  │
                                     │  feed[]     │
                                     └─────┬──────┘
                                           │
                              ┌────────────┼────────────┐
                              ▼            ▼            ▼
                        GET /page    GET /new_items   POST /cache/clear
                     (pagination)   (pull to refresh)  (after algo edit)

Step 1: Serve Your Feed

Call the Serve endpoint with your config_id and user context for personalization.

curl -X POST https://api.mbd.xyz/v3/studio/deploy/serve \
  -H "Authorization: Bearer mbd-YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "config_id": 104,
    "inputs": {
      "farcasterFid": "16085",
      "polymarketWallet": "0x1234..."
    }
  }'

JavaScript:

const response = await fetch('https://api.mbd.xyz/v3/studio/deploy/serve', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer mbd-YOUR_KEY',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    config_id: 104,
    inputs: {
      farcasterFid: '16085',
      polymarketWallet: '0x1234...'
    }
  })
})

const { result } = await response.json()

Python:

import requests

response = requests.post(
    'https://api.mbd.xyz/v3/studio/deploy/serve',
    headers={
        'Authorization': 'Bearer mbd-YOUR_KEY',
        'Content-Type': 'application/json'
    },
    json={
        'config_id': 104,
        'inputs': {
            'farcasterFid': '16085',
            'polymarketWallet': '0x1234...'
        }
    }
)

result = response.json()['result']
  • config_id — your saved feed config ID from the Console
  • inputs — user context for personalization. Pass {} for a non-personalized feed

Response

{
  "result": {
    "served_id": "707a68e8-2483-4b11-9180-5add8a8ec6e2",
    "served_hash": "02a3ee745b36fea8",
    "num_pages": 8,
    "feed": [
      {
        "_id": "661295",
        "_index": "polymarket-items-v20251126",
        "_source": {
          "question": "Will Bournemouth win the Carabao Cup?",
          "outcome_prices": [0.5, 0.5],
          "volume_1wk": 15000
        }
      }
    ],
    "feeds_ages": {
      "42": 0,
      "43": 3600
    },
    "logs": [],
    "took_algo_building": 120,
    "took_backend": 180
  }
}

Save these values from the response:

FieldWhat it's for
served_idPass to the Page and New Items endpoints
num_pagesTotal pages available — controls your pagination UI
feedFirst page of items (default 25 per page)
feeds_agesCache age in seconds per algorithm. 0 = fresh build

Step 2: Paginate Through Results

Use the served_id from Step 1 to fetch additional pages. Pages are 1-based — page 1 was already returned in the feed array.

curl "https://api.mbd.xyz/v3/studio/deploy/serve/page?served_feed_id=707a68e8-2483-...&page_num=2" \
  -H "Authorization: Bearer mbd-YOUR_KEY"

JavaScript:

const params = new URLSearchParams({
  served_feed_id: result.served_id,
  page_num: String(2)
})

const pageResponse = await fetch(
  `https://api.mbd.xyz/v3/studio/deploy/serve/page?${params}`,
  { headers: { 'Authorization': 'Bearer mbd-YOUR_KEY' } }
)

const { result: pageResult } = await pageResponse.json()

Response

{
  "result": {
    "items": [
      { "_id": "661300", "_source": { "question": "Will Russia capture Shakhove?" } }
    ],
    "new_items": 3
  }
}
  • items — feed items for the requested page
  • new_items — optional count of items published since your initial serve

Infinite Scroll Pattern

let currentPage = 1
const allItems = [...result.feed]

async function loadMore() {
  if (currentPage >= result.num_pages) return
  currentPage++

  const params = new URLSearchParams({
    served_feed_id: result.served_id,
    page_num: String(currentPage)
  })

  const res = await fetch(
    `https://api.mbd.xyz/v3/studio/deploy/serve/page?${params}`,
    { headers: { 'Authorization': 'Bearer mbd-YOUR_KEY' } }
  )
  const { result: pageResult } = await res.json()
  allItems.push(...pageResult.items)
}

Step 3: Check for New Items

After the initial serve, new content may be published. Use this endpoint to detect new items without re-fetching the entire feed — useful for "X new items available" banners or pull-to-refresh.

curl "https://api.mbd.xyz/v3/studio/deploy/serve/new_items?served_feed_id=707a68e8-2483-..." \
  -H "Authorization: Bearer mbd-YOUR_KEY"

Response

If new items exist:

{
  "result": {
    "items": [
      { "_id": "new_item_1", "_source": { "question": "Will BTC reach $150k by July?" } }
    ]
  }
}

If no new items exist, the API returns HTTP 404 with {"error": "New items not found"}. Handle this as "no new items" in your code:

const res = await fetch(...)
if (res.status === 404) {
  // No new items — this is normal, not an error
  return []
}
const { result } = await res.json()
return result.items

Polling Pattern

function pollForNewItems(servedId, onNewItems, intervalMs = 60000) {
  const poll = async () => {
    const params = new URLSearchParams({ served_feed_id: servedId })
    const res = await fetch(
      `https://api.mbd.xyz/v3/studio/deploy/serve/new_items?${params}`,
      { headers: { 'Authorization': 'Bearer mbd-YOUR_KEY' } }
    )
    if (res.status === 404) return // no new items
    const { result } = await res.json()
    if (result.items && result.items.length > 0) {
      onNewItems(result.items)
    }
  }

  const interval = setInterval(poll, intervalMs)
  return () => clearInterval(interval)
}

Step 4: Preview Config Changes

Test different algorithm weights or settings without modifying your saved config:

const preview = await fetch('https://api.mbd.xyz/v3/studio/deploy/preview', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer mbd-YOUR_KEY',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    config: {
      main_algos: [
        { algo_id: 42, weight: 0.7 },
        { algo_id: 43, weight: 0.3 }
      ],
      page_size: 25,
      building_timeout_ms: 300
    },
    inputs: { farcasterFid: '16085' }
  })
})

Response is identical to /serve — you get a served_id and can paginate the preview.


Step 5: Clear Cache After Algorithm Updates

When you update an algorithm in the Console, cached feeds continue serving the previous version until the cache TTL expires. Force a refresh:

const clearResponse = await fetch('https://api.mbd.xyz/v3/studio/deploy/cache/clear', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer mbd-YOUR_KEY',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    feedIds: [42, 43],
    inputs: { farcasterFid: '16085' }
  })
})
  • feedIds — these are algorithm IDs, not config IDs
  • inputs — optional. If provided, only clears the cache for that specific user context

Response

{
  "result": {
    "42": { "denied": false, "found": 1 },
    "43": { "denied": false, "found": 0 }
  }
}
  • found: 1 — cache was cleared
  • found: 0 — no cached version to clear

Handling Cold Starts

If your algorithms take longer than building_timeout_ms (default 150ms) to build, the API returns an empty feed:

{ "result": { "feed": [], "num_pages": 0, "served_id": "707a68e8-..." } }

How to handle it:

  1. Increase the timeout — set building_timeout_ms to 300–500ms in your config
  2. Retry with the served_id — the build continues in the background. Wait 1–2 seconds and fetch page 1:
const result = await serve(104, inputs)

if (result.feed.length === 0 && result.served_id) {
  await new Promise(resolve => setTimeout(resolve, 2000))

  const params = new URLSearchParams({
    served_feed_id: result.served_id,
    page_num: '1'
  })
  const retryResponse = await fetch(
    `https://api.mbd.xyz/v3/studio/deploy/serve/page?${params}`,
    { headers: { 'Authorization': 'Bearer mbd-YOUR_KEY' } }
  )
  const { result: retryResult } = await retryResponse.json()
}
  1. Show a loading state — display a skeleton UI rather than "no results found"

Debugging Multi-Algorithm Configs

When your config has two or more algorithms, the serve response doesn't indicate which algorithm produced which item. To debug:

  1. Check feeds_ages — shows cache age per algorithm ID. 0 = fresh, any other value = cached.
  2. Preview each algo separately — use /deploy/preview with one algorithm at a time:
// Test algo 42 alone
const algoA = await preview({
  main_algos: [{ algo_id: 42, weight: 1.0 }]
}, inputs)

// Test algo 43 alone
const algoB = await preview({
  main_algos: [{ algo_id: 43, weight: 1.0 }]
}, inputs)
  1. Check the logs — the logs array in the response may contain debugging output from your algorithm code.

Complete Example

const API_BASE = 'https://api.mbd.xyz/v3/studio/deploy'
const API_KEY = 'mbd-YOUR_KEY'
const headers = {
  'Authorization': `Bearer ${API_KEY}`,
  'Content-Type': 'application/json'
}

// 1. Serve the feed
const serveResponse = await fetch(`${API_BASE}/serve`, {
  method: 'POST',
  headers,
  body: JSON.stringify({
    config_id: 104,
    inputs: { farcasterFid: '16085' }
  })
})
const { result } = await serveResponse.json()
console.log(`Page 1: ${result.feed.length} items, ${result.num_pages} total pages`)

// 2. Fetch remaining pages
for (let page = 2; page <= result.num_pages; page++) {
  const params = new URLSearchParams({
    served_feed_id: result.served_id,
    page_num: String(page)
  })
  const pageResponse = await fetch(`${API_BASE}/serve/page?${params}`, { headers })
  const { result: pageResult } = await pageResponse.json()
  console.log(`Page ${page}: ${pageResult.items.length} items`)
}

// 3. Poll for new items every 60 seconds
setInterval(async () => {
  const params = new URLSearchParams({ served_feed_id: result.served_id })
  const res = await fetch(`${API_BASE}/serve/new_items?${params}`, { headers })
  const { result: newItems } = await res.json()
  if (newItems.items?.length) {
    console.log(`${newItems.items.length} new items since last serve`)
  }
}, 60000)

Python:

import requests
import time

API_BASE = 'https://api.mbd.xyz/v3/studio/deploy'
headers = {
    'Authorization': 'Bearer mbd-YOUR_KEY',
    'Content-Type': 'application/json'
}

# 1. Serve the feed
serve_response = requests.post(f'{API_BASE}/serve', headers=headers, json={
    'config_id': 104,
    'inputs': {'farcasterFid': '16085'}
})
result = serve_response.json()['result']
print(f"Page 1: {len(result['feed'])} items, {result['num_pages']} total pages")

# 2. Fetch remaining pages
for page in range(2, result['num_pages'] + 1):
    page_response = requests.get(f'{API_BASE}/serve/page', headers=headers, params={
        'served_feed_id': result['served_id'],
        'page_num': page
    })
    items = page_response.json()['result']['items']
    print(f"Page {page}: {len(items)} items")

# 3. Check for new items
new_items_response = requests.get(f'{API_BASE}/serve/new_items', headers=headers, params={
    'served_feed_id': result['served_id']
})
new_items = new_items_response.json()['result']['items']
print(f"{len(new_items)} new items since last serve")

What's Next