KotobaMedia Tile Kiln

API ドキュメント

KotobaMedia Tile Kiln は、アップローダーと同じ処理を自動化するための小さな HTTP API を提供します。ジョブを作成し、 返された署名付き URL に 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 /jobsGET /jobs/{jobId} のジョブレスポンスには返されません。
fileName 必須 元ファイル名です。filename も使用できます。安全でない文字はサニタイズされます。
changeKey 任意 呼び出し元が保持する任意のキーです。API キーを使わないアップロードでは、指定すると保持期間が 6 カレンダーか月になり、DELETE /jobs/{jobId} で期限前に削除できます。API キーアップロードでは保持期間は無期限のままで、変更キーは呼び出し元削除だけを有効にします。省略すると呼び出し元からの削除はできません。change_key も使用できます。
apiKey 任意 無期限保持アップロード用の API キーです。通常は x-api-key ヘッダーを使用してください。本文では api_key も使用できます。有効な API キーを使うとアップロードは期限切れにならず、ガベージコレクション対象外になります。
sizeBytes 推奨 元ファイルのバイト数です。fileSizeBytescontentLength も使用できます。
contentType 任意 アップロード時の content type です。省略時は image/tiff です。
options 任意 タイル変換オプションです。options を省略した場合は、各オプションをトップレベルで送ることもできます。

変換オプション

オプション デフォルト 説明
tileFormat png ラスタータイル形式です。pngjpegwebp のいずれかです。
tileSize 256 タイルサイズのピクセル数です。128 から 4096 までの整数です。
resampling bilinear nearestbilinearcubiccubicsplinelanczosmodeaverage のいずれかです。
minZoom 未設定 任意の最小ズームです。0 から 24 までの整数です。
maxZoom 未設定 任意の最大ズームです。0 から 24 までの整数です。minZoom 以上である必要があります。
quality 未設定 任意の出力 quality です。1 から 100 までの整数です。
overviewLevels [2,4,8,16,32,64,128,256] 2 以上の 2 の累乗を含む空でない配列です。

成功レスポンス

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

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

{
  "job": {
    "jobId": "43f5a43d-3081-4378-9a19-7f4a47d0dc0b",
    "status": "AWAITING_UPLOAD",
    "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": {
    "method": "PUT",
    "url": "https://...",
    "headers": {
      "Content-Type": "image/tiff"
    },
    "maxSizeBytes": 100000000,
    "expiresInSeconds": 900
  }
}

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

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

UPLOAD_URL="https://..."

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

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

GET /jobs/{jobId}

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

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

ステータス値

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

数秒ごとにポーリングしてください。ステータスが 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 キーが不正、または保存済みの変更キーがないアップロードや誤った変更キーで削除をリクエストしました。
404 ルート、ジョブ ID、または保持された変換が見つかりません。
409 アップロード処理中に削除をリクエストしました。
413 宣言された入力サイズが 100000000 バイトを超えています。本文に maxSizeBytes も含まれます。
500 予期しないサービスエラーです。