Golf maps service 02 · v1 beta

Golf Course
Maps.

Choose Map Tiles for interactive mapping, request one bounded Static Image, or stream a published PMTiles course archive.

Tile-first course maps

Map data shaped for map tools.

Golf Course Maps preserves a fixed, north-up Web Mercator coordinate system across Map Tiles, PMTiles archives, and Static Images. It fits interactive GPS products, course explorers, operations tools, and other interfaces where your application owns the viewport.

Maps and Graphics share measured course geometry and cartographic foundations, but their output contracts stay separate. Golf Course Graphics fits and rotates a hole, green, or course into a configurable finished composition.

Delivery surfaces

Plan, tile, bound, or package.

GET/api/v1/courses/{courseId}/maps/tiles

Returns coverage, regions, the current course revision, a revision-pinned XYZ template, static-map link, and PMTiles publication status.

GET/api/v1/courses/{courseId}/maps/tiles/{zoom}/{x}/{y}

Returns one north-up lossless WebP in the standard XYZ Web Mercator scheme. Request format=png for the optional PNG representation.

GET/api/v1/courses/{courseId}/maps/static

Returns one pixel-aligned PNG for a WGS84 bounding box and zoom.

GET/api/v1/courses/{courseId}/maps/archive.pmtiles

Streams a published archive and supports HTTP byte ranges required by PMTiles clients.

curl "https://caddie100.com/api/v1/courses/COURSE_ID/maps/tiles?zoom=17&tileSize=512" \
  -H "x-api-key: YOUR_API_KEY"
curl "https://caddie100.com/api/v1/courses/COURSE_ID/maps/tiles/17/35275/49613?tileSize=512&revision=COURSE_REVISION" \
  -H "x-api-key: YOUR_API_KEY" \
  --output course-17-35275-49613.webp
CompatibilityThe previous /api/v1/maptiles/… routes remain callable. New integrations should use the product-scoped /courses/{courseId}/maps/… routes.

Keys, quotas, and revisions

An authorization-safe revision contract.

Use an organization credential with the course-maps:read scope and send it through x-api-key or Authorization: Bearer …. Successful responses report X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset; an exhausted window returns 429 with Retry-After. Licensed organization traffic uses an atomic Supabase rate window shared by every application instance. Environment-configured bootstrap keys retain only a process-local fallback and should be replaced by organization credentials for partner integrations.

The plan returns a full SHA-256 course revision and already includes it in generated URLs. A matching revision guarantees that the requested course data is still current; a stale revision receives 409 and the current revision header. Authenticated responses use Cache-Control: private, no-store so a browser or shared CDN cannot serve licensed content after a key is revoked. Partners may cache artifacts behind their own authenticated boundary using the course revision as part of the cache key.

Authenticationx-api-key or Bearer token
Quota signalsLimit, remaining, reset, and retry headers
Pinned revisionRejects stale course data with 409
Response cachingPrivate, no-store at the licensed API boundary

MapLibre XYZ example

Use the returned template as a raster source.

Read the plan first. Fit the initial viewport to geographicBounds, use coverageBounds as the available padded extent, and retain the returned revision in every tile URL. MapLibre's transformRequest keeps the key in a request header instead of a query string.

Tile plans and tile requests default to lossless WebP. Add format=png to the plan or tile URL only when a client requires PNG.

const apiOrigin = "https://caddie100.com";
const apiKey = "YOUR_API_KEY";
const courseId = "COURSE_ID";
const revision = "COURSE_REVISION";

const map = new maplibregl.Map({
  container: "map",
  transformRequest: (url) => url.startsWith(apiOrigin)
    ? { url, headers: { "x-api-key": apiKey } }
    : { url },
  style: {
    version: 8,
    sources: {
      "caddie100-course": {
        type: "raster",
        tiles: [
          `${apiOrigin}/api/v1/courses/${courseId}/maps/tiles/{z}/{x}/{y}?tileSize=512&revision=${encodeURIComponent(revision)}`
        ],
        tileSize: 512,
        minzoom: 15,
        maxzoom: 20
      }
    },
    layers: [{ id: "caddie100-course", type: "raster", source: "caddie100-course" }]
  }
});

The interactive Maps Explorer is available in the private employee workspace.

One custom image

Bounding box + zoom → PNG.

Provide bbox=west,south,east,north and an integer zoom from 0 through 22. The requested box must stay within the course's padded coverage and may span at most 16 × 16 source tiles. The service snaps outward to whole Web Mercator pixels, brands the finished image after its final crop, then reports the exact resolved box and natural output size in response headers. Source XYZ tiles remain unbranded.

curl "https://caddie100.com/api/v1/courses/COURSE_ID/maps/static?bbox=-83.110736,39.987982,-83.092498,39.997394&zoom=17&revision=COURSE_REVISION" \
  -H "x-api-key: YOUR_API_KEY" \
  --output course-static.png

The Static Map Playground is available in the private employee workspace.

PMTiles delivery boundary

Archive serving is ready for populated courses.

The archive endpoint supports GET, HEAD, ETags, conditional requests, and single HTTP byte ranges without reading a complete archive into memory. A missing archive returns 404; the tile-plan response also reports archive.available so clients can choose XYZ fallback before opening it.

curl "https://caddie100.com/api/v1/courses/COURSE_ID/maps/archive.pmtiles?revision=COURSE_REVISION" \
  -H "x-api-key: YOUR_API_KEY" \
  -H "Range: bytes=0-16383" \
  --output archive-header.bin
Publisher contractPlace a versioned archive at {CADDIE100_MAP_ARCHIVE_ROOT}/{courseId}/{sha256Revision}.pmtiles. The unversioned fallback is {courseId}/course.pmtiles. Archive generation and full-course population are intentionally separate from request delivery.