Documentation / Guides

Changelog

What changed and when: new endpoints, fields and platform features. Additive changes only within v1.

Dates are UTC. Within v1 the contract only gains fields — nothing documented here is removed or renamed without a version bump. Machine-readable spec: OpenAPI 3.1.

2026-10-08 — Australian prize money and tote dividends

  • Australian results now carry prize money and the tote's exacta and trifecta dividends. Today 4,190 Australian races carry prize money, 4,119 an exacta and 4,107 a trifecta, back to 2026 so far, and the count rises daily as the archive is filled back to 2014.
  • No new fields. They fill the same prize money, forecast and tricast fields a British result uses, in the same shape: "1st $1950 | 2nd $600 | 3rd $300 | 4th $75 Race Total $2925", "(3-2) $4.40 exacta", "(3-2-8) $26.90 trifecta". Amounts are Australian dollars.
  • An Australian exacta and trifecta are tote pools paid per $1, not the British computer straight forecast and tricast, so the pool's name is written into the value. Read the last word before comparing a British race with an Australian one.
  • Victorian races carry them, from each race's own result; races from the other states do not yet. Australian going is not published, so it stays null.
  • Nothing you have stored changes: the same race ids, with fields that were null now filled.

2026-09-23 — Race Day: today and yesterday, with the prices on

  • A new plan at $49 a month. Race Day covers yesterday, today and the cards ahead at every track, with each day counted in that track's own timezone so an Australian card arrives whole. It carries the fields the free key holds back: starting prices, Betfair back and lay, runner weights, race weather and forecast and tricast dividends, plus the results stream and webhooks.
  • It reaches 20 endpoints: the cards and results for its days, a race's runners, market, result and status, and the platform and reference endpoints every key has. 120,000 requests a month at 90 a minute. The full list is on the Race Day plan overview in the documentation.
  • Why it exists. Most people building on greyhound data want the prices on today's racing and nothing behind it: a racecard screen, a results bot, a tool that places bets. Until now the only way to get prices was Live at $99, which bundles the whole archive back to 2018. Race Day sells the day on its own.
  • The trade-off is depth. Race Day holds two days at a time. Racing before yesterday, a dog's form and past runs, odds history and the statistics endpoints stay on Live and Live Plus. Asking for an older date is refused with outside_plan_window, naming the window and the plan that carries it, rather than answered with an empty page that would read as "no racing that day". Moving up keeps the same key, with nothing to re-integrate.

2026-09-23 — Free key: yesterday, today and ahead, 50 lookups a day

  • A free key now reads the same days as Race Day: yesterday, today and the cards ahead, across every GB, IE and AU track. Today's and upcoming racecards and today's, yesterday's and the latest results are on it, beside the race and track listings it already had: 17 endpoints in all.
  • Cards and results come through in full: traps, dogs, trainers, owners, grades, distances, going, scheduled and off times, run times, sectionals, comments and finishing order. Starting prices, exchange prices, runner weights and race weather come back empty, each with a note in its place naming the plan that carries it, so a response is never short of a field without saying why.
  • What changed, and why. The free key reached back 7 days at 500 requests a day. That was enough to run a small product on without ever paying, which is not what a free key is for. It now reaches back to yesterday at 50 lookups a day: enough to see every field, build against real racing and judge the data, not enough to run on it.
  • The trade-off for older data. A free key no longer reads the week behind it: anything before yesterday is Live, which carries the full archive back to 2018, and the prices on the free key's own days are Race Day. Existing free keys keep working unchanged; they see the new window and allowance from today, and the daily count resets at 00:00 UTC.

2026-09-22 — Coverage ledger

  • New on every key, the free one included: GET /v1/coverage?date=. For each track and day, how many races were carded, run, resulted and abandoned, and how many Betfair's exchange listed that we never received. It answers "missing or abandoned?" before anyone has to ask.
  • The independent list is Betfair's own catalogue of greyhound WIN markets, collected every 15 minutes. British days from December 2022 to June 2025 are checked against the recorded history of that list; every day from today is checked live.
  • A day with no list says so: listed and missing come back null, never zero, because nothing was checked. Venues we do not carry are shown but never count as missing.
  • Not held to the free key's date window: it carries counts, not racing data.

2026-09-22 — Sire and dam statistics

  • New on Live and Live Plus: GET /v1/sires and GET /v1/dams. Every parent we hold figures for, with the whole record of its offspring: runs, strike rate, wins against what the market expected, how often they lead to the first bend and how their debuts go. Searchable by part of a name and sortable by any of those measures.
  • New on Live Plus: GET /v1/sires/{name}/stats and GET /v1/dams/{name}/stats. One parent's figures split by region, distance, trap, age and track, with the ten biggest winners.
  • Every sire and dam has its own id, sire_id and dam_id, on every row of the lists. The statistics take it in the path: /v1/sires/{sire_id}/stats. The ids stay the same from one weekly rebuild to the next.
  • GET /v1/sires/{sire_id}/progeny and GET /v1/dams/{dam_id}/progeny now take the id instead of the name. A name with a space in it was refused before it reached the API, so for most parents these were not answering at all. They also now count every spelling on record as one parent.
  • Coverage is Great Britain from 2018 and Ireland from 2026. Australian dogs carry no breeding at all, so none of their runs are counted. Irish racing before 2026 is left out because breeding is recorded for only part of those fields. Every response lists what was counted in meta.coverage, and a year joins by itself once 95% of its runs have breeding on record.
  • A figure that cannot be measured comes back null, never 0, with the count behind it: no priced races means no market comparison, not a bad one.
  • The figures are rebuilt weekly.

