KotobaMedia Tile Kiln

API ドキュメント

KotobaMedia Tile Kiln は、アップローダーと同じ処理を自動化するための小さな HTTP API を提供します。ジョブを作成し、 単一 PUT または multipart セッションで GeoTIFF をアップロードし、変換完了までポーリングしてから、生成された TileJSON URL を利用します。ジョブ作成には連絡先メールアドレスまたは有効な API キーが必須ですが、公開ジョブレスポンスにはメールアドレスや API キーメタデータは含まれません。変更キー付きのジョブは期限前にアップロードと生成された出力を呼び出し元から削除できます。

ベース URL

環境 ベース URL
本番 https://tile-kiln-api.kmproj.com
開発 https://tile-kiln-api-dev.kmproj.com
API_URL="https://tile-kiln-api.kmproj.com"

制限とタイミング

1. ジョブを作成する

POST /jobs

curl -sS -X POST "$API_URL/jobs" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "maps@example.com",
    "changeKey": "keep-this-to-delete-later",
    "fileName": "source.tif",
    "sizeBytes": 73400320,
    "contentType": "image/tiff",
    "options": {
      "tileFormat": "png",
      "tileSize": 512,
      "resampling": "bilinear",
      "minZoom": 4,
      "maxZoom": 14
    }
  }'

API キーによるアップロードでは、x-api-key ヘッダーでキーを送信します。JSON 本文の apiKey または api_key も使用できます。有効な API キーが指定された場合、email は任意です。省略すると API キーに紐づく連絡先メールアドレスを使います。

curl -sS -X POST "$API_URL/jobs" \
  -H "Content-Type: application/json" \
  -H "x-api-key: $TILE_KILN_API_KEY" \
  -d '{
    "fileName": "source.tif",
    "sizeBytes": 73400320,
    "contentType": "image/tiff"
  }'

リクエスト本文のフィールド

フィールド 必須 説明
email API キーを使わない場合は必須 内部トレース用の連絡先メールアドレスです。contactEmail も使用できます。API キーアップロードでは、省略すると API キーに紐づく連絡先メールアドレスを使い、指定するとそのジョブ用に上書きします。メールアドレスは POST /jobs と GET /jobs/{jobId} のジョブレスポンスには返されません。
fileName 必須 元ファイル名です。filename も使用できます。安全でない文字はサニタイズされます。
changeKey 任意 呼び出し元が保持する任意のキーです。API キーを使わないアップロードでは、指定すると保持期間が 6 カレンダーか月になり、DELETE /jobs/{jobId} で期限前に削除できます。API キーアップロードでは保持期間は無期限のままで、変更キーは呼び出し元削除だけを有効にします。省略すると呼び出し元からの削除はできません。change_key も使用できます。
apiKey 任意 無期限保持アップロード用の API キーです。通常は x-api-key ヘッダーを使用してください。本文では api_key も使用できます。有効な API キーを使うとアップロードは期限切れにならず、ガベージコレクション対象外になります。
sizeBytes 推奨 元ファイルのバイト数です。fileSizeBytes と contentLength も使用できます。
contentType 任意 アップロード時の content type です。省略時は image/tiff です。
uploadMode 任意 single(既定)または multipart。multipart では sizeBytes が必須です。
options 任意 タイル変換オプションです。options を省略した場合は、各オプションをトップレベルで送ることもできます。

変換オプション

オプション デフォルト 説明
tileFormat png ラスタータイル形式です。png、jpeg、webp のいずれかです。
tileSize 256 タイルサイズのピクセル数です。128 から 4096 までの整数です。
resampling bilinear nearest、bilinear、cubic、cubicspline、lanczos、mode、average のいずれかです。
minZoom 0 任意の最小ズームです。0 から 24 までの整数で、既定値は 0 です。
maxZoom 未設定 任意の最大ズームです。0 から 24 までの整数です。省略時は rio-pmtiles が元データの解像度から算出します。minZoom 以上である必要があります。
quality 未設定 JPEG または WebP の任意の出力 quality です。1 から 100 までの整数で、PNG では無視されます。
overviewLevels [2,4,8,16,32,64,128,256] 非推奨です。後方互換性のため維持されますが無視され、指定範囲の全ズームが PMTiles に含まれます。

成功レスポンス

公開ジョブオブジェクトには、連絡先メールアドレス、変更キー、API キーメタデータは含まれません。

