Skip to content
Developer docsREST · JSON and PNG · /v1

Overview

Build on UK and Irish racing data.

A versioned REST API under /v1 for UK and Ireland only, through the combined region=uk feed. Racecards, results, runner connections, racecourses, weather forecasts and odds return JSON; silks return PNG images.

Download the customer API guide (PDF)

Production gateway: https://racingapi.net. For example, https://racingapi.net/v1/racecards?region=uk. In a development environment, use the gateway URL returned by GET /api/platform in apiBaseUrl. Do not add an extra /api prefix to the racing endpoints.

Authentication

Create keys in Dashboard, API keys. The secret is shown once; store it server-side in an environment variable such as RACINGAPI_KEY. Send it with every request using either header:

Authorization: Bearer $RACINGAPI_KEY
  • Never place keys in URLs, query strings or browser code.
  • Revoke a leaked key immediately from the dashboard and create a new one.
  • These public docs never ask for or store your key. Real calls belong in your server or the signed-in playground.

Subscriptions and API access

Signing up and creating a key do not grant feed access. Choose a monthly plan in Subscriptions and complete Stripe Checkout with a card. Eligible new customers receive one seven-day trial, followed by automatic monthly renewal unless cancelled. API access begins only after server verification of your subscription. Suspended accounts, expired trials and overdue or cancelled subscriptions cannot access the feeds. Explicit administrator-approved access remains valid until its expiry. In Stripe test mode, use test cards only; no real payments are taken.

Rate limits

Limits apply per customer account across all of its keys. Monthly quotas reset on the UTC calendar month.

Enterprise is £99.99 per month with unlimited monthly requests and live support. Its 10-requests-per-second rate limit still applies. No VAT is added. Monthly quotas reset on the first day of each calendar month in UTC, not the subscription anniversary; changing plans does not reset usage. Account and usage JSON represent an unlimited monthly allowance with quota: -1; the monthly limit and remaining response headers contain unlimited.

  • Successful responses include X-RateLimit-Limit, X-RateLimit-Monthly-Limit and X-RateLimit-Monthly-Remaining.
  • There is no per-second remaining or reset header.
  • HTTP 429 includes Retry-After in seconds. Per-second and monthly exhaustion both use quota_exceeded; the code alone cannot distinguish them. Track monthly remaining on successful calls and inspect Retry-After rather than assuming every 429 lasts one second.
  • Requests are charged after key, access, endpoint-activation and initial query validation. Later missing-resource or source failures still count. Rejected authentication, inactive access and disabled-endpoint requests do not consume the approved allowance. HEAD requests also count; use GET to retrieve the data.

Filters and pagination

ParameterBehaviour
regionOnly uk is accepted (also the default). This combined feed includes UK and Ireland. Other regional codes return HTTP 422.
dateYYYY-MM-DD. Omit for the current feed. Historical requests use the same combined UK and Ireland feed; completeness is not guaranteed.
tomorrowtrue selects tomorrow's combined UK and Ireland feed. Cannot be combined with date.
pageTop-level list page, 1–100,000, default 1.
limitItems per page, 1 to 100, default 50.

meta.total is the unpaginated count of matches. Keep requesting while page * limit < meta.total, respecting your per-second limit between pages.

Errors and retries

Errors return JSON with error, code and requestId. Quote the request ID when contacting support.

{
  "error": "Request quota exceeded",
  "code": "quota_exceeded",
  "requestId": "req_example_not_live"
}

Illustrative body. Exact codes per endpoint are in the API reference.

StatusMeaningRetry?
400Malformed requestNo, fix the request
401Missing, invalid or revoked keyNo
403Subscription required, suspended account or playground key-owner mismatchCheck Billing/access and key ownership; do not repeatedly retry
404Not found in the selected regional daily feedNo
405Only GET and HEAD are acceptedNo
422Unsupported region, invalid date or other invalid filtersNo
429Per-second limit or monthly quota exceededYes, after Retry-After
500 / 503Internal error, inactive endpoint or source unavailableOnly transient failures; use bounded backoff

Coverage

Region availability is read live from the platform. UK historical samples reach back to 2016, but a continuous, complete archive has not been verified.

Full detail on the Coverage page.

