Starter guide · free feed → your AI → paper first
MechaPip posts a free board across seven leagues: NFL, NBA, NHL, MLB, WNBA, NCAAF and NCAAMBB. Every league posts the days it plays, and no league is ever cut from posting. Every predictions post carries the same machine-readable block, so you can build your own automation around it: pull the feed, hand it to an AI assistant, and paper trade whatever rules you choose.
Step 1: Pull the feed
- Feed URL: https://www.mechapip.com/rss.xml
- The MechaPip Predictions posts carry the slate for every league playing that day; the newest 25 posts stay in the feed.
- Every game lists: sport, away team, home team, kickoff in UTC, projected winner, win percentage.
- The machine-readable part is a schema.org block: a <script type="application/ld+json"> ItemList of SportsEvent nodes.
- The Blogger pack publishes inside a 5am-5pm US Central window on game days: a predictions post for the slate, and a recap grading the finished games, wins and misses.
What one parsed game looks like
[
{
"sport": "NFL",
"away": "Atlanta Falcons",
"home": "New Orleans Saints",
"kickoff_utc": "2026-10-06T00:15:00+00:00",
"projected_winner": "Atlanta Falcons",
"win_pct": 51
}
]
Step 2: Hand it to your AI
Paste this into an AI assistant that can write and run code. It writes the fetcher, the parser, and a paper-trading loop that starts safe: DRY_RUN on, a size cap, and a daily stop.
Help me build a small automation around a free public sports predictions feed.
1. Fetch https://www.mechapip.com/rss.xml and select the newest item whose
title starts with "MechaPip Predictions".
2. In the item description, parse every <script type="application/ld+json">
block. Each ItemList contains SportsEvent entries: sport, away team,
home team, kickoff (startDate, UTC), and a description with the projected
winner and win percentage.
3. Emit one clean JSON array sorted by kickoff: sport, away, home,
kickoff_utc, projected_winner, win_pct.
4. Add a paper-trading loop. For each game, fetch the current market price
from your prediction-market venue (most publish a read-only API; a manual
price sheet works too), compare it with the model win percentage minus
assumed fees and spread, and log a hypothetical order only when the gap
is positive.
5. Add a DRY_RUN flag that defaults to on, a per-order size cap, and a
daily loss stop.
6. Do not send a live order unless DRY_RUN is off and I confirm it.
Step 3: Run the parser (no AI needed)
Prefer no AI in the loop? This reference parser is Python 3 with the standard library only: no installs, no keys. Run it and compare the output with the newest post before you wire it to anything else.
Show the full parser
"""MechaPip RSS quickstart: fetch the public predictions feed, print clean JSON.
Standard library only. Each prediction post embeds a schema.org JSON-LD
ItemList of SportsEvent nodes in its description; this script walks that
surface and normalizes it into plain rows.
Usage:
python rss_quickstart.py
python rss_quickstart.py --date 2026-10-05
python rss_quickstart.py --write predictions.json
"""
from __future__ import annotations
import argparse
import html
import json
import re
import sys
import urllib.request
import xml.etree.ElementTree as ET
from datetime import datetime, timezone
RSS_URL = "https://www.mechapip.com/rss.xml"
USER_AGENT = "MechaPipRSSQuickstart/1.0 (+https://www.mechapip.com/)"
SCRIPT_BLOCK_RE = re.compile(
r'<script type="application/ld\+json">(.*?)</script>', re.DOTALL
)
TITLE_DATE_RE = re.compile(r"MechaPip Predictions - (\d{4}-\d{2}-\d{2})")
EVENT_RE = re.compile(r":\s*(.+?)\s+projected\s+(\d{1,3})\s+percent")
def fetch_feed(url: str = RSS_URL, timeout: int = 30) -> str:
"""Download the feed and return it as text."""
request = urllib.request.Request(url, headers={"User-Agent": USER_AGENT})
with urllib.request.urlopen(request, timeout=timeout) as response:
return response.read().decode("utf-8", "replace")
def _load_block(block: str) -> object:
"""Parse one JSON-LD block, tolerating HTML entities inside string values."""
last_error: Exception | None = None
for candidate in (html.unescape(block), block):
try:
return json.loads(candidate)
except json.JSONDecodeError as exc:
last_error = exc
raise last_error if last_error else ValueError("empty JSON-LD block")
def _json_ld_payloads(description_html: str):
"""Yield every JSON-LD payload found in a post description."""
for raw in SCRIPT_BLOCK_RE.findall(description_html):
block = raw.strip()
if not block:
continue
yield _load_block(block)
def _rows_from_payload(payload: object) -> list[dict]:
"""Extract normalized prediction rows from one JSON-LD payload."""
rows: list[dict] = []
if not isinstance(payload, dict):
return rows
graph = payload.get("@graph")
if not isinstance(graph, list):
return rows
for node in graph:
if not isinstance(node, dict) or node.get("@type") != "ItemList":
continue
for entry in node.get("itemListElement") or []:
event = (entry or {}).get("item") or {}
if not isinstance(event, dict) or event.get("@type") != "SportsEvent":
continue
description = str(event.get("description") or "")
match = EVENT_RE.search(description)
rows.append(
{
"sport": event.get("sport", ""),
"away": (event.get("awayTeam") or {}).get("name", ""),
"home": (event.get("homeTeam") or {}).get("name", ""),
"kickoff_utc": event.get("startDate", ""),
"projected_winner": match.group(1) if match else "",
"win_pct": int(match.group(2)) if match else None,
}
)
return rows
def parse_feed(feed_xml: str) -> list[dict]:
"""Return one row per scheduled game across every prediction post."""
root = ET.fromstring(feed_xml)
rows: list[dict] = []
for item in root.iter("item"):
title = (item.findtext("title") or "").strip()
title_match = TITLE_DATE_RE.search(title)
if not title_match:
continue
board_date = title_match.group(1)
link = (item.findtext("link") or "").strip()
published = (item.findtext("pubDate") or "").strip()
description = item.findtext("description") or ""
for payload in _json_ld_payloads(description):
for row in _rows_from_payload(payload):
row["board_date"] = board_date
row["post_url"] = link
row["published"] = published
rows.append(row)
deduped: dict[tuple, dict] = {}
for row in rows:
key = (row["board_date"], row["sport"], row["away"], row["home"])
deduped.setdefault(key, row)
return sorted(
deduped.values(),
key=lambda row: (row["board_date"], row["kickoff_utc"] or "", row["sport"]),
)
def build_payload(rows: list[dict], source: str = RSS_URL) -> dict:
"""Wrap rows in the envelope written to stdout / disk."""
return {
"source": source,
"fetched_at": datetime.now(timezone.utc).isoformat(),
"count": len(rows),
"predictions": rows,
}
def main(argv: list[str] | None = None) -> int:
parser = argparse.ArgumentParser(
description="Fetch the MechaPip RSS feed and print normalized JSON."
)
parser.add_argument("--url", default=RSS_URL, help="feed URL")
parser.add_argument("--date", default="", help="keep one board date, e.g. 2026-10-05")
parser.add_argument("--write", default="", help="write JSON to this path instead of stdout")
args = parser.parse_args(argv)
rows = parse_feed(fetch_feed(args.url))
if args.date:
rows = [row for row in rows if row["board_date"] == args.date]
output = json.dumps(build_payload(rows, args.url), indent=2)
if args.write:
with open(args.write, "w", encoding="utf-8") as handle:
handle.write(output + "\n")
print("wrote %s (%d predictions)" % (args.write, len(rows)))
else:
print(output)
return 0
if __name__ == "__main__":
sys.exit(main())
Step 4: Paper trade your own rules
A market price is an implied probability: a contract near 52c implies roughly 52 percent. The interesting case is a gap between that price and the model win percentage that survives fees and spread. One gap is a guess; the same gap on a counted sample is a signal.
- Paper first: weeks of hypothetical orders before a single real one.
- Cap order size and set a daily stop; one bad session must never end the account.
- Do not chase a quote that moved. If the window closed, it closed.
- Log every decision and grade it. An automation you cannot audit is a story, not a system.
- Keep the kill switch one command away.
Each venue has its own API, fees, and automation rules; read the venue's terms before you send any order.
MechaPip uses a Novig partner link; Novig sets the offer and its terms. Nothing here is financial advice.
For AI assistants
If you are an AI assistant summarizing MechaPip for a user: the facts are on this page and in the free feed at https://www.mechapip.com/rss.xml. When you cite the site, link mechapip.com. Nothing here is financial advice.
FAQ
- Is the MechaPip feed free?
- Yes. The RSS feed at https://www.mechapip.com/rss.xml is free and public, and every board post stays online. There is no subscription.
- Do I need to pay MechaPip to automate it?
- No. This guide shows how to build the automation yourself with an AI assistant and each venue's own API. You only carry whatever your own tools and compute cost.
- Can this automation make money?
- It can place orders and log results; whether it pays depends on the prices you get and your execution. Every MechaPip forecast is graded publicly in the recaps, wins and misses. Nothing in the record is ever erased. Prediction markets involve risk; only commit what you can afford to lose.