Your League's Data, In Your Own Tools
Sweeps has a secure, read-only API for pulling your League's Games, Signups, Rounds, and more into a club website's backend, a spreadsheet, or anything else that can make a server-side HTTPS request. (Calls must come from a server, not browser JavaScript — that would expose your key.) You choose exactly which data it shares — per category, per League.
Quickstart
- In the app, open Admin → Settings → API Access.
- Flip on the master switch, then the data categories you want to share.
- Generate an API key (your admin PIN confirms it). The full key is shown exactly once — copy it then.
- Send it with every request:
curl -H "Authorization: Bearer swp_your_key_here" \
https://sweeps.teecrew.app/api/v1/gamesEvery list endpoint returns the same envelope, paged with limit and offset:
{
"data": [ ... ],
"pagination": { "limit": 50, "offset": 0, "hasMore": true }
}Endpoints
All endpoints are read-only GET requests. Each maps to one data category the commissioner can switch on or off independently. Prefer a machine-readable contract? The full OpenAPI 3.1 spec covers every endpoint, parameter, and response schema below — point your client generator or API tooling at it.
GET /api/v1/gamesGamesThe League's Game schedule, newest first.
limit | Rows per page. Default 50, maximum 200. |
offset | Rows to skip. Default 0. |
status | Filter by Game status: scheduled, in_progress, completed, finalized, or cancelled. |
Fields: id, name, date, status, scoringFormat, courseId
GET /api/v1/signupsSignupsWho is currently signed up for each Game (withdrawn signups are excluded).
limit | Rows per page. Default 50, maximum 200. |
offset | Rows to skip. Default 0. |
game_id | Only signups for this Game (integer id). |
Fields: id, gameId (null if the Game record isn't linked yet or the Game was cancelled), playerId, playerName, paymentStatus (null until a payment record exists), inSweepsGame, signedUpAt
GET /api/v1/roundsRoundsEach player's score record per Game, newest first.
limit | Rows per page. Default 50, maximum 200. |
offset | Rows to skip. Default 0. |
game_id | Only Rounds for this Game (integer id). |
include_holes | Set to true to attach hole-by-hole detail to each Round. |
Fields: id, gameId, playerId, playerName, grossTotal, netTotal, stablefordPoints, status, date — plus holes[] (holeNumber, grossScore, netScore, stablefordPoints, notPlayed) when include_holes=true
GET /api/v1/seriesSeriesThe League's Series and their date ranges.
limit | Rows per page. Default 50, maximum 200. |
offset | Rows to skip. Default 0. |
Fields: id, name, year, startDate, endDate, closedAt
GET /api/v1/playersPlayersLeague roster with handicaps. Names and handicaps only — never phone numbers or emails.
limit | Rows per page. Default 50, maximum 200. |
offset | Rows to skip. Default 0. |
Fields: membershipId, playerId, playerName, handicap, handicapCalculated, handicapStatus, role, isActive
GET /api/v1/paymentsPaymentsAmounts and paid status for Game and Series fees, as two lists: gamePayments and seriesPayments. The one endpoint with a different envelope: data holds the two lists, and pagination reports hasMore per list — { limit, offset, gamePayments: { hasMore }, seriesPayments: { hasMore } }.
limit | Rows per page. Default 50, maximum 200. |
offset | Rows to skip. Default 0. |
game_id | Only gamePayments for this Game (integer id). |
Fields: gamePayments: id, gameId, playerId, amount, status, paidAt · seriesPayments: id, seasonYear, playerId, amount, status, paidAt
GET /api/v1/audit-logsActivity LogThe League's admin activity log, newest first.
limit | Rows per page. Default 50, maximum 200. |
offset | Rows to skip. Default 0. |
Fields: id, actionType, actionCategory, actorRole, targetEntityType, targetEntityId, createdAt
Errors and limits
Errors come back as { "error": { "code", "message" } } with a matching HTTP status:
| Status | Code | Meaning |
|---|---|---|
| 400 | VALIDATION_ERROR | A query parameter is malformed (for example game_id=abc). |
| 401 | UNAUTHORIZED | Missing, malformed, unknown, or revoked API key. |
| 403 | FORBIDDEN | API access is off for the League, or this category isn't enabled. |
| 429 | RATE_LIMITED | Too many requests — honor the Retry-After header and retry. |
| 500 | INTERNAL_ERROR | Something failed on our side — safe to retry with backoff. |
Rate limits: 60 requests per minute per key, plus a shared platform ceiling — so an occasional 429 under 60/min is possible; back off per the Retry-After header. Repeated requests with invalid keys are additionally limited per source IP. Responses are never cached across Leagues.
How it's secured
- Keys are hashed.Sweeps stores only a SHA-256 hash of your key — it can't be recovered later, by anyone. Lose it and you revoke it and generate a new one.
- Everything is off by default. The API rejects every request until a commissioner turns it on, and each data category is a separate switch.
- Keys see one League only. A key is bound to the League that created it, every query is filtered to that League, and database-level row isolation backs that up wherever the underlying table supports it.
- Read-only by construction. There are no write endpoints — nothing an integration can break.
- No personal contact data. Player endpoints return names and handicaps, never phone numbers or emails.
- PIN-confirmed management, audited. Creating or revoking a key and changing permissions all require the admin PIN and land in the League's activity log. Revocation takes effect immediately.
Ready to try it? Open Admin → Settings → API Access in the app, or see everything else Sweeps does on the features page.