2026-09-21 — Race write-ups

  • New in Race commentary on live_plus: a written preview and report for each race, with a line on every runner, written from the records.
  • GET /v1/races/{race_id}/writeup returns one race. GET /v1/writeups/upcoming lists the previews for races in the next 1 to 48 hours; GET /v1/writeups/results lists complete write-ups for races run in the last 1 to 168 hours, or on one date.
  • Previews are written before the off, from the field and each dog's form, so they never know the result. The report is added once the race has run, and the preview is kept as it was.
  • Runner comments, race write-ups and the comment vocabulary now share one section, Race commentary.

2026-09-21 — Runner comments, in their own section

  • New section on live_plus: GET /v1/races/{race_id}/comments. How each runner ran, as events with the bend or point of the race they happened at, and a one-line summary in plain English.
  • Great Britain and Ireland describe the race as it was run. Australian racing carries the view before the off instead: a verdict on each runner's chance and the traits behind it.
  • GET /v1/comments/vocabulary lists every event, place, verdict and trait the section can return, with its wording, so nothing has to be guessed.
  • A remark we cannot describe with confidence is left out rather than guessed at, so a runner can come back with comments: null.

2026-09-18 — The weather every race ran in

  • Every race now carries a weather object: temperature, humidity, wind speed and direction, gusts, rainfall and a plain description of the conditions, for the hour that race went off at its own track. It is on the racecards, on a race, and inside result once the race has run.
  • New endpoint: GET /v1/races/{race_id}/weather, for the conditions on their own.
  • before_race is the part worth having. The hour of the off tells you what it was like at the traps; it cannot tell you that six millimetres fell that afternoon, which is what makes a track slow. So each race carries rainfall over the previous 6 and 24 hours and average wind over the previous 6.
  • It is hourly reanalysis for the grid point at the track, not a sensor at the bend, and source names which model an hour came from: a race in the last day or so reads forecast and becomes archive once the archive catches up, so the figure can change between the two.
  • A race whose start time was never recorded returns weather: null with a sentence saying why, rather than a guess. That is now a few hundred Irish races out of 123,000: Great Britain, Australia and Ireland are otherwise complete, back to 2018, 2022 and 2019.
  • No parameter to switch it on, and no rating, going estimate or adjustment applied to any time we publish. See the Weather guide.

2026-09-18 — Irish racing back to 2019

  • Irish coverage now starts in 2019 rather than 2022. Races, runners, finishing positions, winning and estimated times, weights, going, prize money, starting prices and the run comment, under the same Irish ids already in use, so nothing you have stored changes.
  • They carry a real off time, which took a second pass to get. An Irish RESULT is published with a date and no clock, so the first load of the archive had none: every race sat at midnight on its own date. The racecard for the same meeting does carry the time, and the site still serves those cards years later, so every meeting was fetched again and read for it.
  • 121,576 races were given their time this way, which is all but 312 of the archive. What is left is meetings the site has no card for, and races that were carded but never listed on it.
  • The time is the carded one, the site's own "Approx." It is when the race was due rather than the moment the traps opened, so it fills scheduled_start and off, and it is accurate to the minute the track published.
  • Weather followed from it. An hour can now be matched to an Irish race, so the same weather object GB and AU carry is on Irish racing back to 2019, including rainfall over the previous 6 and 24 hours.

2026-09-17 — A lighter racecard

  • Today's racecards takes a summary parameter. With summary=true you get the race name, scheduled time, distance, grade and track — and no runners. It defaults to false, so nothing changes for anyone already calling it.
  • A full GB and AU day is around a thousand runners. If you are listing what is on rather than reading a field, that is a lot of payload for nothing.
  • It drops the finishing positions too, not just the runners — those were the heavier half. A summary row is about a fifth the size of a full one.

2026-09-16 — Australia, not just Victoria

  • Coverage is stated as Great Britain and Australia throughout. The pricing page, the calendar, the track reference and the coverage guide all said Victoria only, and the guide named the other states as not covered.
  • The racing calendar lists every track we hold and how often it runs, which is the place to check a specific one before building on it.