Field semantics

  • JSON responses contain meta and data. List endpoints return an array; ID endpoints return one object. Empty schedules return an empty array.
  • meta.retrievedAt is the gateway retrieval time. Source data may be cached for up to 30 seconds; this is not a freshness guarantee.
  • Times, weights and price strings preserve the source values. No timezone, weight-unit or odds-format conversion is applied.
  • Missing source strings stay empty strings; absent collections stay empty arrays.
  • IDs are resolved within the selected daily feed. Use the same date or tomorrow filter for subsequent race, horse, connection, odds and silk lookups.
  • Jockey and trainer endpoints return daily bookings in that feed (horseId, horseName, raceId, name, connections). They are not runner records or career statistics.
  • Horse Form Statistics supplies every available summary section for a declared horse: race record, conditions, headgear, class rating and connections where present. Discover IDs in the same daily feed and select raceId for declaration-specific context. See the field-level reference and readable tables in the playground. Coverage varies by horse; not a complete race-by-race career archive.
  • Upstream failures return a classified 503 rather than invented empty data.

Silks

GET /v1/silks/{horseId}?raceId=RACE_ID&region=uk returns binary image/png, not JSON. Take both IDs from a racecard for the same region and date, and pass the same date or tomorrow=true. A missing source image or undeclared runner returns 404 with no substitute; an inactive image feed returns 503. Error statuses return the standard JSON error body.

Build a racing-data integration

Start with GET /v1/racecards?region=uk. Each item in data is a race, not a meeting. Its runners array contains the declared horses and their connection IDs. Treat all IDs as strings and carry the same date context into every related request.

What you needWhere to get the IDNext request
Race detailsrace.id/v1/races/{id}
Published resultrace.id/v1/results/{raceId}
Horse profile / formrunner.id/v1/horses/{id} or /v1/horses/{id}/form
Jockey bookingsrunner.jockey.id/v1/jockeys/{id}
Trainer bookingsrunner.trainer.id/v1/trainers/{id}
Course and its racesrace.course.id/v1/racecourses/{id}
Course weather forecastrace.course.id/v1/racecourses/{id}/weather
Bookmaker oddsrace.id/v1/odds/{raceId}
Runner silkrunner.id + race.id/v1/silks/{id}?raceId=RACE_ID

Add region=uk to each request. For dated racecards, also add the same date=YYYY-MM-DD; for tomorrow's cards, add tomorrow=true instead. A resource ID on its own does not select a historical day.

For example, fetch /v1/racecards?region=uk&date=2026-10-26, read a real race ID from that response, then request /v1/results/{raceId}?region=uk&date=2026-10-26. The date is illustrative; always select the day your application needs.

  • Horse search uses /v1/horses/search?q=NAME: case-insensitive substring matching of declared horse names in the selected daily feed. It is not a separate all-time horse directory.
  • Join odds to runners using odds.data.runners[].horseId and race.runners[].id, not array position. Bookmaker names and IDs are in each runner's odds[].
  • Results use raceId rather than id. A race appears in the results feed once finishers are supplied. An empty results list does not mean a scheduled race has been cancelled.
  • Jockey/trainer bookings and connection statistics are scoped to the selected daily feed. Do not add horse-specific statistics together to manufacture career totals.

Racecourse weather

On every REST plan, request GET /v1/racecourses/{id}/weather?region=uk with your API key. Discover the course ID from race.course.id or /v1/racecourses, carrying the same date or tomorrow=true selector. These selectors locate the course in the racing feed; they do not retrieve past weather. Enterprise AI clients can use get_racecourse_weather.

The response contains data.course with verified venue coordinates, data.type: "forecast", data.timezone: "UTC" and an unpaginated data.forecast[]. These are MET Norway local forecasts, not observations from an on-track station. Official track conditions remain the separate going field in racecards and course details.

Field in each forecast pointMeaning
timeUTC ISO timestamp; convert to Europe/London or Europe/Dublin for display. Do not treat local race-time strings as UTC.
temperatureC / humidityPercentAir temperature in °C and relative humidity in percent at that time.
windSpeedMps / windFromDegreesMetres per second; multiply by 2.23694 for mph. Wind direction is where it comes from: 0° north, 90° east.
precipitationMm / precipitationPeriodHoursRainfall total for the NEXT 1, 6 or 12 hours after time. Never add overlapping periods or assume every point is hourly. Null means not supplied, not dry.
symbolCodeSource weather symbol such as partlycloudy_day; null when absent. Icons, if used, have their own licence.

For race-time weather, match the scheduled instant to a forecast at or before it, within the interval to the next point. Do not extrapolate beyond the returned horizon. Forecast spacing becomes wider later in the outlook, which can extend roughly nine days. Refresh according to meta.expiresAt, not the racing feed's 30-second cache. meta.updatedAt is the source forecast update time; meta.fetchedAt is when we downloaded that body. API calls still consume your normal allowance, including cached responses.

