Prometheus

Football World Cup 2026

v1Published

Live Polymarket odds for the 2026 FIFA World Cup Winner market: implied win probability, prices, volume, and liquidity for all 60 team markets.

Author's sample data
event
teams
Publisher
1 subscriber
deam@deam
Every day at 2:00 AM1 runs in 14d · published 2h ago
Versions
managed by author
v1builtapprovedcurrent2h ago
Schedulesdeploy to enable

Run this collector on a cadence — daily, hourly, your call.

API endpointdeploy to unlock

POST to run it on demand and get fresh data in the response.

Activitydeploy to track

1 subscriber runs in the last 14 days.

How this script collects data
import Firecrawl from "@mendable/firecrawl-js";
import * as cheerio from "cheerio";

const apiKey = process.env.FIRECRAWL_API_KEY;
if (!apiKey) {
  console.error("FIRECRAWL_API_KEY is not set");
  process.exit(1);
}
const firecrawl = new Firecrawl({ apiKey });

const EVENT_SLUG = "world-cup-winner";
const API_URL = `https://gamma-api.polymarket.com/events?slug=${EVENT_SLUG}`;

function extractJson(rawHtml: string): any {
  // The gamma API returns plain JSON; depending on rendering it may arrive
  // bare or wrapped in HTML (e.g. inside <pre>). Try bare first, then the
  // text content of the page, then the outermost [...] span.
  const candidates: string[] = [rawHtml];
  try {
    const $ = cheerio.load(rawHtml);
    const pre = $("pre").text();
    if (pre) candidates.push(pre);
    candidates.push($("body").text() || $.root().text());
  } catch {
    // not HTML — bare candidate already covers it
  }
  for (const c of candidates) {
    const trimmed = c.trim();
    const start = trimmed.indexOf("[");
    const end = trimmed.lastIndexOf("]");
    if (start === -1 || end <= start) continue;
    try {
      return JSON.parse(trimmed.slice(start, end + 1));
    } catch {
      continue;
    }
  }
  throw new Error("could not parse JSON from the Polymarket gamma API response");
}

function num(v: unknown): number | null {
  if (v === null || v === undefined || v === "") return null;
  const n = Number(v);
  return Number.isFinite(n) ? n : null;
}

async function main() {
  console.error(`Fetching Polymarket event: ${API_URL}`);
  const doc = await firecrawl.scrape(API_URL, { formats: ["rawHtml"] });
  const rawHtml = (doc as any).rawHtml ?? "";
  if (!rawHtml) throw new Error("empty response from the Polymarket gamma API");

  const events = extractJson(rawHtml);
  if (!Array.isArray(events) || events.length === 0) {
    throw new Error(`no Polymarket event found for slug "${EVENT_SLUG}"`);
  }
  const event = events[0];
  const markets = event.markets;
  if (!Array.isArray(markets) || markets.length === 0) {
    throw new Error("event has no markets array in the gamma API response");
  }

  const teams = markets
    .map((m: any) => {
      let yesPrice: number | null = null;
      try {
        const prices = JSON.parse(m.outcomePrices ?? "[]");
        yesPrice = num(prices[0]);
      } catch {
        yesPrice = null;
      }
      return {
        team: (m.groupItemTitle || m.question || "").trim(),
        impliedProbabilityPct:
          yesPrice === null ? null : Math.round(yesPrice * 10000) / 100,
        yesPrice,
        bestBid: num(m.bestBid),
        bestAsk: num(m.bestAsk),
        lastTradePrice: num(m.lastTradePrice),
        volumeUsd: num(m.volume),
        liquidityUsd: num(m.liquidity),
        eliminated: m.closed === true,
        marketSlug: m.slug ?? null,
      };
    })
    .filter((t: any) => t.team.length > 0)
    .sort(
      (a: any, b: any) =>
        (b.yesPrice ?? -1) - (a.yesPrice ?? -1) ||
        (b.volumeUsd ?? 0) - (a.volumeUsd ?? 0)
    );

  if (teams.length === 0) {
    throw new Error("no team markets could be parsed from the event");
  }
  console.error(`Parsed ${teams.length} team markets`);

  const out = {
    event: {
      title: (event.title ?? "").trim(),
      slug: event.slug ?? EVENT_SLUG,
      url: `https://polymarket.com/event/${event.slug ?? EVENT_SLUG}`,
      endDate: event.endDate ?? null,
      totalVolumeUsd: num(event.volume),
      totalLiquidityUsd: num(event.liquidity),
      teamCount: teams.length,
    },
    teams,
  };
  process.stdout.write(JSON.stringify(out));
}

main().catch((err) => {
  console.error(err);
  process.exit(1);
});
Build prompt
Football World Cup 2026
One person builds it. Everyone keeps it fresh.