2026-09-15 — The whole field in one call

  • New endpoint: GET /v1/races/{race_id}/field, on live_plus. Every runner in a race with its record at that track and trip, its record at that grade, how long since it last ran and how it has gone off breaks of that length, its measured early split, and the strike rate of the trap it has drawn.
  • It exists because describing a single race used to take a call per runner per measure. That is a lot of requests to answer one question, and the question is the same one every time.
  • The runner and the draw are reported as separate figures, so a record can be read against the box it came out of rather than blended into one number.
  • Runners short of history are named in thin and kept in runners. A field of six reads as six. Dropping a thin runner would change the shape of the race without saying so.
  • Measurements only, never a forecast — no rating, no rank, no projected winner, and every figure carries the run count behind it. A value of null means not measured, never zero: a dog that has never run at a grade has not failed there.
  • Form state is rebuilt nightly and form_state_computed_utc says when. Days since last run is the exception and is computed live against the race you asked about, because a stale gap is a wrong gap.

2026-09-10 — See exactly how much data there is, before you pay for it

  • New endpoint: [GET /v1/archive](/documentation/archive). It answers the first question anyone asks — how much is actually in here. Races and runners by track and distance, the earliest and latest date, and how many dogs, trainers, owners, meetings, pace profiles and trap tables are known.
  • Read races and coverage together. races is how much there is; coverage is how much of it carries the field you need. Exchange prices exist on roughly two thirds of races and are concentrated in recent years, so a strategy priced on the exchange has less to work with than the headline count suggests. Coverage varies per distance within a single track, and the endpoint reports it that way.
  • Recomputed nightly from completed races, so it costs one query a night rather than a count per caller. A count of null means that figure was not computed on the last run, which is not the same as zero.
  • Why we built it: we were answering "how much data do you have" with a sentence on the pricing page. Now it is a number you can check, in the same spirit as publishing the [trap-bias figures](/greyhound-trap-bias) rather than asserting them.

2026-09-10 — The whole platform section is now on the free key

  • A free key now reaches 11 endpoints instead of 7. The four added are [Pipeline status](/documentation/api-status), [Your usage](/documentation/usage), [All statistics](/documentation/archive) and [Reference data](/documentation/reference).
  • These were behind the same wall as the racing data, which never made sense. /v1/status and /v1/usage describe your own key and our pipeline; /v1/reference decodes grades, going codes and runner statuses; /v1/archive says how much data exists.
  • Withholding them meant a free key could not answer "is the feed live", "how many calls have I used", "what does grade A3 mean" or "how much data is there" — the questions you ask while deciding whether to pay. Answering those with a 403 was a poor argument for the product.
  • Nothing changes for existing keys except that these four now work on sandbox tier as well. The [sandbox](/sandbox) page lists the current set.

2026-09-07 — Fetch up to 50 records in one call

  • List endpoints now take an ids parameter: GET /v1/races?ids=A-B-C returns all three in one response. Available on [races](/documentation/list-races), [results](/documentation/search-results), [dogs](/documentation/search-dogs) and [tracks](/documentation/list-tracks).
  • If you are calling a single-record endpoint in a loop, this replaces it. Up to 50 ids, dash or comma separated. Every other filter still composes, so ?ids=A-B-C&status=complete narrows the batch.
  • Each id costs one call — a batch of fifty is charged as fifty, the same as fetching them one at a time. Batching saves you round trips and latency, not allowance. Charging it as a single request would let a key pull fifty times its limit, which would make the limit meaningless.
  • An id with no record refuses the whole request with 400 invalid_id, naming it. That is deliberate: returning the 49 that worked gives you a response that looks complete and is not, and the gap turns up later in something you have already shipped.
  • Use the [cursor](/documentation/pagination) instead when you are walking a set rather than naming one. Full details in [batch requests](/documentation/batch-requests).

2026-09-07 — Check our data before you pay for it

  • New [Data](/data-integrity) page, and a [Data integrity](/documentation/data-integrity) guide in the docs. Both let you look up any date and see exactly what we hold for it — races carded, results recorded, and anything without a result, with the reason.
  • Both are powered by a new open endpoint: GET /data-integrity.json. No key, no quota, no rate limit. It returns aggregate day counts, takes from, to and region, and accepts summary=1 for archive totals or group=month for a longer view. Wire it into your own monitoring if you want to watch our coverage without asking us.
  • complete + abandoned + unresolved always equals races. Completeness is measured against races that actually ran, so an abandoned meeting cannot flatter the figure.
  • Why we built it: a feed that is short on a given day is indistinguishable from a day when racing was called off, unless someone tells you which. Rather than asking you to take our word for it, the counts are public and checkable.

2026-09-07 — Races that will never have a result now say so

  • Some races are carded and never run — abandoned meetings, void races, cards lost to weather. Until now the API could only say "no result yet", which is indistinguishable from "no result ever" and left clients polling indefinitely.
  • Where we have confirmed against source that a race has no result and never will, its status is now abandoned, and [GET /v1/races/{race_id}/result](/documentation/race-result) returns 200 with the race, no positions, and meta.result_available: false. A definitive answer rather than an error.
  • Races that simply have not settled yet still return 409 with retry — true while the race is still to run, false once it has run and stalled.
  • Confirmation is never inferred from age. A race is only marked abandoned where the source lists the card without it, or confirms the whole meeting produced no result on any race.

