pnclFEED Free key

Quickstart notes

Automate odds polling on Windows with Task Scheduler

Use Task Scheduler for the Python quickstart on Windows: one fetch per run, a cursor file, a wrapper for variables and a log as the health check.

Windows has no cron, but it does not need one for this job. The Python quickstart polls the odds API in a loop; on a Windows box the same schedule runs on Task Scheduler, with the same rule the cron version uses: one run does one fetch and exits, and a small file carries the cursor between runs. At one run every 15 minutes you get 96 fetches a day, inside the free key's published budget of 100 requests a day and 20 a minute, as the pricing page states, checked 2026-10-21.

One run, one fetch

Take the loop out of the script. Each run reads the cursor, sends it as since, prints what changed, saves the new cursor and exits:

import json, os, sys, requests

BASE = os.environ["ODDS_BASE_URL"]
KEY = os.environ["ODDS_API_KEY"]
CURSOR = r"C:\odds\cursor.txt"

since = None
if os.path.exists(CURSOR):
    since = open(CURSOR).read().strip()

params = {"sport_id": 2}
if since:
    params["since"] = since

r = requests.get(BASE + "/kit/v1/markets", params=params,
                 headers={"x-portal-apikey": KEY}, timeout=10)
r.raise_for_status()
data = r.json()
open(CURSOR, "w").write(str(data["last"]))
print(f"{len(data['events'])} changed events")

A failed run exits nonzero and does nothing else. The next scheduled run is the retry, and the cursor is untouched, so nothing is fetched twice and nothing is skipped.

The wrapper that carries the environment

Task Scheduler starts tasks with a minimal environment, and the two variables the script needs are not there by default. The clean fix is a wrapper batch file that sets them and then calls Python:

@echo off
set ODDS_BASE_URL=https://your-base
set ODDS_API_KEY=your-key
"C:\Python312\python.exe" C:\odds\fetch.py >> C:\odds\odds.log 2>&1

Two details bite often. Quote the interpreter path if it contains spaces, and when you create the task, fill in the Start in field with the script's folder, because the task's working directory is not the script's directory. Relative paths inside the script resolve against Start in, which is why the cursor path above is absolute.

The task itself

One schtasks command creates the schedule:

schtasks /create /tn OddsPoll /sc minute /mo 15 /tr "C:\odds\run.bat" /rl highest /f

This runs the wrapper every 15 minutes. Open the task in the Task Scheduler window once, and set the overlap policy to do not start a new instance: a slow run then never doubles up with the next one, the same job flock does on Linux. The Last Run Result column is the first place to look when something is off; 0x0 is success and anything else is a failed run with the reason in the log.

Watch the log, not the task

With no daemon, the appended log is the health check. One line per run with the changed-event count is enough: a day of lines proves the rhythm, and a log that stops growing points at the task, not the API. Check the quoted paths and the two variables first; both fail silently in a scheduled context.

The shape outgrows Task Scheduler exactly where the loop version does: prices faster than once a minute, or drops pushed to you, belong in a held-open connection and a service, not in a scheduler. For everything the free key can pace, a task, a wrapper and a cursor file are the whole installation.