Docs

Using tourdata.golf data

Junior golf tournament schedules, venues and tours, free to use with attribution. This page covers the endpoints, how fresh the data is, what you may do with it, and what every field means.

Quick start

One base URL, no key for schedules.

Base URL: https://www.tourdata.golf/api/v1. Responses are JSON shaped { data, meta }.

Upcoming AJGA events in Georgia
curl "https://www.tourdata.golf/api/v1/tournaments?tour=AJGA&state=GA&from=2026-06-01&limit=50"
EndpointWhat it returns
GET /tournamentsTournament list. Filters: tour, state, gender, status, level, from/to (on date_start), pbe, min_stars, updated_since. Paging: limit (default 100, max 2000), offset. Sorting: sort, order.
GET /tournaments/{id|slug}One tournament by UUID or slug.
GET /tournaments/searchName typeahead: q (2+ chars), gender.
GET /tourstour_code → name mapping with event counts.
GET /coursesCourses: q, state, slug, limit, offset.
GET /courses/{id|slug}One course.
GET /pbe-eventsAJGA Performance Based Entry events and star awards.

The list response includes meta.total, meta.has_more and meta.next_offset. A full upcoming season is roughly 1,000 rows, so page until has_more is false.

Access & limits

Open schedules, keyed partner data.

DataAccess
Tournaments, search, tours, courses, PBE eventsOpen: no key needed
Players, results, leaderboards, rosters/fields, rankings, recruiting, projectionsPartner API key: Authorization: Bearer <key>

Without a key you get 100 requests per day per IP. Keys get a higher daily limit. Every response carries X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset, and an over-limit request returns 429 with Retry-After. A free key is the easiest way to get higher limits and lets us reach you about changes. Ask at [email protected].

Responses are cached at the edge for up to 5 minutes (courses and tours: 1 hour).

Freshness

How often data is refreshed.

WhatWhenNotes
New events, every tourWeekly · WednesdayAdds newly announced events; does not edit existing ones.
Full schedule refresh, every tourMonthly · the 2ndRe-reads every upcoming event: dates, venue, fees, registration.

So a newly announced event appears within about a week, and a change to an existing event (new dates, a fee update) is picked up within about a month. updated_at on a record tells you when its published content last changed. The MCP get_data_freshness tool reports the latest change per tour.

Usage terms

Fully open, with attribution.

Tournament, schedule, course and tour data is free to use, including commercially. You may:

  • store records in your own database and keep them in sync, with no retention limit;
  • show them in your product, and include event details in calendar exports (.ics) and reminder emails;
  • combine them with your own data.

We ask that you:

  • show “Data via tourdata.golf” with a link wherever the data appears. Linking each event's tourdata_url is ideal; the homepage is fine;
  • re-sync at least monthly (weekly is better) so dates and fees don't go stale;
  • send people to official_url to register, and don't present the data as the official record. Organizers can change details at any time;
  • stay within rate limits, or ask for a key.

Player, results and roster data is not covered by these terms and needs a partner agreement.

Incremental sync

Keep a local copy in step.

updated_at moves only when a record's published content changes, not when we re-score its quality. To sync, remember the largest updated_at you have seen and ask for everything since then, oldest first:

Changed since your last sync
GET /api/v1/tournaments?updated_since=2026-10-01T00:00:00Z&sort=updated_at&order=asc&limit=500&offset=0
# follow meta.next_offset until meta.has_more is false,
# then store max(updated_at) as the next checkpoint

Ordering is stable (id breaks ties), so offset paging won't skip rows. Records are never deleted. A cancelled event stays in the feed with status: "cancelled". Run a full pull now and then (monthly) as a safety net.

Field reference

What each field means.

FieldMeaning
idPermanent UUID. Never changes once created. Use it as your foreign key.
slugUnique, human-readable ID (e.g. taylormade-junior-all-star-ca-2026). Also accepted by /tournaments/{id}.
tour_code, external_idtour_code is the tour (see /tours). external_id is the organizer's own event ID, unique within its tour_code when present.
recurring_event_idReserved for linking yearly editions of the same event. Not populated yet.
statusactive · completed · cancelled · postponed. Treat active with a past date_end as completed. Rescheduled events keep active with new dates.
date_start, date_end, registration_opens, registration_closesLocal calendar dates at the venue (YYYY-MM-DD). No times, no time zone.
location_*, address, course_name, course_id, country_codeVenue. course_id links to /courses/{id} when matched.
entry_fee, entry_fee_low, entry_fee_high, is_fee_estimatedWhole currency units as published (USD for US events). null = not published, 0 = free. Low/high give a range when fees vary by division.
gender_typeboys · girls · mixed (both, competing in separate or combined divisions) · null if unknown.
age_divisionsArray of organizer-defined division labels, e.g. ["11 & Under", "12-14", "15-18"]. Not normalized. Use age_low / age_high for the overall eligible range.
competitive_levelElite · National · Regional · Local.
ranked_byComma-separated ranking codes exactly as the organizer lists them, e.g. AJGA(B&G),JGS,TUGR,WAGR. Common codes: AJGA (with (B)/(G)/(B&G) for boys/girls), JGS (Junior Golf Scoreboard), WAGR (World Amateur Golf Ranking), GJGR (Global Junior Golf Rankings). Split on commas and trim.
is_ajga_pbe, pbepbe is the authoritative AJGA Performance Based Entry block (max_stars, per-division awards, has_full_exemption, source_url), or null. is_ajga_pbe is a simple flag that the event is a PBE event; when pbe is present, use it.
website_url, registration_urlThe organizer's event page, and a separate registration link when one is published (rare).
official_urlWhere to send people: registration_url if present, else website_url, else the organizer's event page built from external_id (AJGA, HJGT). null only when none is known.
tourdata_urlPublic tourdata.golf page for the event. Use it for the attribution link.
scoring_diff, signing_diff, diff_*, signee_*Junior Golf Scoreboard scoring differential: score minus course rating. Lower is better; negative means the player beat the rating (rank 1 is about −9.4, roughly +4 is rank 4,000). Not a handicap, so never show a plus sign on positive values. Values are returned signed and unchanged; the differential endpoint also sends meta.sign: "lower-is-better".
created_at, updated_atISO timestamps (UTC). updated_at moves only when published content changes.
AI integration

