Trainer statistics
/v1/trainers/{trainer_id}/statsAvailable on Live Plus only
A Live key reaches the other 35 endpoints and
receives 403 upgrade_required here. Compare plans
Strike rate for one trainer split by track, distance, trap and grade over a rolling window — answering how a yard performs AT A GIVEN TRACK, which a flat results list cannot.
Authenticate with your key in the X-API-Key header —
see Authentication. Works with sandbox keys
inside the sandbox data window.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
trainer_id |
path | integer | yes | The trainer identifier. Trainer IDs come from Search trainers. |
months |
query | integer | no | Rolling window in months. Default 12, maximum 60. |
track_id |
query | integer | no | Restrict every split to one track. |
Request
curl "https://api.greyhoundapi.com/v1/trainers/118/stats" \
-H "X-API-Key: $GAPI_KEY"<?php
$ch = curl_init('https://api.greyhoundapi.com/v1/trainers/118/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/trainers/118/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/trainers/118/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/trainers/118/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
{
"meta": {
"request_id": "req_8f2ac1",
"data_as_of": "2026-07-08T13:05:12Z"
},
"data": {
"trainer_id": 118,
"name": "M E Wiley",
"window_months": 12,
"track_id": null,
"overall": {
"runs": 1887,
"wins": 342,
"places": 991,
"win_pct": 18.12,
"place_pct": 52.52,
"first_run": "2025-08-19",
"last_run": "2026-08-18"
},
"by_track": [
{
"track": "Romford",
"runs": 1831,
"wins": 335,
"places": 959,
"win_pct": 18.3,
"place_pct": 52.38
},
{
"track": "Central Park",
"runs": 43,
"wins": 6,
"places": 26,
"win_pct": 13.95,
"place_pct": 60.47
}
],
"by_distance": [
{
"distance_m": 400,
"runs": 1687,
"wins": 304,
"places": 886,
"win_pct": 18.02,
"place_pct": 52.52,
"best_time_s": 23.64,
"avg_time_s": 24.93
}
],
"by_going": [
{
"going": "standard",
"going_values": [
0
],
"runs": 210,
"wins": 61,
"places": 118,
"win_pct": 29.05,
"place_pct": 56.19
}
],
"by_trap": [
{
"trap": 1,
"runs": 370,
"wins": 89,
"places": 219,
"win_pct": 24.05,
"place_pct": 59.19
}
],
"by_grade": [
{
"grade": "A12",
"runs": 313,
"wins": 49,
"places": 157,
"win_pct": 15.65,
"place_pct": 50.16
}
]
}
}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
Every trainer has tracks they do well at and tracks they turn up to. A yard running 18% overall might be 28% at its home course and 6% everywhere else, and that difference is the single most useful thing you can know about a runner away from home.
This endpoint answers that directly. It splits a trainer's record by track, distance, trap, grade and going over a rolling window, and accepts track_id to narrow every split to one course. Previously the same question meant paging through /v1/trainers/{trainer_id}/results and counting by hand.
The window is bounded deliberately. A busy yard has thousands of runners a year, and trainer form is not a permanent property — a strike rate from four years ago describes a different operation. Twelve months is the default; months widens it to sixty when you want a longer view.
Common uses
Away-day form
Call with track_id set to the course being run to see whether a yard actually travels. It is the fastest way to separate a genuine contender from a runner having a night out.
Tool calls for AI race analysis
An assistant asked "is this trainer in form at Monmore" can answer it in one call with grounded numbers instead of guessing. Small, aggregated and unambiguous is exactly the shape that keeps a model honest.
Model features
Trainer strike rate at track, at distance and at grade are standard features in a runner-level model, and computing them client-side means holding the whole results archive. Here they arrive ready.
Editorial and stable tours
The by_grade and by_distance splits describe what a yard specialises in, which is the substance behind most trainer profiles.
Notes
The window is bounded on purpose: a busy yard has thousands of runners and an unbounded scan would be slow for every caller. Widen it with months, up to 60. As with dog statistics, times appear only in by_distance.