Dog statistics
/v1/dogs/{dog_id}/statsAvailable on Live Plus only
A Live key reaches the other 42 endpoints and
receives 403 upgrade_required here. Compare plans
Career splits for one greyhound: strike rate by distance, trap, track, grade, going and trainer, with best and average winning times in each — what tends to happen, rather than what happened. Narrow every split to one track and distance with track_id and distance_m.
Authenticate with your key in the X-API-Key header, or
?key= if your tool cannot set one —
see Authentication. Works with sandbox keys
inside the sandbox data window.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
dog_id |
path | integer | yes | The dog identifier. |
track_id |
query | integer | no | Restrict every split to this track. Combine with distance_m to ask "at this track, over this trip". |
distance_m |
query | integer | no | Restrict every split to this distance. |
Request
curl "https://api.greyhoundapi.com/v1/dogs/3514967427/stats" \
-H "X-API-Key: $GAPI_KEY"<?php
$ch = curl_init('https://api.greyhoundapi.com/v1/dogs/3514967427/stats');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['X-API-Key: ' . getenv('GAPI_KEY')],
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
print_r($response['data']);const res = await fetch('https://api.greyhoundapi.com/v1/dogs/3514967427/stats', {
headers: { 'X-API-Key': process.env.GAPI_KEY }
});
if (!res.ok) throw new Error(`GreyhoundAPI ${res.status}`);
const { meta, data } = await res.json();
console.log(meta.data_as_of, data);import { useEffect, useState } from 'react';
export default function Example() {
const [data, setData] = useState(null);
useEffect(() => {
fetch('https://api.greyhoundapi.com/v1/dogs/3514967427/stats', {
headers: { 'X-API-Key': import.meta.env.VITE_GAPI_KEY }
})
.then((r) => r.json())
.then((body) => setData(body.data));
}, []);
if (!data) return <p>Loading…</p>;
return <pre>{JSON.stringify(data, null, 2)}</pre>;
}Browser code exposes whatever key it ships with — use a sandbox key for prototypes and proxy live keys through your own server in production.
import os
import requests
r = requests.get(
"https://api.greyhoundapi.com/v1/dogs/3514967427/stats",
headers={"X-API-Key": os.environ["GAPI_KEY"]},
timeout=10,
)
r.raise_for_status()
body = r.json()
print(body["meta"]["data_as_of"], body["data"])curl "https://api.greyhoundapi.com/v1/dogs/3514967427/stats?key=$GAPI_KEY"<?php
$url = 'https://api.greyhoundapi.com/v1/dogs/3514967427/stats' . (str_contains('https://api.greyhoundapi.com/v1/dogs/3514967427/stats', '?') ? '&' : '?') . 'key=' . getenv('GAPI_KEY');
$ch = curl_init($url);
curl_setopt_array($ch, [CURLOPT_RETURNTRANSFER => true]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
print_r($response['data']);const res = await fetch(`https://api.greyhoundapi.com/v1/dogs/3514967427/stats?key=$GAPI_KEY`.replace('$GAPI_KEY', process.env.GAPI_KEY));
if (!res.ok) throw new Error(`GreyhoundAPI ${res.status}`);
const { meta, data } = await res.json();
console.log(meta.data_as_of, data);import { useEffect, useState } from 'react';
export default function Example() {
const [data, setData] = useState(null);
useEffect(() => {
fetch(`https://api.greyhoundapi.com/v1/dogs/3514967427/stats?key=$GAPI_KEY`.replace('$GAPI_KEY', import.meta.env.VITE_GAPI_KEY))
.then((r) => r.json())
.then((body) => setData(body.data));
}, []);
if (!data) return <p>Loading…</p>;
return <pre>{JSON.stringify(data, null, 2)}</pre>;
}Browser code exposes whatever key it ships with — use a sandbox key for prototypes and proxy live keys through your own server in production.
import os
import requests
r = requests.get(
"https://api.greyhoundapi.com/v1/dogs/3514967427/stats",
params={"key": os.environ["GAPI_KEY"]},
timeout=10,
)
r.raise_for_status()
body = r.json()
print(body["meta"]["data_as_of"], body["data"])A key in the URL appears in server logs, browser history and anything the link is pasted into. Convenient for a spreadsheet or a quick look; the header is safer everywhere else.
Response
{
"meta": {
"request_id": "req_8f2ac1",
"data_as_of": "2026-07-08T13:05:12Z"
},
"data": {
"dog_id": 1655044329,
"name": "A Bad Winter",
"overall": {
"runs": 59,
"wins": 11,
"places": 33,
"win_pct": 18.64,
"place_pct": 55.93,
"first_run": "2022-03-25",
"last_run": "2024-09-20"
},
"by_distance": [
{
"distance_m": 462,
"runs": 28,
"wins": 6,
"places": 18,
"win_pct": 21.43,
"place_pct": 64.29,
"best_time_s": 28.02,
"avg_time_s": 28.5
},
{
"distance_m": 500,
"runs": 15,
"wins": 4,
"places": 11,
"win_pct": 26.67,
"place_pct": 73.33,
"best_time_s": 29.05,
"avg_time_s": 29.46
}
],
"by_trap": [
{
"trap": 1,
"runs": 40,
"wins": 9,
"places": 23,
"win_pct": 22.5,
"place_pct": 57.5
}
],
"by_track": [
{
"track": "Kinsley",
"runs": 39,
"wins": 7,
"places": 21,
"win_pct": 17.95,
"place_pct": 53.85
}
],
"by_grade": [
{
"grade": "A4",
"runs": 14,
"wins": 3,
"places": 9,
"win_pct": 21.43,
"place_pct": 64.29
}
],
"by_going": [
{
"going": "standard",
"going_values": [
0
],
"runs": 28,
"wins": 7,
"places": 16,
"win_pct": 25,
"place_pct": 57.14
}
],
"by_trainer": [
{
"trainer": {
"trainer_id": 3095937498,
"name": "M E Wiley"
},
"runs": 27,
"wins": 8,
"places": 17,
"win_pct": 29.63,
"place_pct": 62.96,
"avg_vs_par_s": -0.05,
"first_run": "2025-04-01",
"last_run": "2026-05-18",
"note": null
},
{
"trainer": {
"trainer_id": 1180000118,
"name": "P W Young"
},
"runs": 3,
"wins": 0,
"places": 1,
"win_pct": null,
"place_pct": null,
"avg_vs_par_s": 0.277,
"first_run": "2026-06-11",
"last_run": "2026-08-11",
"note": "3 runs \u2014 too few for a rate, counts only."
}
]
}
}Run it live
A real request from your browser straight to api.greyhoundapi.com
with your own key — nothing is proxied or logged by this page.
Held in this tab only, sent only to the API host. No key yet? Create a free sandbox key — it runs every endpoint against the sandbox data window.
What this is for
A greyhound's raw form line tells you what happened. It does not tell you what tends to happen — and those are different questions. A dog with four wins from twenty runs looks ordinary until you notice all four came over 400 metres from trap 1, and that it has never won over 500. That pattern is invisible in a results list and obvious in a split.
This endpoint returns the whole career cut six ways: by distance, by trap, by track, by grade, by going and by trainer. Every split carries runs, wins, places, strike rate and place rate, so a small sample announces itself rather than hiding behind a percentage. Three runs at 33% and thirty runs at 33% are not the same claim, and the runs field is there so your code can tell them apart.
The splits are independent by default, which is a real limitation: by_track says Romford 18% and by_distance says 400m 20%, but neither says Romford over 400m — the only figure that matters for a specific race. Pass track_id and distance_m and every split narrows to those conditions, so by_trap becomes this dog's trap record at this track over this trip.
by_trainer shows each spell in a dog's career separately, with dates, so a change of kennels is visible rather than averaged away. Around four in five greyhounds keep one trainer, so this is usually a single entry — but for the rest it answers a question the individual records cannot: did the dog run better after the move? Each spell carries avg_vs_par_s, its time measured against the average winning time for each course, because two trainers use different tracks and raw times are not comparable. A dog that changes kennels usually does so because something else changed — age, injury, a drop in form — so a weaker later spell is not evidence about that trainer.
It is built for the moment before a race, when you have six dogs and a card and need to know which of them has actually done this before — this trip, this box, this track.
Common uses
Feeding a model or an AI assistant
Language models reason well over small, clean, pre-aggregated inputs and badly over hundreds of raw form lines. One call returns a compact profile that fits comfortably in a prompt, which is why this is usually a better tool call than /form when the question is comparative rather than historical.
Race previews and tipping content
Pull the six runners in a race, take each dog's by_distance and by_trap rows for the trip being run, and you have the factual spine of a preview without writing an aggregation layer.
Trap and draw research
Combine by_trap here with /v1/tracks/{track_id}/stats to separate a dog's own box preference from the track's draw bias — a fast dog from trap 1 at Romford is partly a fast dog and partly trap 1 at Romford.
Sales, syndication and stud research
Strike rate broken down by grade and distance is the honest version of a sales pitch, and first_run / last_run show whether a record is current or historical.
Notes
best_time_s and avg_time_s appear only in by_distance. A time averaged across mixed trips is meaningless — a 268m sprint and a 500m race do not share a scale — so the fields are omitted elsewhere rather than shipped with a caveat. going is banded (fast, slightly fast, standard, slightly slow, slow) because the raw allowance splits a career into slices too small to read; the underlying values are returned in going_values. Completed runs only — reserves and withdrawals excluded, and a split with no runs is omitted rather than returned as zero.