2026-09-07 — A race with no result is no longer a 404

  • [GET /v1/races/{race_id}/result](/documentation/race-result) now returns 409 instead of 404 when a race exists but has no result. 404 said the race was missing when it was simply not settled, and that is a different problem needing a different response.
  • 404 now means one thing only: no race with that ID. The error code result_not_available is unchanged, so anything switching on error.code needs no edit.
  • The payload adds retry. true means the race has not run yet and polling will succeed eventually; false means it ran and was abandoned, voided or never settled, so polling is pointless.
  • These responses no longer count toward the error rate on our side either. Asking for a result that is not ready is a reasonable thing for a client to do, not a fault.

2026-09-07 — Results searches now tell you what is missing

  • [/v1/results](/documentation/search-results) adds meta.unresolved — the number of races that ran inside your query but have no result. Abandoned meetings, void races, cards that were never settled.
  • A day returning total: 130 with unresolved: 10 means the card was 140 and ten races have no finishing positions anywhere. Previously those races were simply absent, which is indistinguishable from data we had lost.
  • Nothing in data changes and no existing field moves, so integrations are unaffected. Races scheduled in the last six hours are excluded from the count, so tonight's cards do not inflate it.

2026-09-04 — The whole API as one downloadable file

  • New at [/documentation/manual.md](/documentation/manual.md): every endpoint, parameter, sample response and guide assembled into a single markdown document. About 160KB, roughly forty thousand tokens — small enough to paste whole into an assistant and still leave room for it to answer.
  • It is generated, not written. The endpoints come from the same definition the API routes on, the guides from the same source the documentation renders from, and the limits from the same configuration billing uses. There is no separate copy to forget to update.
  • The date on the document is the date the definition last changed, not the date you fetched it — so two downloads with no release between them are byte-for-byte identical and a diff shows only what actually moved.
  • Also new: [/llms.txt](/llms.txt), a short index pointing at the manual and listing every guide and endpoint, following the emerging convention for making a site legible to language models.

2026-09-03 — Race card with form, in one call

  • New endpoint GET /v1/races/{race_id}/form on Live Plus: the full race card with every runner's recent form attached. Built because get-race and dog-form are the two most-called endpoints on the platform and they are one question — this race, and how these dogs have been running. Answering it used to cost a call for the race and one per runner.
  • Every form line returned happened before the race went off, and the race itself is excluded. For a historical race, "recent runs by this dog" would otherwise include what it did afterwards — anyone testing a method against that would be measuring their own lookahead. The cutoff is echoed in meta.form_cutoff.
  • runs controls how many lines per runner: 6 by default, 12 at most. A runner with no prior completed runs returns an empty form array rather than an error — that is a first-timer.
  • One query, not one per runner. Looping the card server-side would only move the fan-out from your code into ours.

2026-09-03 — The racing calendar, and coverage you can check

  • New page at [/racing-calendar](/racing-calendar) — every meeting we hold, by track and by day, for the last thirty days. It is the coverage itself rather than a description of it, so you can see which tracks are carried and how often they race before you build anything.
  • New endpoint GET /v1/meetings/calendar, on every paid key rather than Live Plus. One row per track per day with the number of meetings and races on each. Answering the same question through /v1/meetings meant one call per day; this is one call for the range.
  • Dates are each track's own local date, so an Australian evening card lands on the day it ran in Victoria rather than the UTC day it started. Days with no racing are simply absent — a gap is the answer, not a missing row.
  • New guide [What we cover](/documentation/coverage): the regions, the roughly fourteen Victorian tracks, the states that are not covered, and the fields that are not present on every race — sectionals on about two thirds of British runs, going on British racing only.
  • The sample response on [List tracks](/documentation/list-tracks) showed Wentworth Park, which is in New South Wales and not covered. It now shows a Victorian track.

2026-09-02 — Meeting analysis and race precedents

  • New endpoint GET /v1/meetings/{meeting_id}/analysis, on Live Plus — a whole card as one unit. Every winning time measured against par for its own distance, so a card mixing 400m and 575m is still comparable, with spread_s showing whether the surface held through the evening.
  • agreement_s compares that measurement against the published going, which is a signed hundredths-of-a-second track adjustment. It says whether the official figure matched the racing, and when it disagrees that is a finding rather than an error. GB only — almost no Australian races carry a going value.
  • Trap wins on a card are returned as counts, with no percentage. One trap taking three of twelve races is 25% against 16.7% expected, and that is indistinguishable from chance. The trap statistics on /races/{race_id}/conditions rest on thousands of races.
  • New endpoint GET /v1/races/{race_id}/precedents, on Live Plus. Races from the archive that ran under matching conditions at this track and distance, with what actually happened in them.
  • There is no similarity score. A single number hides its own weighting — 0.94 could mean identical except going, or the same track with the wrong grade and field size, and you can neither tell them apart nor re-weight it. Every row carries matched_on and differs_on instead, so you filter for your own question.
  • Matching relaxes when it must, and says so. Of 1,182 distinct track, distance, grade and field-size combinations across two years of GB racing, 407 hold fewer than ten races. Field size drops first, then grade, and matching.relaxed names what was given up. Track and distance never relax.
  • No aggregate is returned. The frequency of an outcome across these races is history, not a probability — you can compute it from what comes back, but presenting it as a finding would be a claim we do not make.
  • Also available as the race_precedents tool on the [MCP server](/documentation/mcp-server-claude-chatgpt).

