A non-interactive command-line interface for the Uploadcare platform, written in Go. Manage files, projects, webhooks, conversions, and more from your terminal or CI/CD pipelines.
- File management — upload, list, search, copy, store, and delete files
- File tags — attach tags while uploading; list, replace, update, or clear them later
- Project management — create, update, delete projects; manage API secrets and usage metrics
- JSON & NDJSON output — structured output with field filtering and
jqsupport - Stdin piping — compose commands for batch operations
- Dry-run mode — preview destructive operations before executing
- Multi-project support — switch between projects via config or flags
- AI-friendly — input sanitization, field masking, NDJSON streaming
curl -fsSL https://raw.githubusercontent.com/uploadcare/uploadcare-cli/main/scripts/install.sh | shThe install script supports environment variables: VERSION (e.g., 0.1.0), INSTALL_DIR (default: /usr/local/bin), and UNINSTALL=1 to remove the binary. Pass them to sh:
curl -fsSL <install-url> | VERSION=0.1.0 shPre-built binaries for all platforms are available on the Releases page.
Requires Go 1.26.1+.
git clone https://github.com/uploadcare/uploadcare-cli.git
cd uploadcare-cli
make build
./bin/uploadcare version# Set credentials
export UPLOADCARE_PUBLIC_KEY="your-public-key"
export UPLOADCARE_SECRET_KEY="your-secret-key"
# List files
uploadcare file list
# Upload a file
uploadcare file upload photo.jpg --tag vacation --tag featured
# Search by text and tags
uploadcare file search invoice --tag-all approved --tag-none archived
# Add and remove multiple tags atomically
uploadcare tag update <uuid> --delete draft --add approved --add featured
# Get file info as JSON
uploadcare file info <uuid> --json all
# Delete all unstored files (piping)
uploadcare file list --page-all --stored false --json uuid \
| uploadcare file delete --from-stdin
# Mirror all stored files to ./backup with 8 parallel workers
uploadcare file list --page-all --stored true --json uuid \
| uploadcare file download --from-stdin --output-dir ./backup --parallel 8The CLI resolves configuration from multiple sources. Higher-priority sources override lower ones.
| Priority | Source | Example |
|---|---|---|
| 1 (highest) | CLI flags | --public-key pk --secret-key sk |
| 2 | Environment variables | UPLOADCARE_PUBLIC_KEY=pk |
| 3 | Named project (--project flag or UPLOADCARE_PROJECT env) |
--project "Staging" |
| 4 | Default project from config file | default_project: "My App" |
| 5 (lowest) | Top-level keys in config file | public_key: pk |
If either --public-key or --secret-key is set via flags or environment variables, named project lookup is skipped entirely. This prevents misconfigured environments from silently targeting the wrong project.
Location: ~/.uploadcare/config.yaml
Minimal config:
public_key: "demopublickey"
secret_key: "demosecretkey"Full config with multiple projects:
# Account-level token (for project management commands)
project_api_token: "your-bearer-token"
# Default project for commands that need public_key/secret_key
default_project: "My App"
# Named projects
projects:
"My App":
public_key: "abc123"
secret_key: "secret..."
cdn_base: "https://my-custom-cdn.example.com" # optional per-project override
"Staging":
public_key: "def456"
secret_key: "secret..."
# Global cdn_base fallback — used when neither the flag, env var, nor the
# resolved project entry provides a cdn_base. When omitted entirely, the
# CDN base is auto-computed from the project's public key.
# cdn_base: "https://global-cdn-override.example.com"| Variable | Description |
|---|---|
UPLOADCARE_PUBLIC_KEY |
API public key |
UPLOADCARE_SECRET_KEY |
API secret key |
UPLOADCARE_PROJECT_API_TOKEN |
Account-level bearer token |
UPLOADCARE_PROJECT |
Named project to use from config |
UPLOADCARE_VERBOSE |
Enable verbose HTTP logging (1 or true) |
UPLOADCARE_CDN_BASE |
Override CDN base URL |
NO_COLOR |
Disable colored output |
# Uses default_project from config
uploadcare file list
# Select a specific project
uploadcare --project "Staging" file list
# Same via environment variable
UPLOADCARE_PROJECT="Staging" uploadcare file list
# Override with explicit keys (skips project lookup)
uploadcare --public-key pk --secret-key sk file listThe CLI works with three Uploadcare APIs, each using different credentials:
| API | Auth method | Credentials |
|---|---|---|
| REST API | HMAC signature | public_key + secret_key |
| Upload API | Simple auth | public_key only |
| Project API | Bearer token | project_api_token |
Commands validate that the required credentials are present before executing. Missing credentials produce a clear error with instructions on how to set them.
uploadcare
├── file
│ ├── list List files in project
│ ├── search Search files by text, fields, ranges, and tags
│ ├── info Get file details
│ ├── upload Upload local file(s)
│ ├── upload-from-url Upload file from URL
│ ├── store Store file(s)
│ ├── delete Delete file(s)
│ ├── local-copy Copy file within Uploadcare storage
│ ├── remote-copy Copy file to remote storage
│ └── download Download file(s) from the CDN to local disk
├── tag
│ ├── list List a file's tags
│ ├── replace Replace a file's complete tag set
│ ├── update Atomically add and delete tags
│ └── clear Remove all tags from a file
├── metadata
│ ├── list List all metadata keys for a file
│ ├── get Get a metadata value by key
│ ├── set Set a metadata key-value pair
│ └── delete Delete a metadata key
├── group
│ ├── list List file groups
│ ├── info Get group details
│ ├── create Create a file group
│ └── delete Delete a file group
├── convert
│ ├── document Convert a document
│ └── video Convert a video
├── addon
│ ├── execute Execute an add-on on a file
│ └── status Check add-on execution status
├── webhook
│ ├── list List webhooks
│ ├── create Create a webhook
│ ├── update Update a webhook
│ └── delete Delete a webhook
├── project
│ ├── info Get project details
│ ├── list List all projects
│ ├── create Create a new project
│ ├── update Update project settings
│ ├── delete Delete a project
│ ├── use Switch active project
│ ├── secret
│ │ ├── list List API secrets
│ │ ├── create Create a new API secret
│ │ └── delete Delete an API secret
│ └── usage Get usage metrics
├── url-api URL API reference (CDN transformations)
├── api-schema Print machine-readable CLI schema as JSON
├── version Print CLI version
└── completion Generate shell completions
| Flag | Description |
|---|---|
--public-key |
API public key |
--secret-key |
API secret key |
--project-api-token |
Account-level bearer token |
--project |
Named project from config |
--json <fields> |
JSON output: all for every field, or field1,field2 to select |
--jq <expr> |
Apply jq expression (implies --json) |
-q, --quiet |
Suppress non-error output |
-v, --verbose |
Log HTTP requests/responses to stderr |
--no-color |
Disable colored output |
Search accepts an optional full-text query plus exact, phrase, range, image, and tag filters. At least one query or filter is required. Full-text and phrase values must contain at least four characters.
# Full-text search with exact MIME type and tag filters
uploadcare file search invoice \
--exact detected_mime_type=application/pdf \
--tag-all approved \
--tag-none archived \
--sort score \
--sort=-datetime_uploaded
# Exact metadata match
uploadcare file search --exact 'metadata[camera]=Canon' --json uuid,filename,tags,highlight
# Stream every reachable page as NDJSON
uploadcare file search --tag-any featured --page-all --json uuid,tagsRange filters are --uploaded-gt, --uploaded-gte, --uploaded-lt,
--uploaded-lte, and the corresponding --size-* flags. --limit accepts
1–100 and --offset + --limit cannot exceed 1000. The API serves at most the
first 1000 matches of a search, so --page-all streams up to 1000 results.
Pages are filled by following the API's next cursor, so offset-stepped pages
may occasionally overlap; prefer --page-all when completeness matters.
Search uses an asynchronous index, so recent uploads, metadata changes, and
tag changes may take time to appear.
Tags are normalized to lowercase, de-duplicated in first-seen order, and may
contain a-z, 0-9, ., _, and -. Each tag can contain up to 100
characters, and a file can have up to 50 tags.
# Add tags during direct or URL upload
uploadcare file upload photo.jpg --tag vacation --tag featured
uploadcare file upload-from-url https://example.com/photo.jpg --tag remote
# Inspect and replace the complete tag set
uploadcare tag list <uuid>
uploadcare tag replace <uuid> approved featured
# Repeat --add and --delete as many times as needed
uploadcare tag update <uuid> \
--delete draft \
--delete needs-review \
--add approved \
--add featured
# Preview a mutation or remove every tag
uploadcare tag update <uuid> --delete draft --add approved --dry-run
uploadcare tag clear <uuid>Updates apply every deletion before every addition. If the same tag is supplied to both operations, it is present afterward.
Human-readable (default) — tabular output to stdout:
$ uploadcare file list --limit 2
UUID SIZE FILENAME STORED UPLOADED
a1b2c3d4-e5f6-7890-abcd-ef1234567890 1258000 vacation-p...nal-v3.jpg true 2026-03-01T00:00:00Z
b2c3d4e5-f6a7-8901-bcde-f12345678901 348160 document.pdf false 2026-03-02T00:00:00Z
Long filenames, URLs, and paths are shortened to keep one record per line,
marked with ... and keeping the file extension visible. UUIDs and timestamps
are never shortened, so they stay copy-pasteable. Redirected or piped output is
never shortened:
uploadcare file list | less # full filenamesUse --json for values you intend to parse.
JSON — activated with --json all (all fields) or --json field1,field2 (specific fields):
uploadcare file info <uuid> --json uuid,size,filenameNDJSON — one JSON object per line with --page-all:
uploadcare file list --page-all --json uuid,sizeVerbose — HTTP details on stderr (combine with any mode):
$ uploadcare file list --verbose
--> GET https://api.uploadcare.com/files/?limit=100
<-- 200 OK (127ms)
| Code | Meaning |
|---|---|
| 0 | Success |
| 1 | API error or unexpected failure |
| 2 | Usage error (invalid flags, bad input) |
| 3 | Authentication/configuration error |
The CLI is designed for use by AI agents (Claude Code, Cursor, Copilot, etc.) that invoke it as a subprocess. Every feature below works without interactive prompts — missing input causes an immediate, descriptive failure.
A single command gives an agent the full CLI surface — all commands, flags, arguments, examples, and available JSON fields — without parsing --help text:
uploadcare api-schemaThe output includes:
commands[].json_fields— available fields for--json field1,field2filtering per commandagent_notes— usage tips (e.g.--jsonsyntax, timestamp format, piping patterns)url_api— complete URL API reference with all transformation operations
# List all command paths
uploadcare api-schema | jq -r '.commands[].path'
# Get available JSON fields for file info
uploadcare api-schema | jq '.commands[] | select(.path == "file info") | .json_fields'
# Read agent-specific guidance
uploadcare api-schema | jq '.agent_notes'Use --json all to get machine-parseable output, or --json field1,field2 to select specific fields and reduce token usage:
# Full JSON — all fields
uploadcare file info <uuid> --json all
# Only uuid and size — ~50 bytes instead of ~2KB
uploadcare file info <uuid> --json uuid,size
# Apply jq expression (implies --json automatically)
uploadcare file list --jq '.[].uuid'Commands accept --from-stdin for batch operations. Input is auto-detected as plain text (one value per line) or NDJSON (objects with a target field):
# Delete all unstored files
uploadcare file list --page-all --stored false --json uuid \
| uploadcare file delete --from-stdin
# Stream all files as NDJSON (one object per line, no memory buildup)
uploadcare file list --page-all --json uuid,size,mime_type--dry-runon mutating commands previews what would happen without making changes- Input sanitization rejects control characters, path traversal, and double-encoded strings before any API call
- Deterministic exit codes:
0success,1API error,2bad input,3missing credentials — agents can branch on these without parsing stderr
# 1. Discover available commands (no auth needed)
uploadcare api-schema | jq '.commands[].path'
# 2. Upload and extract UUID
uuid=$(uploadcare file upload photo.jpg --jq '.uuid')
# 3. Tag it
uploadcare metadata set "$uuid" category landscape
# 4. Verify before destructive action
uploadcare file delete "$uuid" --dry-run# Build
make build
# Run tests
make test
# Format Go files
make fmt
# Lint
make lint