PinSeek Developer contact
Developer documentation

PinSeek connector API

A per-user HTTPS API for finding golf tee times and managing the user's PinSeek alerts. PinSeek provides links to course booking sites; it never makes or pays for a reservation. This page describes the API used by PinSeek's connector CLIs and the current authentication flow.

Base URL and access

Base URL: https://app.pinseek.io. JSON endpoints are under /api/v1 and require Authorization: Bearer <access_token>.

A PinSeek account is required. Tee-time search and course lookup do not require a paid membership. Creating or resuming alerts requires a Pro membership; the API returns membership_required with an account URL otherwise. Alerts are sent through the user's configured PinSeek channels.

The public pinseek-cli client ID is bundled with the local agent guide. Other client IDs are provisioned by PinSeek; for one, contact PinSeek support. The API does not accept an API key or OAuth authorization-code/PKCE login at this time.

OAuth 2.0 device authorization

PinSeek uses the OAuth 2.0 Device Authorization Grant (RFC 8628), per user. Request teetimes.read for searches and reading alerts; request rules.write for creating, changing, pausing, resuming, or deleting alerts.

  1. POST /oauth/device/code with form fields client_id and scope (space-separated). The response supplies device_code, user_code, verification_uri_complete, expires_in, and interval.
  2. Show the verification URL and code to the user. The user signs in to PinSeek and approves the requested access in their own browser. The code expires after 10 minutes.
  3. Poll POST /oauth/token with form fields grant_type=urn:ietf:params:oauth:grant-type:device_code, client_id, and device_code, respecting the returned interval.
  4. Refresh via POST /oauth/token with grant_type=refresh_token and refresh_token. Revoke with POST /oauth/revoke and form field token.

Keep tokens out of URLs, logs, and chat messages. Tokens are scoped to the approving PinSeek account; access tokens expire after one hour and refresh tokens after 180 days unless revoked sooner.

Read endpoints

All endpoints below require teetimes.read, except /me, which accepts either scope.

  • GET /api/v1/me — account plan, active-alert limits, permitted channels, and a membership URL when applicable.
  • GET /api/v1/courses — find courses with q, state, or lat and lng. Optional radius_miles and limit.
  • GET /api/v1/teetimes — repeat course for up to 10 course keys; supply date (YYYY-MM-DD) and players (1–4). Optional holes (0, 9, 18), time_start, time_end (24-hour course-local HH:MM), max_price (USD per player), and budget_ms (1000–15000). Dates run from the course's today through eight days ahead.
  • GET /api/v1/courses/{key}/forecast — a course's historical availability pattern.
  • GET /api/v1/deeplink — supply course, date, and optional players for a course booking URL; this does not check availability.
  • GET /api/v1/rules and GET /api/v1/rules/{id}/matches — the user's alerts and observed matches.

Alert management

These endpoints require rules.write. Confirm the user's intended course, dates, time window, party size, and price cap before creating, updating, or deleting an alert. Do not replay an uncertain write without first reading the current rule state.

  • POST /api/v1/rules — JSON body with course_keys, date_start, date_end, time_start, time_end, and players. Optional name, holes, max_price, and alert_channels. The date window is at most three days.
  • PATCH /api/v1/rules/{id} — update only supplied fields. Alerts with weekly schedules, groups, pursuits, or unsupported channels must be edited in PinSeek.
  • POST /api/v1/rules/{id}/pause, POST /api/v1/rules/{id}/resume, and DELETE /api/v1/rules/{id}.

Illustrative body: replace the date placeholder with a date in the supported window.

{
  "course_keys": ["teeitup:10164"],
  "date_start": "YYYY-MM-DD",
  "date_end": "YYYY-MM-DD",
  "time_start": "12:00",
  "time_end": "18:00",
  "players": 4,
  "max_price": 60,
  "alert_channels": ["email"]
}

Response and error semantics

The API returns JSON directly. Tee-time search returns as_of, results[], providers[], and truncated. Each result includes course name/key, tee_local, tee_utc, players_available, price, holes, deeplink, source (live or cached), and observed_at.

A cached result reflects the last scan, not a current opening. The provider entry explains whether a search was live or fell back to a scan. A missing price is unknown, not free, and may pass a price filter. A deeplink opens a booking site but does not reserve anything. Availability can change after either a live check or a scan.

Errors are JSON with error and message; some include fields, action_url, or retry_after. Relevant statuses include 401 invalid_token, 403 insufficient_scope, 402 membership_required, 409 duplicate_rule, 422 invalid_request, and 429 rate_limited. Respect Retry-After where present.