ジョブレスポンスの expiresAt は、公開ジョブステータスの有効期限の目安です。API キージョブは無期限のため、expiresAt は省略されます。

{
  "job": {
    "jobId": "43f5a43d-3081-4378-9a19-7f4a47d0dc0b",
    "status": "AWAITING_UPLOAD",
    "uploadMode": "single",
    "fileName": "source.tif",
    "canDelete": true,
    "contentType": "image/tiff",
    "createdAt": "2026-06-25T04:30:00.000Z",
    "updatedAt": "2026-06-25T04:30:00.000Z",
    "expiresAt": 1798173000,
    "input": {
      "bucket": "tile-kiln-input",
      "key": "uploads/43f5a43d-3081-4378-9a19-7f4a47d0dc0b/source.tif",
      "sizeBytes": 73400320
    },
    "output": {
      "bucket": "km-tileserver",
      "key": "uploads/raster/43f5a43d-3081-4378-9a19-7f4a47d0dc0b/source.pmtiles"
    },
    "options": {
      "overviewLevels": [2, 4, 8, 16, 32, 64, 128, 256],
      "resampling": "bilinear",
      "tileFormat": "png",
      "tileSize": 512,
      "minZoom": 4,
      "maxZoom": 14
    }
  },
  "upload": {
    "mode": "single",
    "method": "PUT",
    "url": "https://...",
    "headers": {
      "Content-Type": "image/tiff"
    },
    "maxSizeBytes": 1000000000,
    "expiresInSeconds": 3600
  }
}

2. GeoTIFF をアップロードする

返された署名付き URL に元ファイルを直接アップロードします。Authorization ヘッダーは追加しないでください。

UPLOAD_URL="https://..."

curl -sS -X PUT "$UPLOAD_URL" \
  -H "Content-Type: image/tiff" \
  --data-binary @source.tif

Multipart アップロード

uploadMode に multipart を指定し、sizeBytes を送信します。レスポンスには秘密の bearer capability である uploadId、64 MiB の partSizeBytes、想定される各 part の署名付き URL が含まれます。GET /jobs/{jobId} は upload ID を返しません。

{
  "upload": {
    "mode": "multipart",
    "uploadId": "private-capability",
    "partSizeBytes": 67108864,
    "expiresInSeconds": 3600,
    "maxSizeBytes": 1000000000,
    "parts": [
      { "partNumber": 1, "url": "https://..." },
      { "partNumber": 2, "url": "https://..." }
    ]
  }
}

ファイル全体をバッファせずに slice し、最大 4 part を並列 PUT して各レスポンスの ETag を保存します。401 または 403 では、{"uploadId":"...","partNumbers":[3,4]} を POST /jobs/{jobId}/upload/parts に送信します。

全 part 成功後、ETag を並べて {"uploadId":"...","parts":[{"partNumber":1,"etag":"..."}]} を POST /jobs/{jobId}/upload/complete に送り、それからポーリングします。中止は DELETE /jobs/{jobId}/upload に upload ID を JSON で送ります。

3. ジョブをポーリングする

GET /jobs/{jobId}

JOB_ID="43f5a43d-3081-4378-9a19-7f4a47d0dc0b"

curl -sS "$API_URL/jobs/$JOB_ID"

ステータス値

ステータス 意味
AWAITING_UPLOAD ジョブは存在しますが、元ファイルはまだアップロードされていません。
PROCESSING アップロードイベントを受け取り、変換を実行中です。
COMPLETED PMTiles アーカイブが正常に書き込まれました。
FAILED 変換に失敗しました。ジョブに error と errorDetails が含まれる場合があります。

数秒ごとにポーリングしてください。ステータスが COMPLETED または FAILED になったら停止します。

4. TileJSON URL を組み立てる

完了した出力キーは .pmtiles で終わります。公開 TileJSON URL は、同じキーから .pmtiles を除き、.json を付けて https://tiles.kmproj.com/ 配下に置いた URL です。

OUTPUT_KEY="uploads/raster/43f5a43d-3081-4378-9a19-7f4a47d0dc0b/source.pmtiles"
TILEJSON_URL="https://tiles.kmproj.com/${OUTPUT_KEY%.pmtiles}.json"

echo "$TILEJSON_URL"
map.addSource("uploaded-raster", {
  type: "raster",
  url: "https://tiles.kmproj.com/uploads/raster/43f5a43d-3081-4378-9a19-7f4a47d0dc0b/source.json",
  tileSize: 512,
});

