CLI tool to migrate images from one Cloudflare Images account to another, or list source images.
- Lists source images via
GET /accounts/{account_id}/images/v1 - Exports each original image via
GET /accounts/{account_id}/images/v1/{image_id}/blob - Uploads into destination via
POST /accounts/{account_id}/images/v1 - Supports
--dry-run, concurrency, retries, and resume from a state file
- Bun
- Cloudflare API token for source account with Images read permissions
- Cloudflare API token for destination account with Images write permissions
cp .env.example .envSet:
CF_SOURCE_ACCOUNT_IDCF_SOURCE_API_TOKENCF_DEST_ACCOUNT_IDCF_DEST_API_TOKEN
List source images:
bun run src/index.ts listList as NDJSON:
bun run src/index.ts list --json --max-pages 2Real migration (resumable):
bun run src/index.ts migrate --resume --concurrency 6 --page-size 100With explicit flags:
bun run src/index.ts \
migrate \
--source-account-id <SOURCE_ACCOUNT_ID> \
--source-api-token <SOURCE_TOKEN> \
--destination-account-id <DEST_ACCOUNT_ID> \
--destination-api-token <DEST_TOKEN>--state-file <path>: default.context/cf-images-migration-state.jsonl--max-pages <n>: limit how many pages are fetched when usinglist--json: emit one JSON object per image (list mode)--on-conflict skip|error: defaultskip--no-preserve-id: do not reuse source IDs in destination--no-preserve-metadata: do not copy metadata--no-preserve-signed-urls: do not copyrequireSignedURLs
Show all options:
bun run src/index.ts --help- State is stored as JSONL and includes
success,skipped, andfailedrows. - With
--resume, previouslysuccess/skippedIDs are ignored.