2026-09-02 — Performance trend, layoff record, and everything about a race in one request

  • GET /v1/dogs/{dog_id}/trend — whether a greyhound is running quicker or slower lately. Every run is measured against par, its time minus the average winning time for that track and distance, so a dry fast night does not read as improvement. Narrow it with track_id and distance_m to ask whether the dog is improving over one specific trip.
  • The change is reported beside run_to_run_stdev_s, the dog's own variation. A six-dog field spreads about a quarter of a second, so a 0.06s change across eight runs is noise — and when the change is smaller than the variation, the response says there is no clear trend rather than leaving you to work it out.
  • GET /v1/races/{race_id}/analysis — conditions and early pace in one request, joined per runner. The same figures as the two endpoints it draws on, assembled so an application stops making three calls and doing the join itself.
  • GET /v1/dogs/{dog_id}/layoff — how a greyhound has run after a week off, a fortnight, a month, two months. Most dogs race weekly and have very few runs after a long break, so the dog's own figures appear as counts with the rate suppressed below five runs, beside a population figure drawn from tens of thousands of runs. That comparison is what makes a small personal number readable.
  • avg_vs_par_s on the layoff buckets answers what a strike rate cannot: whether greyhounds are actually slower after a break, measured against the average winning time for each course.
  • All three are Live Plus. None rates a runner, ranks a field by chance, or forecasts a result.

2026-09-02 — Early pace — who gets away quickly, measured rather than judged

  • New endpoint GET /v1/races/{race_id}/pace, on Live Plus. Each runner's average early split from past runs at this track over this exact distance, with its best, the number of runs behind it, and how it compares to the rest of the field.
  • The scope is narrow on purpose. Same track: GB splits average 4.84s and AU 7.17s, so they are not the same measurement. Same distance: at Romford the split reads 3.86s over 400m, 13.84s over 575m and 24.48s over 750m — it is not a fixed point on the track. Handicaps excluded: runners start from different marks, so the run to the split differs per dog.
  • Grade is deliberately not in the scope. At Romford over 400m, grade A averages 3.86 and Open 3.78 — eight hundredths apart, against a within-field spread of about a quarter second. Splitting by grade would shrink every sample to buy noise.
  • A runner needs three timed runs before a figure is published. Below that it is listed under without_data with its count and the reason, because a two-run average looks exactly as authoritative as a twenty-run one and is worth far less. Runners we cannot measure are named, never quietly dropped from the field.
  • vs_field_s compares a runner against the others we can measure — negative is quicker away. spread_s is the gap between quickest and slowest: narrow and they reach the first bend together, wide and someone gets clear.
  • Sprints carry no split at all, so those races return an empty list and say why rather than appearing broken.
  • Also available as the race_pace tool on the [MCP server](/documentation/mcp-server-claude-chatgpt).

2026-09-02 — Race conditions — the context a race runs in, in one call

  • New endpoint GET /v1/races/{race_id}/conditions, on Live Plus. How each trap has performed at this track over this exact distance, what a winning time looks like there, and how experienced the field is.
  • Everything in it could already be assembled from other endpoints — a track statistics call plus one per runner, then picking the right distance bucket and weighting it yourself. This is that work done once.
  • Every figure carries its sample size. A trap bias drawn from 40 races is noise and one drawn from 11,000 is signal, and you cannot tell them apart from the percentage alone. Trap percentages are shown against an even break, so a number is readable without counting boxes, and where the sample is thin the response says so.
  • Winning times cover a rolling 24 months with average, fastest and standard deviation. Field figures describe the runners' careers to date — average win rate, average runs, and the experience spread across the race.
  • Nothing here rates a runner or forecasts the race, and the response says as much rather than leaving it implied.
  • Also available as the race_conditions tool on the [MCP server](/documentation/mcp-server-claude-chatgpt).

