What API customers asked for and what shipped, once a week. Read it here or have it emailed.
One email a week, only when an issue is published. Nothing else goes to this list, and every email carries a one-click unsubscribe.
Issue #5
This one is early. Issue #4 went out on Thursday; the four days since it carried more change than most full weeks, so rather than hold it to Thursday, here it is on Monday. What changed: which plan sells what, and the cutover rules that keep every existing key whole; team keys, per-key caps and overage on the commercial tiers; greyhound results rebuilt on our own collection for every state; a stable horse identity across meetings; in-place prorated upgrades and promotion codes that apply themselves; and MCP 0.3.0. Everything in here shipped between Friday morning and Monday 11:00. Several of the new things have no customer usage yet and say so.
POST /v1/racing/clv. Standard (A$29, 75,000 credits) keeps everything else and serves the closing-line archive over a rolling 90-day window; the window is stated on every archive response in X-Archive-Window-Days, on JSON as well as CSV. Business (A$99) gains IP whitelisting. The pricing page, the FAQ, the terms and llms.txt all render the same ladder from one source.betslip_win_url, betslip_place_url and betslip_app_only on next-to-go and changes, betslip_url on best-odds quotes, stripped at serve time so a cached response never leaks them. Routes exist for four books: Unibet, TAB, Ladbrokes and Neds. Monday's 09:00 canary passed Unibet and failed TAB (the slip renders, the runner is missing), so TAB's fields are null until a run passes. Ladbrokes and Neds open the app, not the browser, and are not tap-tested.POST /v1/racing/clv, Plus and above, 5 credits: send up to 200 bets and get each one scored against the closing line, as the named book's close or the market best or median, with the CLV percentage, whether you beat the close, the result, flat-stake profit and the aggregates. It is the calculation most model builders were doing by hand from the archive.unlimited, every gate and Stripe mapping is unchanged, and older changelog entries keep the old name. Two keys were on it and neither has a subscription, so nobody's invoice changed.Asked by a customer running one integration for several clients who wanted to hand each one a key and a budget, and two commercial customers who asked what happens on the last day of the month when the pool runs dry.
POST /v1/keys/team mints, GET /v1/keys/team lists each key's calls and credits this period beside the pool balance, PATCH sets a label or a cap, DELETE revokes and keeps the history. All four are free calls and only the primary key can make them. Charges land on the pool; the usage log keeps the calling key; whitelist, rate limit and last-used are per key. /v1/usage and /v1/keys/info carry the pool and the team.monthly_cap at mint or by PATCH, between 1 and the plan's monthly credits, null to remove. A capped key is metered on its own row and then the pool; at its cap it gets a 402 that names the cap and the label and charges nothing, with X-Credits-Key-Used, X-Credits-Key-Cap and X-Credits-Key-Remaining beside the pool headers. Caps reset with the monthly reset. No customer has minted a team key yet.X-Credits-Remaining reads 0 and two new headers, X-Credits-Overage and X-Credits-Overage-Ceiling, say where you are; the 402 at the ceiling names the ceiling, the rate, how to switch it off and the tier that raises it.POST /v1/billing/overage switches it on and GET shows the state; /v1/usage carries the same object and credits_remaining_with_overage. The charge is added to your next subscription invoice as one line, computed at the monthly reset before the counters zero, with a ledger row and an idempotency key so it cannot be billed twice. The first invoice that can carry one is 1 October, and the billing step has not yet run against a real account. The at-cap email, when overage is on, says overage is carrying you rather than that you are out.Asked by three customers in one week who wanted to show our prices inside their own product with nothing of ours visible, one of them through the request form.
GET /v1/bulk/datasets lists them at no credit cost, Business and above. GET /v1/bulk/{dataset}/{period} serves a day (2026-09-20, Business and above, 20 credits) or a month (2026-09, Platform and above, 100 credits), as Parquet or gzip CSV, with a sha256 ETag so a repeat fetch of an unchanged file is a free 304. Five datasets: closing lines, price paths, results, acceptances and track conditions, built nightly at 02:30 Sydney from the same SQL and column contracts as the paged endpoints, so a column added to the endpoint appears in the file. Your withheld-books list applies. August's closing lines are 11 MB.POST /v1/widgets creates one with your colours, your allowed origins, your link template with per-bookmaker overrides and an hourly fill budget; ten per account; the config is validated with a 422 that names the field. Embed https://puntersedge.online/widget/v1/<id>/racing, /racing-greyhound or /<sport>. Each fill costs the owner 2 credits for racing and 1 for a sport, and one render is cached for 60 seconds however many readers load it. Every refusal, whether no credits, an origin you did not allow, the budget, a revoked widget or the API being unreachable, renders Odds unavailable right now in your colours with HTTP 200, and the reason is only in an HTML comment and X-Widget-Status. The templates carry no PuntersEdge, no tracking parameter and no link home, and a test enforces that. Recipe, parameters, affiliate example and a CSP snippet at /widgets. It went live at 11:00 Monday; no widget has been created yet./v1/racing/greyhounds/form and /stats served from a store built on those results from 20:37. Data we collect and settle ourselves is data we can stand behind, extend and license, which is the point of the change.field_source is fasttrack or grsa. The first pass filled 292 Victorian and 653 other races back to the 13 September card. Over the seven days to Monday morning, 594 scraped races matched 594 of ours and 592 of 592 comparable fields agree with the bookmaker-fed placings; two are blocked pending. Fill by day since Friday: 133 of 137, 85 of 90, 110 of 111.grv: or grsa: reference is accepted as a filter, and the two registries are mapped where the same dog carries both (14 of 6,109 names on Sunday). An unknown dog is a free 404. Coverage starts at the 13 September card and the response says so. Runs now carry the measured columns the result pages print: first_split_s, second_split_s, weight_kg, pir and grade, where the source prints them: grade on every run, first split on 288 of 362 measured, weight on 295; Western Australia publishes no weight, and only FastTrack publishes position-in-running. Stats have no grade dimension yet, because stored runs are never rewritten and none of the older ones carry it.Asked by a customer joining form to results across meetings, who found the same horse under a different reference every time it ran.
runner_ref on a thoroughbred is Racing Australia's code for that day's entry, not the horse. Measured on Monday over our own results: of 1,452 horses with two or more runs, 1,450 carried a different code on every run, and one horse carried five codes over six runs. Every document that called it stable has been corrected. Greyhound references are per dog and are unchanged.horse_ref, the form pe: plus the folded name, now sits beside runner_ref on results runners and placings, next-to-go, best-odds, the closing-line archive and price-path exports in JSON, CSV and Parquet; greyhound and harness rows carry an explicit null. It is derived at serve time, so nothing had to be migrated. None of 2,708 acceptance names or 10,092 result names carried a country suffix and none collided. The known weakness: a name can be re-registered about seventeen years after a horse retires.GET /v1/racing/horses/runs, every plan, 3 credits: one horse's record out of our own settled results, no contact with Racing Australia, so it answers when the form endpoint cannot. 10,092 runs from 31 August to 20 September, refreshed hourly; win_time_s is null because Racing Australia publishes none. It is the fallback the MCP form tool now names.upstream.state, walled_since and walled_hours; the 503 text points at /horses/runs, which answers from our own results; and a watchdog pages after three hours walled with work queued. Customer-visible 503s on form: none Friday, 128 to four keys Saturday, 35 to two Sunday, none Monday by 11:00. The overnight pre-read of tomorrow's acceptances is next, once the wall lifts.Asked by customers moving between tiers, and everyone who has been sent a code.
CAPPED50, no expiry. It fires once per key per calendar month. The 500K thank-you code from last Thursday, half price for the first month, is open until Thursday 25 September./v1/demo/best-odds now compares Australian books only. The keyless demo had been taking the best price over every book we hold a reference feed for, offshore ones included, so its best_bookmaker and arb_exists could point at a price no Australian can take. Since Monday the playground and the landing pages that render it show the same market the paid endpoints always did./v1/health/stats, no key needed, now returns paying_customers: distinct billing customers on active paid keys, floored down to a multiple of five, so the site says 35+ and will say 35+ until it is 40.puntersedge-mcp 0.3.0 is on PyPI as of Monday 10:48, 30 tools. It teaches the assistant that runner_ref is same-meeting only and to join on horse_ref; adds horse_runs; adds racing_clv, its first POST tool; and adds bulk_datasets and check_overage, both free reads. The greyhound stats tool no longer claims a grade dimension. The Postman collection is rebuilt with the team-key routes, 67 requests.since is required on /v1/racing/changes, direction on /v1/racing/movers is firming or drifting, and a price-history example carried a venue and date that no longer existed. Pages more than three-quarters identical to their nearest neighbour fell from 22 to 2, the two being this page and the latest issue's permalink, which now declares the relationship. The eight /compare pages are still 82% boilerplate and are on the list./horses/runs answers from our own results in the meantime. The overnight pre-read is still not built./v1/racing/markets endpoint. Specified in full this weekend, no code yet.Reply with anything you want on the list. The two largest changes this issue, greyhounds from our own collection and a horse identity that survives the meeting, make the racing data ours to stand behind and to build on, and both are stated at their real coverage rather than the coverage we would like. Write to hamish@punters-edge.com or use the feature request form.
All issues