MCP server for AI assistants.

Connect Claude, Cursor or any MCP client to https://www.tourdata.golf/api/mcp (streamable HTTP, no key). It serves the same tournament records as /api/v1/tournaments, plus courses. It has no player or results data.

ToolDoes
list_toursEvery tour with its code, name and event count. Start here.
search_tournamentsFilter by tour (code), state, dates, age, gender, status.
get_tournamentFull record by id or slug, with PBE stars and course.
search_coursesFind courses by name, city or state.
get_courseCourse details plus recent and upcoming events.
get_data_freshnessLast change per tour and the refresh schedule.
.mcp.json / Claude Desktop config
{
  "mcpServers": {
    "tourdata": { "type": "url", "url": "https://www.tourdata.golf/api/mcp" }
  }
}
Integration FAQ

Questions partners ask.

How often is tournament and schedule data refreshed? Does it vary by tour?
Both, and yes. Every tour's schedule is scanned weekly (Wednesdays) for newly announced events, and every upcoming event is fully re-read monthly (the 2nd) for date, venue, fee and registration changes. See Refresh schedule.
Can we store and periodically sync records, and use them in calendar exports and reminder emails?
Yes. Storing, syncing, calendar exports and reminder emails are all fine. There is no retention limit. We ask that you show the attribution, re-sync at least monthly, and send people to official_url to register. See Usage terms.
What do the sellable_tier values mean? Should they affect display?
That field was an internal data-quality grade and has been removed from the public API, along with the record_*_score fields. Display every record the API returns. Our own pages note that details should always be confirmed with the organizer, and we suggest you do the same.
Should we build against /api/v1/browse or /api/v1/tournaments?
/api/v1/tournaments. That is the canonical endpoint. /api/v1/browse was the name used in older docs and is kept as an alias that returns the same data.
Is venue time-zone information available? Are registration dates local? Are exact deadline times available?
There is no time-zone field. All dates (date_start, date_end, registration_opens, registration_closes) are plain local calendar dates at the venue, with no time component. Exact deadline times aren't collected. Treat a deadline as the end of that local day and derive the zone from location_state / lat-lng if you need one.
Are id values permanent? Is external_id unique within a tour_code? Will recurring_event_id be populated?
id (UUID) is permanent: it never changes once a record is created. slug is also unique and stable. (tour_code, external_id) is unique whenever external_id is present (a small number of manually added events have none). recurring_event_id is not populated today; don't rely on it.
What status values can occur? How are cancellations and postponements represented? Are records ever removed?
active, completed, cancelled, postponed. Cancellations are marked cancelled. A postponed or rescheduled event usually keeps active with new dates after the next refresh. Past events can still read active, so treat active with a date_end in the past as completed. Records are never deleted.
Is there an updated-since filter? What is the default sort order?
Yes: updated_since plus sort=updated_at&order=asc. The default order is date_start descending with id as the tiebreaker. See Incremental sync.
Are fees always in US dollars? How do we tell a free event from an unpublished fee?
Fees are whole numbers as published by the organizer, which is US dollars for US events. A handful of non-US events (country_code other than US) are in the organizer's local currency. null means the fee isn't published; 0 means free. is_fee_estimated flags fees we inferred rather than read.
When is registration_url populated? Should we use website_url?
registration_url is only set when an organizer publishes a separate registration link, which is rare. Use official_url: it is registration_url when present, else website_url, the organizer's official event page.
What can ranked_by contain (WAGR?), how do is_ajga_pbe and pbe relate, and what do gender_type and age_divisions mean?
See ranked_by, pbe, gender_type and age_divisions. In short: ranked_by is a comma-separated list of ranking codes as the organizer publishes them, and it can include WAGR. The pbe block is authoritative for stars. gender_type is boys, girls or mixed. age_divisions are organizer-defined labels.
Is there a mapping between tour names and tour_code?
Yes: GET /api/v1/tours returns code, name, alt_codes and event counts, and the MCP list_tours tool returns the same codes.

Something missing? Email [email protected], or browse the data.