Documentation / Dogs

Dog statistics

GET/v1/dogs/{dog_id}/stats

Available on Live Plus only

A Live key reaches the other 35 endpoints and receives 403 upgrade_required here. Compare plans

Career splits for one greyhound: strike rate by distance, trap, track, grade and going, with best and average winning times in each — what tends to happen, rather than what happened.

Authenticate with your key in the X-API-Key header — see Authentication. Works with sandbox keys inside the sandbox data window.

Parameters

NameInTypeRequiredDescription
dog_id path integer yes The dog identifier.

Request

curl "https://api.greyhoundapi.com/v1/dogs/655044/stats" \
  -H "X-API-Key: $GAPI_KEY"
<?php
$ch = curl_init('https://api.greyhoundapi.com/v1/dogs/655044/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/655044/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/655044/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/655044/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"])

Response

200 OKapplication/json
{
    "meta": {
        "request_id": "req_8f2ac1",
        "data_as_of": "2026-07-08T13:05:12Z"
    },
    "data": {
        "dog_id": 655044,
        "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": "fast",
                "going_values": [
                    -40,
                    -20
                ],
                "runs": 4,
                "wins": 0,
                "places": 3,
                "win_pct": 0,
                "place_pct": 75
            },
            {
                "going": "standard",
                "going_values": [
                    0
                ],
                "runs": 9,
                "wins": 1,
                "places": 6,
                "win_pct": 11.11,
                "place_pct": 66.67
            }
        ]
    }
}

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.

GET/v1/dogs/{dog_id}/stats

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 five ways: by distance, by trap, by track, by grade and by going. 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.

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.