Course data · v1 beta

Golf Course
Data API.

Read normalized course data and submit licensed facility, layout, scorecard, and tee corrections for independent review.

Authentication

License the smallest useful scope.

Every request requires course-data:read. Private keys are intended for servers and are rejected when a browser sends an Origin header. Publishable keys work only when Course Data has been separately enabled for browser use and the request origin exactly matches the key allowlist.

Send credentials using x-api-key or Authorization: Bearer …. Keys in URLs are rejected.

curl "https://caddie100.com/api/v1/courses/COURSE_ID" \
  -H "x-api-key: c100_live_YOUR_PRIVATE_KEY"

Endpoint

Read a course summary.

GET/api/v1/courses/{courseId}

Returns the canonical ID, course name, city, region, country, longitude, latitude, hole count, per-hole geometry availability, and available course services. Coordinates use WGS 84 decimal degrees.

const response = await fetch(
  "https://caddie100.com/api/v1/courses/COURSE_ID",
  { headers: { "x-api-key": "c100_pk_live_YOUR_PUBLISHABLE_KEY" } }
);
const course = await response.json();

Browser examples require a c100_pk_… publishable key. Never substitute a private c100_live_… or c100_test_… key.

Example response

{
  "id": "34697689-6423-4c2e-a2c4-b0ecbbfc77d2",
  "name": "Raymond Memorial Golf Course",
  "location": {
    "city": "Columbus",
    "region": "OH",
    "country": "US",
    "longitude": -83.097579,
    "latitude": 39.991513
  },
  "holeCount": 18,
  "holes": [
    { "number": 1, "available": true },
    { "number": 2, "available": true }
  ],
  "services": {
    "graphics": { "available": true, "delivery": "generated-on-demand" },
    "courseMap": { "available": false, "delivery": "pre-rendered-pmtiles" },
    "staticMapImages": { "available": false, "delivery": "pre-rendered-pmtiles" }
  }
}

Graphics are generated on demand for active courses with usable geometry. Course maps and static map images are available only after a PMTiles archive has been pre-rendered and published for the course.

Dataset delivery

Request only the course data a page needs.

GET/api/v1/courses/{courseId}/data?include=…&holes=…

Returns named factual, scorecard, hole-strategy, measurement, geometry, elevation, media, and provenance datasets. The optional holes filter accepts one or more comma-separated hole numbers.

Private server keys onlyThis endpoint is intended for partner server-side rendering. Use a c100_live_… or c100_test_… private key. Publishable c100_pk_… keys and requests carrying a browser Origin are rejected.
curl "https://caddie100.com/api/v1/courses/COURSE_ID/data?include=identity,scorecard,holes.strategy,measurements.greens,measurements.fairways,geometry.fairways&holes=9" \
  -H "x-api-key: c100_live_YOUR_PRIVATE_KEY"

Dataset permissions

Every Course Data client starts with identity. Every other dataset keyword is disabled until CADDIE.100 staff grants it at the client level. Dataset grants apply to all of the client's Course Data keys and cannot be selected during client-side key creation or rotation.

An unauthorized requested property remains in data with error: true and code: "dataset-permission-denied". Check meta.deniedDatasets; denied sources are not loaded.

Dataset keywords

Core datasets are identity, contact, amenities, operations, scorecard, holes.summary, holes.detail, holes.strategy, greens.summary, greens.analysis, measurements.greens, measurements.fairways, elevations.ground, elevations.trees, media.images, media.videos, and provenance.

measurements.greens provides per-hole green area, depth, width, status, and measured geometry. measurements.fairways provides measured fairway area and width stations for applicable par-4 and par-5 holes. holes.strategy provides tee-shot landing widths, water intersections, and approach metrics. Validated courses are served from the revisioned CADDIE.100 canonical geometry catalog; courses not yet approved for canonical serving retain compatibility behavior until their measurement revisions pass validation. All three honor the holes filter; use measurements to request both measurement datasets.

Geometry layers are requested as geometry.course-boundary, geometry.hole-boundaries, geometry.fairways, geometry.greens, geometry.tees, geometry.centerlines, geometry.bunkers, geometry.water, geometry.waste, geometry.cart-paths, geometry.trees, geometry.buildings, geometry.roads, geometry.parking, or geometry.land-surfaces. Use geometry to expand every registered geometry layer.

Recognized datasets that do not yet have a stable source remain present in data with a todo message. Check meta.complete and meta.todoDatasets.

Filtered response

