CLI
The Sarkoa CLI (sarkoa) uploads, lists, downloads, and manages files from the terminal. It works with the same account and API keys as the QGIS plugin.
Requirements: Python 3.10+ on Linux or macOS (curl installer or pipx). Debian/Ubuntu apt and PyPI are coming soon.
Install
curl (recommended)
Prefer inspecting the script before piping:
curl -fsSL https://packages.sarkoa.com/install.sh
curl -fsSL https://packages.sarkoa.com/install.sh | bash
sarkoa --help
pipx (CDN wheel)
pip/pipx need a versioned wheel filename in the URL. Resolve the wheels/LATEST pointer first (do not pass the rolling latest alias URL to pipx):
pipx install "https://packages.sarkoa.com/wheels/$(curl -fsSL https://packages.sarkoa.com/wheels/LATEST)"
sarkoa --help
Or: pip install "https://packages.sarkoa.com/wheels/$(curl -fsSL https://packages.sarkoa.com/wheels/LATEST)" inside a virtual environment.
Coming soon: PyPI and apt
When published: pipx install sarkoa, or the Debian/Ubuntu apt source at https://packages.sarkoa.com/apt.
More install options: Downloads.
Configuration
Point the CLI at your API key. Settings resolve in this order (first wins):
--token TOKENCLI flag (one-shot; not stored)SARKOA_*environment variables~/.sarkoa/configJSON (viasarkoa config set)
The CLI talks to the Sarkoa web app by default. You do not need to set a base URL for normal use.
Persist credentials
sarkoa config set token YOUR_API_TOKEN
sarkoa config set org-id YOUR_ORG_UUID # optional; same as SARKOA_ORGANIZATION_ID
sarkoa config show # token masked
You need an API key from the web app, see Authentication.
Environment variables
| Variable | Purpose |
|---|---|
SARKOA_API_TOKEN |
API key from Profile → API & Integrations |
SARKOA_ORGANIZATION_ID |
Org UUID from the web app URL (/org/<uuid>/…). Usually unnecessary (the API key already targets one org); required for download-bulk (also via org-id / --org) |
download-bulk is the one command an API key cannot run. It goes through the manifest API,
which accepts a signed-in session only, so an API key gets 403 there. Everything else on this
page works with a key.
Commands
Grouped by what they act on. Most commands take --json where a machine-readable form makes
sense, and --help on any subcommand lists its flags.
Files and folders
| Command | Purpose |
|---|---|
sarkoa upload PATH |
Upload a file; optional --folder FOLDER to place it in a folder; optional --cloud-native PATH for a pre-built PMTiles/COG (required path when the source is > 10 GB, since remote optimize cannot run, see File optimization). Large transfers show a progress bar on a TTY (--progress / --no-progress) |
sarkoa sync DIR |
Upload everything in a local directory that is not already in the library (--folder, --recursive, --dry-run). Run it with --dry-run first to see what it would send |
sarkoa list |
List files: name, type (Polygon / Polyline / Raster bands / Folder), status, size, relative time, short ref (--json, --search, --folder) |
sarkoa mkdir NAME |
Create a folder (--parent FOLDER to nest it) |
sarkoa mv REF FOLDER |
Move a file or folder (--root to move to the library root) |
sarkoa rename REF NAME |
Change a file or folder's display name. The stored object is untouched |
sarkoa status REF |
Processing status and metadata (full ID, type, format) |
sarkoa inspect REF |
Fields, statistics, geometry, and bands for styling decisions (--json). Run this before writing a style or a --where filter |
sarkoa retry REF |
Requeue a file whose optimization failed. --dry-run prints the failure diagnosis without requeueing |
sarkoa quota SIZE |
Ask whether an upload of that size would exceed the plan limit. SIZE is a byte count or a path (--json) |
sarkoa download REF DEST |
Download to a destination path (progress bar on large TTY transfers) |
sarkoa download-bulk |
Bulk download via the manifest API. Signed-in session auth only: an API key gets 403, so this one needs --org or a stored org-id |
sarkoa view REF |
Open the file in the web app; --print-only |
sarkoa delete REF --yes |
Delete (requires --yes; folders that still have contents also need --recursive) |
Sharing
| Command | Purpose |
|---|---|
sarkoa share create REF |
Create a public /share/… link (--access, --expires-days, --password, --json) |
sarkoa share list REF |
List shares for a file (--json) |
sarkoa share revoke REF SHARE_ID --yes |
Revoke a share (requires --yes) |
Time series
| Command | Purpose |
|---|---|
sarkoa series list |
List your Time series |
sarkoa series create NAME |
Create a Time series container (see Time series) |
sarkoa series append SERIES_ID PATH --captured-at ISO |
Add one dated snapshot to a series; optional --period-start, --idempotency-key, --temporal-mode off (recommended for vector snapshots) |
sarkoa series status SERIES_ID REQUEST_ID |
Check whether an append finished |
Maps
| Command | Purpose |
|---|---|
sarkoa map list |
List custom maps (--json) |
sarkoa map get REF |
Print or save a full map document (--json, --output) |
sarkoa map create --file map.json |
Create a map from a map document (--dry-run, --json; --yes for public, password, or email-invite) |
sarkoa map apply REF --file map.json |
Replace a map with a document (--expected-revision, --dry-run; --yes to publish) |
sarkoa map validate --file map.json |
Validate a map document without saving |
sarkoa map export REF --output map.json |
Export the current map document |
sarkoa map clone REF |
Duplicate a map (--title, --json) |
sarkoa map open REF |
Open the map viewer (--print-only) |
sarkoa map embed REF |
Print a ready-to-paste iframe (--width, --height, --title) or just the URL (--url-only) |
sarkoa map delete REF --yes |
Delete a map (requires --yes) |
sarkoa map display REF |
Set visitor defaults: --mode 2d|3d and --projection mercator|flat|globe |
sarkoa map access REF |
Set who can open the map: --mode organization|org|public|specific_users|invited_emails|password (--yes for public, password, and email invite) |
Map layers
| Command | Purpose |
|---|---|
sarkoa map layer add MAP --asset REF |
Add a file as a layer (--attribution-text, --attribution-url) |
sarkoa map layer remove MAP LAYER |
Remove a layer |
sarkoa map layer show|hide MAP LAYER |
Toggle layer visibility |
sarkoa map layer style MAP LAYER --file style.json |
Replace a layer style |
sarkoa map layer classify MAP LAYER --attribute FIELD |
Assign categorized class colours (--shuffle to recolour) |
sarkoa map layer attribution MAP LAYER |
Set the cartographic credit for one layer |
sarkoa map layer popup MAP LAYER |
Configure the click popup: --fields, --title-field, --image-fields, --disable, --clear |
sarkoa map layer legend MAP LAYER |
Label a raster layer's legend: --title, --min-label, --max-label, --unit, --disable, --clear |
Map review
| Command | Purpose |
|---|---|
sarkoa map comment list MAP |
List review comments, open and resolved |
sarkoa map comment add MAP |
Post a comment, or reply to a thread |
sarkoa map comment resolve MAP COMMENT_ID |
Resolve a comment, or reopen it |
sarkoa map comment delete MAP COMMENT_ID |
Delete a comment |
Querying data
| Command | Purpose |
|---|---|
sarkoa query REF |
Filter a hosted vector layer without downloading it: --bbox, --where FIELD:OP:VALUE (repeatable), --fields, --limit (default 100, max 1000), --offset, --no-geometry, --output. See Feature query |
sarkoa stac search |
Search the raster catalogue: --bbox, --datetime, --collection {rgb,multispectral,singleband,elevation}, --limit, --offset |
Webhooks
| Command | Purpose |
|---|---|
sarkoa webhook list |
List endpoints (--events also prints the subscribable event types) |
sarkoa webhook create URL |
Register an endpoint (--event repeatable, --description). Prints the signing secret once |
sarkoa webhook test ID |
Send a signed test delivery now and show what the endpoint returned |
sarkoa webhook rotate ID |
Issue a new signing secret (takes effect immediately, with no overlap) |
sarkoa webhook deliveries |
Recent delivery attempts (--webhook-id, --status, --limit) |
sarkoa webhook delete ID |
Remove an endpoint and its pending deliveries |
Account
| Command | Purpose |
|---|---|
sarkoa whoami |
Show email, organization, scopes, and auth method |
sarkoa config … |
set / show / clear stored config |
Folders in practice
Create a folder, upload into it, list its contents, or move files. Names, short refs, and full IDs all work as folder references.
sarkoa mkdir Surveys
sarkoa mkdir 2026 --parent Surveys
sarkoa upload ./data.geojson --folder Surveys
sarkoa list --folder Surveys
sarkoa mv ./other.geojson Surveys
sarkoa mv abc12345 --root
sarkoa delete Surveys --yes --recursive
sarkoa list without --folder shows the library root (folders and files that are not nested). Deleting a folder that still has contents requires --recursive.
Large series appends resume automatically if the connection drops, so you do not need to restart a long upload from the beginning.
Upload rate limits
Completing an upload (POST /api/uploads/complete) is limited to 30 requests per 10 minutes per API key. Presign allows 60 per 10 minutes. If you receive 429 Too Many Requests, wait for the number of seconds in the Retry-After response header before trying again. The CLI handles this automatically for completion. For bulk backfills, pace uploads (about one completion every 20 seconds stays under the cap).
Map documents
Custom maps are authored as JSON documents (apiVersion: "sarkoa.com/maps/v1"). A document can set the camera, basemap and theme, access mode, collaboration/download settings, and per-layer styles such as fill color, outline, categorized or graduated symbology, raster palettes, popups, and legends.
Example:
sarkoa map validate --file ./quake-map.json
sarkoa map create --file ./quake-map.json --json
sarkoa map layer add MAP_ID --asset faults.geojson --attribution-text "City of Ottawa" --attribution-url https://ottawa.ca
sarkoa map layer classify MAP_ID layer-a --attribute kind
sarkoa map layer classify MAP_ID layer-a --shuffle
sarkoa map display MAP_ID --mode 2d --projection globe
sarkoa map access MAP_ID --mode organization
sarkoa map layer style MAP_ID layer-a --file ./fault-style.json
sarkoa map open MAP_ID
Use --file=- to read a document from stdin. Prefer --expected-revision when updating an existing map so concurrent edits do not overwrite each other. Public, password-protected, and email-invite maps need --yes on map create, map apply, and map access.
Backfilling a directory
sarkoa sync uploads what is missing rather than re-sending what is already there, which makes it safe to re-run after a failure part way through a large batch.
sarkoa sync ./exports --folder Surveys --recursive --dry-run
sarkoa sync ./exports --folder Surveys --recursive
Always do the --dry-run pass first. It prints exactly what would upload and stops, which is the cheapest way to notice you pointed it at the wrong directory. Mind the completion rate limit above on large batches.
Querying instead of downloading
Once a layer is hosted, you can filter it server-side and get GeoJSON back:
sarkoa inspect parcels # see the attribute names first
sarkoa query parcels \
--bbox -79.6,43.5,-79.1,43.9 \
--where zone:eq:RM4 \
--where assessed_value:gt:750000 \
--fields address,zone,assessed_value \
--output ./rm4.geojson
--no-geometry makes attribute-only work dramatically smaller. Full operator list and paging: Feature query.
Being told when processing finishes
Instead of polling sarkoa status in a loop:
sarkoa webhook create https://example.com/hooks/sarkoa \
--event asset.processing.succeeded \
--event asset.processing.failed
sarkoa webhook test WEBHOOK_ID
The signing secret prints once, on create. Store it then. If a delivery goes missing, sarkoa webhook deliveries --status exhausted has the HTTP code your endpoint returned and the error text. See Webhooks.
See sarkoa --help and each subcommand’s --help for flags.
Related
- Authentication: creating API keys
- MCP (AI agents): same API key over hosted Streamable HTTP
- QGIS plugin: same workflows in desktop GIS
- Feature query:
sarkoa queryandsarkoa stac searchin detail - Webhooks: events, signing, and retries
- API reference: public tile and metadata API
- Map documents: full map document field reference