QDG Knowledge Base Read-only viewer QWebHub
general

API Reference

Version 1 · Initial detailed API reference, reconciled against urls.py/views.py/queries.py (adds /guide/ endpoints missing from the older API docx)

Runner Form — API Reference

Base path: /form/ (mounted by config/urls.py). All routes below are relative to that.

A prior Runner_Form_API_Documentation.docx (repo root) documents a shared dev/staging host at http://138.226.220.168:8081/form. It also predates the /guide/ feature below — confirm which host and route set applies before relying on that file.

Authentication

  • POST /form/ with form fields username and password — dev credentials are admin / a hard-coded password checked in views.py (value intentionally not reproduced here — see source, or ask a maintainer). Success sets request.session['is_authenticated'] = True and redirects (302) to /form/runner/. Failure re-renders the login page (200) with "Invalid username or password".
  • GET /form/logout/ flushes the session and redirects to the login page.
  • Every route below except /form/api/search/ requires an authenticated session (redirects 302 to /form/ otherwise).
  • SESSION_ENGINE = signed_cookies — sessions live in a signed browser cookie, no DB session table.

HTML page endpoints

Method Path Auth Description
GET /form/ Public Login page
POST /form/ Public Authenticate
GET /form/logout/ Session Log out
GET /form/runner/ Required Landing page — redirects to WINX's brief form
GET /form/runner/<horse_id>/ Required Runner form page. ?tab=brief|detailed (default brief), ?date=YYYY-MM-DD, ?race=<race_id> pre-select the toolbar
GET /form/jockey/<jockey_id>/ Required Jockey profile + ride history
GET /form/trainer/<trainer_id>/ Required Trainer profile + entry history
GET /form/guide/ Required Form guide — today's (or ?date=) meetings/races, optional ?country=, ?race=<race_id> shows runners inline

FEATURES.md / Runner_Form_Features.docx describe the form guide as a page still to be built. It is implemented today (views.form_guide, template form-guide.html, backed by get_form_guide, get_form_guide_dates, get_form_guide_countries in queries.py) — those planning docs are stale on this point.

JSON API endpoints

Method Path Auth Description
GET /form/api/dates/ Required All available race dates, newest first
GET /form/api/meetings/?date= Required Venues racing on a date
GET /form/api/races/?date=&meeting= Required Races for a date, optionally filtered to one venue
GET /form/api/runners/?race_id= Required Runners nominated in a race
GET /form/api/search/?q= Public Search horses by name (autocomplete). Minimum 3 characters

GET /api/dates/

{ "dates": ["2026-06-25", "2026-06-24", "2026-06-23"] }

GET /api/meetings/?date=2026-06-25

{ "meetings": [ { "meeting_id": "RANDWICK", "meeting_name": "RANDWICK" } ] }

Empty/missing date → { "meetings": [] }.

GET /api/races/?date=2026-06-25&meeting=RANDWICK

{ "races": [
  { "race_id": 4833570, "race_number": 6, "race_name": "Tennant Creek Cup",
    "course_name": "TENNANT CREEK", "distance": "1200", "class_level": "Open" }
] }

GET /api/runners/?race_id=4833570

{ "runners": [
  { "horse_id": 1194902, "tab_number": 3, "horse_name": "EQUAL BALANCE",
    "jockey": "Jane Smith", "trainer": "Tom Brown", "barrier": 4 }
] }

Missing/invalid race_id → { "runners": [] }.

GET /api/search/?q=winx

{ "horses": [ { "id": 989258, "runner_name": "WINX", "country": "AUS" } ],
  "query": "winx", "count": 1 }

Query under 3 characters → { "horses": [], "query": "wi", "min_chars": 3 }.

Template context (page endpoints)

Endpoint Key context variables
/runner/<id>/ horse, runs (≤200, normalised), stats, trial_runs, sales, meeting_races, selected_date, selected_race_id, selected_race_runners, active_tab
/jockey/<id>/ jockey, runs, stats, active_tab
/trainer/<id>/ trainer, runs, stats, active_tab
/guide/ selected_date, selected_country, dates, countries, meetings (each with nested races), selected_race_id/number/name, runners

stats (from compute_stats(runs)): career, by_country, by_jockey, by_trainer, by_distance, by_track, by_class, by_sot, last5, preferred_range, consecutive_wins. See [[database-schema]] for the source columns.

Errors

Scenario Status Body
Not authenticated 302 Redirect to /form/
Horse/jockey/trainer ID not found 200 error.html — "{Entity} {id} not found."
Search query < 3 chars 200 { "horses": [], "query": "..", "min_chars": 3 }
Missing race_id/date on a JSON endpoint 200 Empty array under the relevant key
Sales lookup fails (DB error) 200 Page renders with sales = [] (caught and logged in views.runner_form)

Caching

All JSON endpoints and page data are cached via Django's default in-memory (per-process) cache. Keys are versioned (v2, v3, v7, …) so a query/shape fix can be forced through by bumping the suffix in queries.py instead of flushing the cache server.

Cache key Timeout Data
runner_form:data:v7:{horse_id} 120s Full runner page data (horse, runs, stats, trials)
runner_form:sales:v3:{horse_id} 600s Sales records
runner_form:available_dates 300s /api/dates/
runner_form:meetings:v2:{date} 300s /api/meetings/
runner_form:races:{date}:{track} 300s /api/races/
runner_form:runners:{race_id} 300s /api/runners/
runner_form:horse_search:{q} 180s /api/search/
runner_form:form_guide:v2:{date}:{country} 120s /guide/ meetings + races
runner_form:form_guide_dates:v3 300s /guide/ date tab strip
runner_form:form_guide_countries:v2:{date} 300s /guide/ country filter
runner_form:meeting_details:{date} 120s get_meeting_details() — used by meeting_list.html/meeting_detail.html templates, which exist but aren't currently wired to a URL in urls.py

A condensed version of this table lives in [[quick-reference]].

Updated by Claude on Aug. 12, 2026, 9:43 a.m. · Task: create project documentation (API document)