Display meta.source.attribution with links from meta.source.url and meta.source.licenceUrl: “Weather data from MET Norway. Reformatted by RacingAPI.net.” Weather uses CC BY 4.0; retain that credit in your own weather display. Venue coordinates are checked against Wikipedia/Wikidata, GeoNames and linked venue-location references.

A course missing from the selected feed returns 404. An unrecognised course location returns 422 weather_location_unmapped; weather source interruptions return 503 weather_unavailable or weather_format_error. No substitute town or fabricated weather is used. Source availability is not guaranteed.

Display Horse Form Statistics

Use the same UK and Irish daily feed for discovery and form. Page through racecards or /v1/horses/search?q=NAME using meta.total. Then request /v1/horses/{id}/form?region=uk&raceId=RACE_ID, carrying the same date or tomorrow selection. Without raceId, the first declaration in feed order is selected; the response identifies it and lists matching declarations.

Group every data.form row by section and display name, runs, wins, places and winPercentage. Keep their original strings: "" means not supplied, while "0" is a supplied zero. Use text rendering rather than inserting API values as HTML. Label the view “Supplied summary statistics”, identify the horse, race, course and feed date, and show “No form supplied” for form: []. The playground provides these readable tables alongside JSON and a copyable request.

Sections can include race record, conditions, headgear, class rating and connections where supplied. Render unknown section names too, rather than maintaining a fixed list. Availability varies by declared horse. These summaries are not verified race-by-race past performances or a complete career archive.

Polling, caching and pagination

The racing feeds use HTTP polling, not a racing WebSocket or push-webhook subscription. Source responses may be cached for 30 seconds. Repeating the same call more often can consume allowance without returning newer data. This cache duration is not a promise that source odds or results update every 30 seconds.

  • Cache and share responses in your own backend instead of making one feed request per visitor.
  • Coordinate workers and API keys against the same account-wide limit: Developer 2/second, Professional 5/second, Enterprise 10/second. Extra keys do not create extra allowance.
  • Only top-level list responses are paginated. limit does not limit the runners inside a race; ID responses remain a single object. Use meta.total to decide when the list is complete.
  • Pagination is not a frozen snapshot. A daily feed may change between pages; deduplicate using resource IDs and refresh your stored view when polling.
  • Use a request timeout and bounded retries. Respect Retry-After; a monthly-quota wait can be much longer than a per-second wait. See the tested server-side retry example.

Display silks safely

Fetch the silk on your server using your RacingAPI.net key. On a successful response, forward the PNG through an appropriately protected image route on your own application. The browser can then display your own image URL. An ordinary <img src="…"> cannot attach your API-key header, and putting the key in an image URL would expose it.

Check the HTTP status and Content-Type before storing or forwarding a response. A successful image is image/png; an unsuccessful request has a JSON body. Cache using the horse ID, race ID and date context together, without logging the key or making it part of a public URL.

Before going live

Integration checklist

  • Keep the key in server-side secrets; verify active billing and generate a separate named key for each application.
  • Exercise racecards, a related detail request and a dated lookup with IDs returned by your own requests, not the illustrative IDs in these docs.
  • Handle empty arrays, empty source strings and unknown added fields without replacing them with invented values.
  • Check per-second scheduling, monthly allowance, request timeouts and bounded retry behaviour across all workers.
  • Confirm that logs omit API keys and retain the requestId from unsuccessful API calls for support.
  • Test key rotation and update your application's secret promptly: the old key stops working immediately.

Compatibility and changes

Import the downloadable OpenAPI document into Postman, Insomnia or your client generator. It includes the REST racing feeds and Enterprise MCP protocol, not the site's login, billing or administrator APIs. REST uses GET under /v1; MCP uses POST at /api/mcp and has its own setup guide. Set the X-API-Key or Bearer authentication value to your RacingAPI.net key for REST; browser session tokens and MCP OAuth tokens are not REST feed keys. The document's servers URL identifies the environment you downloaded it from.

  • The major version is in the path. Treat the /v1 contract at /api/openapi.json as the source of truth.
  • Tolerate unknown fields and new enum values; do not rely on property order.
  • Pin client generation to a downloaded contract and diff it before regenerating.
  • Notable changes are announced on the changelog.

No implied guarantees

Example values across these docs are illustrative. Coverage depth, freshness and availability are described as observed, not as service-level commitments.