Leagues
A league (an event series in the API) is a season a golfer registers and pays for once. Every event linked to it draws its field from the league roster, including golfers who join after those events were created — so a Thursday-night league is set up in one place rather than event by event.
Everything on this page is public and unauthenticated. Managing a league — its roster, its teams, its linked events — is an admin API and is not part of the public surface.
List an organization's leagues
curl "https://api.front9.com/api/public/v1/orgs/{orgSlug}/event-series"
Returns leagues that are open, active or completed. Draft and private leagues are omitted; members-only leagues appear (registration is gated separately, the same way events behave).
[
{
"id": 7,
"name": "Thursday Night Simulator Scramble",
"slug": "thursday-night-simulator-scramble",
"seasonLabel": "2026 season",
"scheduleLabel": "Thursdays: 7:00-9:30",
"status": "active",
"feeMembersCents": 12000,
"feePerEntry": true,
"entrySize": 2,
"seriesImageUrl": "/media/defaults/event-series/league-1.png",
"memberCount": 20,
"linkedEventCount": 4
}
]
Two fields shape everything else:
entrySize—1is an individual league.2or more makes it a team league: golfers register as a declared pair (or foursome) that stays together all season, and the teams below apply.feePerEntry— whentruethe fee buys a whole entry, so a pair pays once between them and the partner joins free. Whenfalseevery golfer pays.
memberCount is the active roster size and linkedEventCount how many events
the league feeds, so a listing page needs no second round trip. All prices are in
cents.
scheduleLabel is free text for when the league plays, written by the
organizer — "Thursdays: 7:00-9:30". Render it as given: it is never parsed, may
say anything ("alternate Sundays, weather permitting"), and may be absent. The
authoritative dates are on the linked events; this is the one-line answer to
"when is this league?" for a listing or a header.
Filtering
The list takes three optional query parameters, combined with AND:
| Parameter | Meaning |
|---|---|
status | open, active or completed. Repeat it or comma-separate (status=open,active). Any other value is a 400. |
year | Leagues whose season overlaps that calendar year. A November–February league appears under both years. A league with no dates never matches. |
q | Case-insensitive search on name, season label and description. |
curl "https://api.front9.com/api/public/v1/orgs/{orgSlug}/event-series?year=2026&status=open,active&q=couples"
To build a year picker, ask for the years that have leagues:
curl "https://api.front9.com/api/public/v1/orgs/{orgSlug}/event-series/list-metadata"
{
"years": [2027, 2026, 2025],
"oldestYear": 2025,
"newestYear": 2027,
"statusCounts": { "open": 2, "active": 1, "completed": 4 }
}
years is newest first, and each year in it returns at least one league from
?year=. statusCounts omits statuses with no leagues.
Get a single league
curl "https://api.front9.com/api/public/v1/orgs/{orgSlug}/event-series/{slug}"
Same shape, addressed by slug — this is the league's public landing page.
Returns 404 for a league that is private or still in draft, so a not-found and
a not-public league are indistinguishable from outside.
A league's schedule and standings
A league page usually knows one thing: the league's slug, out of its own URL. Both of the things such a page wants to show are addressable that way, so it never has to learn an internal id.
The nights, from the ordinary event list:
curl "https://api.front9.com/api/public/v1/orgs/{orgSlug}/events?filter=all&eventSeries={slug}"
eventSeries matches on the link between an event and its league — the one an
admin makes when adding a night — not on a tag or a naming convention, so the
schedule cannot drift from the league itself. It combines with the other filters
(tag, filter, year), and a slug that names no public league is a 404.
The table:
curl "https://api.front9.com/api/public/v1/orgs/{orgSlug}/event-series/{slug}/standings"
{
"standingSeries": { "id": 5, "name": "Mixed 9-Team League", "scope": "team", "seasons": [] },
"table": { "seriesId": 5, "seriesName": "Mixed 9-Team League", "rows": [] }
}
The league carries the link to its standings table (standingSeriesId), so this
resolves it for you. The series comes back alongside the table because a
season-based league needs its seasons to offer a season picker, and because
standingSeries.id is what every follow-up request uses — a different season
(?seasonId=), or a competitor's contributions — through the ordinary
standings endpoints.
A league with no standings table linked answers 200 with both fields null.
The league is real, it simply hasn't been wired up, and a page can say so rather
than show an error. Set standingSeriesId on the league to connect the two.
Start a registration
As with events and memberships, payment never flows through this API. The start endpoint hands you a hosted URL where Front9 collects the card.
curl -X POST \
"https://api.front9.com/api/public/v1/orgs/{orgSlug}/event-series/{id}/register/start"
{ "url": "https://registration.front9.com/?org=acme&eventSeries=7" }
Redirect the golfer to url to finish registering and pay for the season.
On a team league, pass ?eventSeriesTeam={teamId} to land the golfer on an
existing team rather than starting a new one — this is what a partner following
an invite link needs:
curl -X POST \
"https://api.front9.com/api/public/v1/orgs/{orgSlug}/event-series/{id}/register/start?eventSeriesTeam=12"
The parameter is passed straight through to the hosted page, so forward
org, eventSeries and eventSeriesTeam through your signup flow if the
golfer has to create an account on the way.
Team invite links
On a team league, a registrant names their partners and each is emailed a join link. Before that visitor has an account, you can read what they are joining:
curl "https://api.front9.com/api/public/v1/orgs/{orgSlug}/event-series/{id}/teams/{teamID}"
{
"id": 12,
"name": "Wolfpack",
"memberNames": ["Jamie Lee"],
"entrySize": 4,
"seatsFilled": 1,
"seatsRemaining": 3,
"complete": false
}
Deliberately narrow: names and counts only — never user ids, never the invited email addresses. It is served to whoever holds the link.
Use it to show what the visitor is joining and how many seats are open. The
seats they can fill for other people is seatsRemaining - 1 — their own seat
comes out of the same total.
Partner invites
When the invited golfer already has a Front9 account, their emailed link carries
an inviteToken. Resolve it to render the join page:
curl "https://api.front9.com/api/public/v1/event-series-invites/{token}"
{
"eventSeries": {
"id": 7,
"slug": "thursday-night-simulator-scramble",
"name": "Thursday Night Simulator Scramble",
"orgSlug": "acme",
"orgName": "Acme GC"
},
"team": {
"id": 12,
"name": "Wolfpack",
"memberNames": ["Jamie Lee"],
"entrySize": 4,
"seatsFilled": 1,
"seatsRemaining": 3,
"complete": false
},
"inviter": { "firstName": "Jamie", "lastName": "Lee" },
"email": "partner@example.com",
"feeCents": 12000,
"accepted": false
}
feeCents— what this golfer owes. Unlike an event invite nobody else is covering it: a league partner pays their own season fee. The exception is afeePerEntryleague where a teammate has already bought the entry, in which case it comes back0.accepted— the link outlives the invite. Once the golfer registers it keeps resolving withaccepted: true, so a second click can say "you're already registered" instead of erroring. It stops resolving only if the team is deleted.team— absent if the team has since been deleted.
An invited address with no Front9 account gets no token; their link carries
only org, eventSeries and eventSeriesTeam, and the team endpoint above is
all you need to render it.
404 if the token is unknown.
Sub requests
When a regular on a team league can't make a night, the organizer can post their seat as a sub request: one night, one seat, optionally with a fee the substitute pays, and open to everyone, to the organization's members, or to golfers already playing in one of its leagues. Your site lists the open requests, and a golfer who takes one plays that night in the regular's seat — their result counts for the regular's team.
List every open request across the organization (the "subs needed" page):
curl "https://api.front9.com/api/public/v1/orgs/{orgSlug}/sub-requests"
or just one league's:
curl "https://api.front9.com/api/public/v1/orgs/{orgSlug}/event-series/{slug}/sub-requests"
[
{
"id": 7,
"eventSeriesId": 3,
"eventSeriesName": "Thursday Couples",
"eventSeriesSlug": "thursday-couples",
"teamName": "Wolfpack",
"eventId": 112,
"eventTitle": "Week 4",
"eventSlug": "week-4",
"eventDate": "2026-11-05",
"feeCents": 2000,
"eligibility": "everyone",
"membersOnly": false
}
]
-
feeCents— what the substitute pays.0means free. -
eligibility— who can take it:everyone;org_members— golfers with an active membership in the organization;league_players— golfers on the roster of one of the organization's leagues that is open or in progress (any league, not only this one).
Show it so golfers who don't qualify aren't sent into a flow that will turn them away.
membersOnlyis the older form of the same information:trueexactly wheneligibilityisorg_members. -
eventStartTime— the night's first tee time, present once the tee sheet has one.teamNameandnoteare also omitted when empty.
The regular who is out is never named — the list is public. Only open
requests appear, on leagues that are open or active and not private, for nights
that haven't started. A request drops off the list as soon as someone takes it
or the organizer withdraws it.
To render a page for one request:
curl "https://api.front9.com/api/public/v1/orgs/{orgSlug}/sub-requests/{requestID}"
Same shape. It answers 404 once the request is no longer listed, so a stale
link can say "this spot has been filled" rather than offer a dead button.
Taking a sub request
Taking a request goes through the same hosted registration page as joining the
league. Pass the request's id as subRequest to the league's start endpoint:
curl -X POST \
"https://api.front9.com/api/public/v1/orgs/{orgSlug}/event-series/{eventSeriesId}/register/start?subRequest=7"
{ "url": "https://registration.front9.com/?org=acme&eventSeries=3&subRequest=7" }
Redirect the golfer to url. Front9 signs them in, checks they're eligible,
collects the fee if there is one and puts them in the seat. Only one golfer can
take a given request: if two try at once, the second is told it has been filled
before their card is charged. As with the other parameters, forward
subRequest through your signup flow if the golfer has to create an account
on the way.
Related
Leagues feed the ordinary event endpoints — a league night is a normal event, so its field, tee sheet, leaderboard and live streams are the same endpoints as any other. See Real-time updates and the API reference.