2026-09-02 — Live odds — best Betfair back and lay price on every runner

  • Every runner can now carry a betfair block with back_price, lay_price, selection_id and last_update. Both prices are decimal odds taken from the top of the Betfair exchange book, and they appear wherever a runner does — race cards, results and career form — so a historical price comes back attached to the run it belongs to.
  • For a single race, GET /v1/races/{race_id}/market returns every runner's prices together with the exchange market id.
  • Prices are captured in roughly the last three minutes before the off and refresh every few seconds until the traps open. A race further out than that returns betfair: null — the market exists, but there is rarely enough money in it that far ahead for a price to mean anything. Test for presence rather than assuming it is there.
  • last_update is the moment the price was read from the exchange, not the moment you called us. It is the only honest measure of how stale a number is, and it is worth trusting over your own clock. A few seconds is normal; several minutes means the race has already run and you are looking at the final capture before the off.
  • That final price is the interesting one. It sits alongside the official sp on results and form, so you can compare what the bookmakers returned against what the exchange was actually paying at the same moment. They disagree more often than you would expect.
  • What this is not: it is the best price on each side, not the depth or size behind it; each capture overwrites the last, so you get the current price rather than how it moved; and a short price reflects what other people are willing to bet, nothing more. As with everything here, it is market data — never a rating, a forecast or a suggestion.
  • Full detail, including how to poll for a live price without hammering the endpoint, is in the new [Live odds](/documentation/live-odds) guide.

2026-09-01 — Four fields retired from the runner payload

  • The following are no longer returned and are no longer supported: comment and btn on runners, and total_volume and trade_count inside the betfair block.
  • The comment_codes decode table has been removed from GET /v1/reference for the same reason. That endpoint now returns regions, race_statuses and runner_statuses.
  • Every documented sample, the OpenAPI description and the Postman collection have been regenerated so what is published matches what is served.
  • If you read any of these fields, they will now be absent rather than null. Code that treats a missing key as an empty value needs no change; code that assumes the key exists does.

2026-08-30 — Ask the archive in plain English

  • New at [/ai-greyhound-racing-assistant](/ai-greyhound-racing-assistant): ask a question about British or Australian racing in ordinary language and get the answer looked up through this API, live, with the tool calls shown as they happen.
  • It answers from the data and nothing else. Every figure it gives you came from an endpoint you can call yourself, and it will tell you when a race has not been run rather than guessing at a result. It does not predict, rate runners or suggest bets, and it will decline if you ask it to.
  • Ten questions a week on the free plan, 100 on Live, 250 on Live Plus. The API calls behind each answer are on us — your own key and its limits are untouched.
  • Conversations are threaded and keep their context, so a follow-up like "and over 500m?" works without repeating yourself. Each answer proposes three follow-up questions, which is usually the harder half of asking a good one.

2026-08-28 — MCP server — use the API from Claude or ChatGPT

  • An MCP endpoint at https://greyhoundapi.com/mcp exposes 15 tools generated straight from the endpoint registry, so what the assistant can call and what the documentation describes cannot drift apart.
  • Point Claude Desktop or the OpenAI Responses API at it, authenticate with your existing key, and ask questions in plain language. Your tier applies exactly as it does over REST — a sandbox key reaches sandbox data, a Live Plus key reaches the split statistics.
  • Setup, the full tool list and the one configuration field people usually get wrong are in the [MCP server](/documentation/mcp-server-claude-chatgpt) guide.

2026-08-26 — Errors that tell you what to do about them

  • An empty result set now explains itself. Ask a sandbox key for a date outside its window and the response carries meta.empty_because naming the date you asked for, the window your key reaches and a date that would have worked — rather than an empty array that looks identical to a genuinely quiet day.
  • The window guard says which side of the window you fell off. "Too recent" and "too old" are different problems with different fixes, and the message now distinguishes them and names the dates involved.
  • These hints cover date, date_from and date_to. Range queries previously returned empty with no explanation at all, which was the most common way to conclude the data was missing when it was simply out of reach.

2026-08-24 — Timezone handling and mobile documentation

  • Race times were already returned as both scheduled_start.utc and scheduled_start.local, but the local value is the track's own calendar date — which is what a date filter matches against. The [Timezones](/documentation/timezones) guide now says so explicitly, because an Australian evening card belongs to the following UTC day and that catches people out.
  • The documentation reads properly on a phone. Wide reference tables scroll within themselves instead of widening the page, and the endpoint list collapses rather than filling the screen before you reach the content.

2026-08-20 — The free sandbox now covers every track

  • The sandbox key was scoped to three tracks — Romford and Nottingham in GB, Sandown Park in AU. That restriction is gone: a free key now reaches every GB and AU track in the API, the same list a live key sees.
  • Nothing else about the free tier changed. It still covers the race and track endpoints only, still serves races between 24 hours and 7 days old, and is still capped at 60 requests a minute and 500 a day. The limit on a free key is when, not where.
  • Why: the track restriction was costing more than it protected. Someone evaluating the API almost always wants to look up a course they already know, and if that course was not one of the three they had no way to tell whether the data existed at all — a missing track and an empty response look identical from outside. The data window is what separates the free tier from the paid one, and it is untouched.
  • If you hold a sandbox key, no action is needed and nothing you have already built will break — this only widens what your existing key resolves. Requests for tracks that previously returned 403 upgrade_required now return data.
  • The sandbox notice attached to every free-key response no longer claims a track limit, and the sandbox scope is now declared in one place that the gate, the notice and the published documentation all read — so what the pricing page says the free key covers is what the API enforces.