{
  "courseId": "COURSE_ID",
  "revision": "sha256:…",
  "data": {
    "scorecard": {
      "holes": [{ "number": 9, "par": 4, "mensHandicap": 11 }],
      "teeSets": [{ "name": "Blue", "yardages": [{ "hole": 9, "yards": 385 }] }]
    },
    "holes": {
      "strategy": { "schemaVersion": 1, "holes": [{ "number": 9, "par": 4, "teeShots": [], "approaches": {} }] }
    },
    "measurements": {
      "greens": { "holes": [{ "number": 9, "status": "complete", "m2": 500, "ft2": 5382, "measurementVersion": 2 }] },
      "fairways": { "measurementVersion": 2, "holes": [{ "number": 9, "par": 4, "m2": 4200, "widths": {} }] }
    },
    "geometry": {
      "fairways": { "type": "FeatureCollection", "featureCount": 1, "features": [{ "type": "Feature", "geometry": { "type": "Polygon", "coordinates": [] }, "properties": { "holeNumbers": [9] } }] }
    },
    "elevations": {
      "ground": { "type": "PointSet", "unit": "meters", "pointCount": 1, "sourcePointCount": 1840, "points": [[-73.4388, 40.7531, 31.4]] }
    }
  },
  "meta": {
    "complete": true,
    "requestedHoles": [9],
    "resolvedHoles": [9],
    "todoDatasets": [],
    "deniedDatasets": []
  }
}

Successful responses expose database, source-fetch, serialization, and total durations through the standard Server-Timing header. x-caddie100-response-bytes reports the uncompressed JSON size.

Partner corrections

Submit bounded changes for review.

Licensed partners can request the private course-data:submit scope plus exact facility/layout grants or an explicit organization * grant for all-course access. The wildcard is assigned by CADDIE.100 staff; clients do not send * in a key scope or proposal. It authorizes every supported section for every course, while no resource grants means no course access. Submission access is independent from read-dataset permissions and is never available to publishable keys or browser-origin requests.

GET/api/v1/courses/{courseId}/change-context

Returns granted writable fields, opaque current version tokens, and every layout affected by a shared facility change.

POST/api/v1/courses/{courseId}/change-requests

Creates one atomic submitted proposal. CADDIE.100 staff must independently approve it before canonical data changes.

GET/api/v1/course-change-requests/{requestId}

Using a private key with course-data:submit, poll an organization-owned request for submitted, in-review, approved, or rejected status and reviewer notes.

No geometry or hole-count changesThe first version accepts facility information, layout name/sub-name, scorecard arrays, and complete tee rows. The * grant does not override this boundary: geometry, hole count, active state, classification, internal metadata, and source assertions are rejected.

Safe retry sequence

Read change context immediately before composing a proposal. Persist one random Idempotency-Key with the logical change and reuse it after timeouts or 503 responses. An identical retry returns the same request; using that key with different normalized content returns 409 idempotency-key-conflict. A 409 course-change-base-stale means another revision was approved—read context again, re-evaluate the correction, and submit with a new key.

curl -X POST "https://caddie100.com/api/v1/courses/COURSE_ID/change-requests" \
  -H "Authorization: Bearer c100_live_YOUR_PRIVATE_KEY" \
  -H "Idempotency-Key: ptc-change-1842" \
  -H "Content-Type: application/json" \
  --data '{
  "schema": "caddie100-course-change-proposal@1",
  "title": "Update the official scorecard",
  "rationale": "Verified against the current facility scorecard.",
  "baseVersions": {
    "scorecard": "TOKEN_FROM_CHANGE_CONTEXT"
  },
  "changes": {
    "scorecard": {
      "parByHole": [
        4,
        4,
        3,
        5,
        4,
        4,
        3,
        4,
        5,
        4,
        4,
        3,
        5,
        4,
        4,
        3,
        4,
        5
      ]
    }
  },
  "evidence": {
    "kind": "official-scorecard",
    "sourceUrl": "https://example.com/scorecard",
    "clientReference": "ptc-change-1842"
  }
}'

Request bodies are limited to one megabyte and reject unknown fields. Never place API keys, authorization headers, raw idempotency keys, or unnecessary personal data in evidence, logs, or support tickets. Poll the returned status URL; webhooks and partner withdrawal are not part of this version.

Discovery

Find nearby courses.

GET/api/v1/courses/{longitude}/{latitude}

Returns up to 10 active courses with a current geometry record and valid coordinates, ordered nearest-first. Courses farther than 10 miles from the supplied WGS 84 point are omitted. Each result includes its distance in miles.

curl "https://caddie100.com/api/v1/courses/-83.097579/39.991513" \
  -H "x-api-key: c100_live_YOUR_PRIVATE_KEY"

Directory

Download course IDs.

GET/api/v1/courses/bulk-ids

Returns every active course with a current geometry record as a compact record containing only its CADDIE.100 ID, name, and location. Missing longitude or latitude values remain null instead of removing the course. Use this dataset to map an existing product's course identifiers to CADDIE.100 IDs.

curl "https://caddie100.com/api/v1/courses/bulk-ids" \
  -H "x-api-key: c100_live_YOUR_PRIVATE_KEY" \
  --output caddie100-course-ids.json

Operational contract

Shared limits and usage.

Successful licensed requests include rate-limit headers. Limits are consumed atomically across all CADDIE.100 application instances, and authorized requests are metered to the owning organization. A 429 response includes Retry-After; a temporary metering or limiter failure fails closed with 503.