5. 任意の呼び出し元削除

DELETE /jobs/{jobId}

呼び出し元からの削除は、ジョブ作成時に空でない changeKey を指定した場合のみ利用できます。API キー認証だけでは削除は認可されません。変更キーは API から返されません。

完了済みの変換では、保持された変換状態と生成された PMTiles 出力を削除します。一時的な元ファイルやエラー状態がまだ存在する場合は、それらも削除します。変換が完了していない場合は、一時的なアップロード状態を削除します。

ジョブが PROCESSING の間は delete は 409 を返します。COMPLETED または FAILED になってから再試行してください。

JOB_ID="43f5a43d-3081-4378-9a19-7f4a47d0dc0b"

curl -sS -X DELETE "$API_URL/jobs/$JOB_ID" \
  -H "Content-Type: application/json" \
  -d '{"changeKey":"keep-this-to-delete-later"}'

変更キーは x-change-key ヘッダーでも送信できます。

エンドツーエンドのシェルスクリプト

このスクリプトは jq を使い、ジョブ完了までポーリングします。

#!/usr/bin/env sh
set -eu

API_URL="${API_URL:-https://tile-kiln-api.kmproj.com}"
FILE_PATH="${1:?Usage: $0 source.tif maps@example.com}"
EMAIL="${2:?Usage: $0 source.tif maps@example.com}"
CHANGE_KEY="${CHANGE_KEY:-}"
FILE_NAME="$(basename "$FILE_PATH")"
SIZE_BYTES="$(wc -c < "$FILE_PATH" | tr -d ' ')"

CREATE_RESPONSE="$(
  jq -n \
    --arg email "$EMAIL" \
    --arg changeKey "$CHANGE_KEY" \
    --arg fileName "$FILE_NAME" \
    --arg contentType "image/tiff" \
    --argjson sizeBytes "$SIZE_BYTES" \
    '({
      email: $email,
      fileName: $fileName,
      sizeBytes: $sizeBytes,
      contentType: $contentType,
      options: {
        tileFormat: "png",
        tileSize: 512,
        resampling: "bilinear"
      }
    } + (if $changeKey == "" then {} else { changeKey: $changeKey } end))' |
  curl -sS -X POST "$API_URL/jobs" \
    -H "Content-Type: application/json" \
    --data-binary @-
)"

JOB_ID="$(printf '%s' "$CREATE_RESPONSE" | jq -r '.job.jobId')"
UPLOAD_URL="$(printf '%s' "$CREATE_RESPONSE" | jq -r '.upload.url')"
CONTENT_TYPE="$(printf '%s' "$CREATE_RESPONSE" | jq -r '.upload.headers["Content-Type"]')"

curl -sS -X PUT "$UPLOAD_URL" \
  -H "Content-Type: $CONTENT_TYPE" \
  --data-binary "@$FILE_PATH"

while :; do
  JOB_RESPONSE="$(curl -sS "$API_URL/jobs/$JOB_ID")"
  STATUS="$(printf '%s' "$JOB_RESPONSE" | jq -r '.job.status')"
  printf '%s\n' "job $JOB_ID: $STATUS"

  case "$STATUS" in
    COMPLETED)
      OUTPUT_KEY="$(printf '%s' "$JOB_RESPONSE" | jq -r '.job.output.key')"
      printf '%s\n' "https://tiles.kmproj.com/${OUTPUT_KEY%.pmtiles}.json"
      exit 0
      ;;
    FAILED)
      printf '%s\n' "$JOB_RESPONSE" >&2
      exit 1
      ;;
  esac

  sleep 3
done

エラー

エラーレスポンスは JSON です。

{
  "message": "fileName is required."
}
HTTP ステータス 意味
400 JSON 本文が不正、必須フィールドが不足、メールアドレスが不正、変更キーが不正、または変換オプションが不正です。
403 指定された API キーまたは multipart uploadId が不正、または保存済みの変更キーがないアップロードや誤った変更キーで削除をリクエストしました。
404 ルート、ジョブ ID、または保持された変換が見つかりません。
409 multipart 操作がジョブ状態と一致しない、または処理中に削除をリクエストしました。
413 宣言された入力サイズが 1000000000 バイトを超えています。本文に maxSizeBytes も含まれます。
500 予期しないサービスエラーです。