API reference

Sarkoa answers HTTP on two hosts, and picking the wrong one is the most common way to lose an afternoon. They are not tiers of the same API; they do different jobs.

flowchart LR
  Client[Your app or script]
  Client -->|Bearer sk_...| PublicAPI[Public API host]
  Client -->|Bearer API key| AppHost[App host]
  PublicAPI --> Tiles[Tiles and metadata]
  AppHost --> Library[Files, maps, series]
  AppHost --> Data[Feature query and catalogue]
  AppHost --> Hooks[Webhooks]

Which host serves what

You want Host Auth Plan
Tiles and dataset metadata for your own map client api.sarkoa.com Asset API key (sk_…), from API Routes Essential and above
Upload, list, move, download, delete files app host, /api/… API key from Profile β†’ API & Integrations Any
Create and edit custom maps app host, /api/… Same Any
Query features and search the raster catalogue app host, /api/… Same Any
Webhooks app host, /api/… Same Essential and above

The two key types are separate and not interchangeable. An Asset API key will not manage files, and a Profile key will not fetch tiles from api.sarkoa.com. See Authentication.

Most people never call the app host by hand. The CLI, QGIS plugin, and MCP are all clients of it, and they handle presigned uploads, paging, and retries for you. Reach for raw HTTP when you are embedding Sarkoa in something those cannot cover.

The public API

  • Hostname: https://api.sarkoa.com
  • Header: Authorization: Bearer sk_...
  • Query alternative: ?api_key=sk_..., for contexts where you cannot set a header (an <img> tag, some GIS clients). Prefer the header: query strings end up in logs, proxies, and browser history.
  • Scopes: tiles:read, metadata:read
  • Methods: GET and HEAD only, plus OPTIONS for CORS. Anything else returns 405.

Machine-readable spec

The full OpenAPI 3.0 document is public and needs no key to read:

https://app.sarkoa.com/openapi/sarkoa-public-api.json

Point a generator, an HTTP client, or an agent at it rather than transcribing this page. Signed in, the web app also renders it as interactive docs with your own organization and dataset slugs filled in.

Routes

GET /health                                            # liveness, no API key

By file ID:

GET /v1/assets/:assetId/tiles/:z/:x/:y.webp            # raster tile
GET /v1/assets/:assetId/vector/tiles/:z/:x/:y.mvt      # vector tile
GET /v1/assets/:assetId/metadata                       # asset metadata JSON

By org and dataset slug, where datasetSlug is a name you configure under API Routes:

GET /v1/:orgSlug/:datasetSlug/tiles/:z/:x/:y.webp
GET /v1/:orgSlug/:datasetSlug/vector/tiles/:z/:x/:y.mvt
GET /v1/:orgSlug/:datasetSlug/metadata

Raster tiles are served as WebP. There is no .png route. Calling the raster path for a vector file, or the reverse, returns 400 with wrong_asset_type rather than an empty tile.

The spec also describes temporal tile and manifest routes. Those are turned off on the public API and answer 503 with feature_disabled; time-based layers are served through the app and the map viewer today.

Errors

Every error carries a human-readable error and a stable code. Match on code, never on the message.

{ "error": "Public API access requires an Essential plan or higher.", "code": "plan_upgrade_required" }
Status Common codes Meaning
400 invalid_asset_id, invalid_time, wrong_asset_type Malformed request, or the wrong tile family for this file type
401 api_key_required, invalid_api_key, api_key_expired Key missing, unrecognized, or past its expiry date
403 insufficient_scope, plan_upgrade_required Key lacks the scope, or the plan is below Essential
404 asset_not_found, route_not_found, not_found, no_snapshot Unknown, deleted, archived, or belonging to another organization
429 quota_exceeded Over tile quota. Honour the Retry-After header
503 feature_disabled The API or that tile family is turned off

Deleted and archived files stop serving immediately and return 404. An asset belonging to another organization also returns 404 rather than 403, so a key cannot be used to probe what exists elsewhere.

Caching

Tiles and metadata for private files are returned as Cache-Control: private, so they are stored only by the requesting client. Files shared with a public link are returned as public and may be cached by shared proxies and CDNs.

Billing notes

  • For files shared with a public link, tile usage is billed to the organization that hosts the file. Otherwise it is billed to the organization the API key belongs to

More on usage: Tile requests.

  • Authentication: the two key types, and their scopes
  • Feature query: asking which features match, not just drawing them
  • Webhooks: being told when data lands, instead of polling
  • CLI: upload, list, and download from the terminal
  • MCP: the same surface for AI agents
  • Tile requests: how tile fetches relate to billing