APIHorseRacing

Documentation / Racecards & meetings

Today's cards

GET /v1/racecards/today

Every meeting and race today, grouped by course.

Available on

Free key 24h – 7d
Live today + 3 days
Archive back to 2017
Complete 2017 → upcoming
Complete + Analyst 2017 → upcoming

Send your key in the X-API-Key header, or as ?key= if your tool cannot set one. A date outside your plan's window returns 403 outside_window naming the window, never an empty array.

Parameters

None. This endpoint takes nothing beyond your key.

Request

curl https://api.apihorseracing.com/v1/racecards/today \
  -H "X-API-Key: $AHR_KEY"

Response

200 OK application/json harvested 8 days ago
{
    "meta": {
        "request_id": "574b1a1ca58eadd7",
        "data_as_of": "2026-09-12T05:10:27+00:00",
        "plan": "analyst",
        "window": "2017 → upcoming",
        "date": "2026-09-13",
        "meetings": 0,
        "count": 0
    },
    "data": []
}

A real response from /v1/racecards/today, captured by calling the live API rather than written by hand.

Errors this endpoint returns

CodeStatusWhen
unauthorized 401 No key, or one we do not recognise. Check the header name and that the key has not been rotated from your account page.
outside_window 403 You asked for a date your plan cannot see. The body names the window and the date you asked for. Search endpoints clamp instead of refusing, and report what they actually searched in meta.
upgrade_required 403 The endpoint returns computed statistics and your plan does not include them. The body names every plan that does reach it, so you never have to look it up.
rate_limited 429 Past your per-minute rate. Retry-After is set, and X-RateLimit-Remaining tells you where you stand on every successful response too.
server_error 500 Ours, not yours. Every response carries a request_id; quote it and we can find the exact request.

Run it live

A real request from your browser straight to api.apihorseracing.com with your own key. Nothing is proxied and nothing is logged by this page.

Remembered in this browser so you do not paste it on every page.

What this is for

Every meeting and race carded today, grouped by course, in the order they are due off. This is the first call most consumers make and the one they make most often.

A meeting is one course on one day, and races arrive nested inside it because that is how a card is read. Nobody wants a flat list of forty races across nine tracks to group themselves.

The times are local to the course, deliberately. A 14:35 at Musselburgh and a 14:35 at Meydan are both the time on the racecourse clock, which is what a card shows and what your users expect. Everything else in the API is UTC.

Common uses

A homepage or an app card list

One call gives you the whole day. Cache it for a few minutes rather than per race; declarations change, but not by the second.

Deciding what to poll

Take the race identifiers from here, then follow individual races as they run. Polling this endpoint repeatedly to detect a result is the wrong shape; a webhook on result.received costs nothing against your quota.

Filling a schedule ahead of time

Use /v1/racecards/upcoming for tomorrow onwards. This endpoint is today only, and returns an empty list rather than yesterday if nothing is carded.

Notes

Needs a plan with a live window. A free key reads a delayed window and gets outside_window here, which is honest: it is the one endpoint a free key genuinely cannot serve. While the backfill is still running this may be empty even on a paid plan; /v1/meta/coverage tells you how far the archive has reached.

Related in Racecards & meetings