2026-08-19 — Owner statistics — including which yards an ownership actually wins with

  • New endpoint GET /v1/owners/{owner_id}/stats. Owners had two endpoints against the trainers' four; this closes most of that gap. Strike rate for one ownership split by track, distance, trap, grade and going, over a rolling window.
  • The split unique to this endpoint is by_trainer — runs, wins and strike rate for every yard the ownership has runners with. It answers a question no other endpoint can: not how the dogs are doing, but which trainers the ownership is getting results from. A syndicate with three yards can see which one is earning its keep.
  • Accepts months (default 12, maximum 60) and track_id to restrict every split to a single track.

2026-08-19 — Trainer statistics — strike rate by track, distance, trap and grade

  • New endpoint GET /v1/trainers/{trainer_id}/stats. GET /v1/trainers/{trainer_id}/results returns what happened; this returns what tends to happen. Splits cover track, distance, trap, grade and going, each with runs, wins, places, strike rate and place rate.
  • The question it exists to answer is "how does this yard do at this track". Previously that meant paging through every result and counting by hand. It is now one call, or one call with track_id to narrow every split to a single course.
  • Bounded to a rolling window — months, default 12, maximum 60. This is deliberate: a busy yard has thousands of runners in a year, and an unbounded career scan on every request would be slow for every caller, not just the one asking.

2026-08-19 — Dog statistics — career splits by distance, trap, track, grade and going

  • New endpoint GET /v1/dogs/{dog_id}/stats. Five splits over a dog's whole career, each with runs, wins, places, win percentage and place percentage, plus an overall block carrying first and last run dates.
  • by_distance also returns best_time_s and avg_time_s. Those two fields appear nowhere else, and that is on purpose: a time averaged across mixed trips is not a rough figure, it is a meaningless one. A dog running 268m in 16s and 500m in 29s has an "average" that describes neither. Rather than ship the fields everywhere with a caveat, they are omitted wherever distance varies.
  • by_going is banded — fast, slightly fast, standard, slightly slow, slow — rather than grouped on the raw going allowance. Grouping on the raw value split a 59-run career across ten buckets of three or four runs each, none large enough to read anything into. The underlying values are still returned in going_values, so nothing is hidden.
  • Completed runs only: reserves and withdrawals are excluded throughout. A split with no runs is omitted rather than returned as zero, so an absent trap means the dog has never boxed there — not that it has run and never won.

2026-08-19 — The REST surface is now 38 endpoints — three of them on Live Plus

  • Three additions take the API from 35 endpoints to 38: dog, trainer and owner statistics. No existing endpoint changed shape, and no field was removed — everything already built against v1 keeps working.
  • All three are in the OpenAPI schema, the Postman collection and the "Run it live" console automatically, because the documentation and the router are generated from the same registry.
  • The three statistics endpoints are part of Live Plus. Live keys reach the other 35 and receive 403 upgrade_required on these, with a message naming the endpoint and pointing at the plan comparison — no silent failure and no empty payload.
  • Why these three: they return pre-aggregated splits computed across the whole archive, which is materially more expensive per call than returning rows. Putting them with the higher plan keeps the base plan fast and gives the upgrade something concrete behind it.
  • The endpoint count shown across the site is now counted from that registry rather than written into the copy, and it is tier-aware — the pricing table reads 35 for Live and 38 for Live Plus from the same source the gate uses, so the two cannot drift apart. It had been hardcoded in six places, which is how a number quietly goes stale.

2026-08-10 — Documentation redesign — colour-coded requests and responses, and a rebuilt "Run it live"

  • Every request example and JSON response across the documentation is now syntax-highlighted — keys, strings, numbers and booleans each in their own colour — making payloads far easier to scan. It applies to all 35 endpoint pages, the guides and the quickstart.
  • Redesigned code panels: the five request examples (cURL, PHP, JavaScript, React and Python) sit in one card whose language tabs form the panel header with a single copy button, and the sample Response now sits under a header showing a green 200 OK status and its content type.
  • The "Run it live" console was rebuilt: your key, the parameters and the Run button are grouped into one card, and the live result mirrors the documented Response — a header whose status chip is colour-coded by outcome (green for 2xx, amber for 4xx, red for 5xx), the request line with timing, rate-limit remaining and request id, and the response body syntax-highlighted. It re-colours correctly on every run.

2026-08-10 — Page numbers in list meta: next_page, prev_page & totals on the first page

  • meta.next_page and meta.prev_page: every page-based list response now includes the next and previous page numbers (null at the ends) alongside the existing page, per_page, total and total_pages — a pager has what it needs without computing offsets.
  • The first page of a list now carries the page picture without opting into ?page=: a default call (no cursor) to the race, racecard and result lists returns page, per_page, total, total_pages and next_page, so you learn the total size of a result set from the first request. Cursor continuations stay lean, returning only count and next_cursor.

2026-08-10 — Webhooks, richer race status & a fresher tracks list

  • Webhooks for push delivery: subscribe an HTTPS endpoint to result.received, race.status_changed and card.updated for GB, AU or both, and stop polling. Deliveries are signed (HMAC-SHA256 in an X-GAPI-Signature header), retried with backoff, and at-least-once with an X-GAPI-Delivery id for de-duplication. Manage them yourself in the dashboard under Webhooks — add an endpoint, choose events and regions, get a signing secret shown once, and send a test delivery. A new Webhooks guide covers the payloads and verification.
  • New race lifecycle status results_pending: once a race has jumped and a short settling period has passed with no final result, it reports results_pending instead of remaining on off, so you can tell a race still to run apart from one that has run and is awaiting its result. It applies to GB and AU, and appears across the race endpoints and GET /v1/races/{id}/status.
  • New result_status field on the result block and GET /v1/races/{id}/status: provisional while placings are still arriving, final once every dog that ran has a finishing position — so a winner-only result is distinguishable from a settled one. Documented in Data freshness.
  • GET /v1/tracks now lists only tracks raced on in the last two weeks across GB and AU, rather than the all-time roster, so the list reflects tracks currently in rotation. Individual track lookups (get-track, track-races, track-stats) are unchanged and still resolve any track by id.
  • Fixed the /status day counters — races and results today, and the results-delay percentiles — to count by race day and exclude synthetic sample data, so they reflect real racing accurately.

2026-08-01 — Live Plus Launched

  • New payment tier added for Live Plus which gives increased request rate.

2026-07-29 — Page-number pagination on list endpoints

  • List endpoints now accept a 1-based page parameter alongside cursors — for example ?page=2&limit=100 — and add meta.page, meta.per_page, meta.total and meta.total_pages to the response.
  • Cursors are unchanged and remain the recommended approach for large or streaming exports; a page-number response also returns a next_cursor, so you can start with page numbers and switch to cursors for a full pull.
  • The Pagination guide now documents both approaches side by side.

2026-07-27 — Status Page updated and Chat support added

  • Status page expanded to offer deeper insight of the current upstream inner workings.
  • Chat support added on-site.

2026-07-09 — Status page, dashboard usage & the live stream endpoint

  • Public status page at /status: an overall banner plus live per-component health for the results pipeline, the WebSocket stream and the REST API — each with a rolling 90-day uptime chart.
  • Pipeline freshness on the status page: results today, results in the last 24 hours, the latest result received, and the median and p95 results delay.
  • Account dashboard gains an API usage view — a 90-day daily request heatmap, a 30-day request bar chart, and a breakdown of the endpoints you hit.
  • Billing on the dashboard: plan status, your renewal date and invoice history, with a link straight to the Stripe billing portal.
  • WebSocket stream reachable at wss://greyhoundapi.com:8443/v1/stream (proxied by Cloudflare); a live key still allows up to two concurrent connections.

2026-07-08 — v1 — full surface, self-serve accounts, docs console

  • All 35 REST endpoints live: racecards, races, results, meetings, dogs, trainers, owners, tracks, breeding, Betfair market summaries and platform endpoints.
  • Self-serve accounts: sign in with Google, free sandbox key in seconds, live plan at $99/month with Stripe checkout and billing portal.
  • Key lifecycle from the dashboard — create, label, rotate with a 24-hour overlap, revoke. Keys are shown once and stored hashed.
  • "Run it live" console on every endpoint page: real requests from your browser with your own key.
  • OpenAPI 3.1 export at /openapi.json, generated from the same registry that drives the documentation and the API router.
  • Postman collection at /postman-collection.json (Postman v2.1 — also imports into Insomnia and Bruno): all 35 endpoints grouped by section, X-API-Key auth pre-wired to an {{apiKey}} variable, and a saved example response on every request.
  • Errors guide documents the full code set, including method_not_allowed and the pre-launch-only not_implemented.
  • WebSocket stream for live results, status and card channels, with a 15-minute replay window and up to two connections per live key.

2026-07-04 — Access tiers, rate limits & wider track coverage

  • Two access tiers: a free sandbox and a single live plan. The sandbox is scoped to the race & track endpoints over a rolling 7-day window at 500 requests a day.
  • Per-key rate limiting: a per-minute ceiling and a daily quota, with the live plan raised well above the sandbox.
  • GB + AU track coverage: race and track listings across the major British and Australian venues, each with its own distances.

2026-07-01 — Documentation hub

  • Thirty-five endpoint pages with parameters, five-language request examples and canonical response payloads.
  • Guides: getting started, authentication, pagination, rate limits, errors, timezones, data freshness, sandbox, WebSocket stream.
  • Interactive head-to-head demo on the sandbox page built from real documented payloads.

2026-06-25 — Platform foundation

  • Registry-driven v1 API: a single registry drives the request router, the documentation and the OpenAPI spec, so they never drift apart.
  • Core data model for Great Britain and Australia greyhound racing: tracks, meetings, races, runners and results, all under one consistent JSON envelope with request ids.
  • API keys stored as SHA-256 hashes; every request authenticates with the X-API-Key header, and unknown or revoked keys are rejected consistently.