Command-line interface for the Data API.
Data API: Wistia Data API
curl -fsSL https://raw.githubusercontent.com/wistia/wistia-cli/main/scripts/install.sh | bashiwr -useb https://raw.githubusercontent.com/wistia/wistia-cli/main/scripts/install.ps1 | iexbrew install wistia/tap/wistia-cliDownload pre-built binaries for your platform from the releases page.
Shell completions are available for Bash, Zsh, Fish, and PowerShell.
# Add to ~/.bashrc:
source <(wistia completion bash)
# Or install permanently:
wistia completion bash > /etc/bash_completion.d/wistia# Add to ~/.zshrc:
source <(wistia completion zsh)
# Or install permanently:
wistia completion zsh > "${fpath[1]}/_wistia"wistia completion fish | source
# Or install permanently:
wistia completion fish > ~/.config/fish/completions/wistia.fishwistia completion powershell | Out-String | Invoke-Expressionwistia upload-or-import-media post-form --bearer-auth 'Bearer test_token' --url 'http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4' --low-priority=true
This CLI is built to be driven by AI coding agents as well as people: everything an agent needs is discoverable from the binary itself, and every command can be validated without credentials. Work down this ladder:
| Run | You get |
|---|---|
wistia --help, wistia account get --help |
Commands by category, runnable examples, flags |
wistia --usage, wistia account get --usage |
The command surface as machine-readable KDL: commands, aliases, flags, defaults, env vars, config keys |
wistia account update-brand-preload --schema |
The exact JSON Schema of the command's request body (all $refs bundled) — build a valid --body from it |
wistia account get --dry-run |
The exact HTTP request (method, URL, headers, body), with no credentials or network call |
wistia account get --output-format json (or --jq) |
Machine-readable output |
# Every command, flag, default, env var and config key, as KDL
wistia --usage
# One command's subtree only
wistia account get --usage--schema is available on every command that accepts a request body (--body, stdin, or a whole-body flag where the command has one), including intent commands. It prints the JSON Schema the request is validated against and exits without calling the API.
# JSON Schema (draft 2020-12) of the request body, with every $ref bundled under $defs
wistia account update-brand-preload --schemaStart quota-spending commands with --dry-run. It validates inputs, resolves the request, redacts secrets and binary payloads, makes no network call, and exits 0. It never reads the OS keychain; credentials supplied by flag, environment, or config file are included only as [REDACTED].
# Human preview: the [DRY-RUN] block is on stderr and stdout is empty
wistia account get --dry-run
# Machine preview: compact JSON on stdout and silent stderr
wistia account get --dry-run --output-format jsonThe machine form writes one object per would-be request, one per line (NDJSON for multi-request commands), with exactly this shape:
{"dry_run":true,"request":{"method":"POST","url":"https://…","headers":{"Accept":["application/json"],…},"body":<JSON value | string | null>}}body is a parsed JSON value when the body is JSON, a string for text, "<bytes:N>" for binary data, and null when absent. An explicit caller --jq also selects this JSON preview protocol, but the filter is not applied to preview objects. Command-declared jq presets do not select or filter the preview.
Local mutation commands make no request under --dry-run: instead of a preview they emit one {"dry_run":true,"local":true,"command":"…","message":"…"} object. select(.request) keeps only would-be requests; select(.local) keeps the local no-ops.
# JSON on stdout
wistia account get --output-format json
# Filter or reshape with a jq expression (always emits JSON, overrides --output-format)
wistia account get --jq '.'
# Print jq string results as plain text instead of JSON strings (like jq -r)
wistia account get --jq '.' --raw-output--output-format toon emits TOON, a compact line-oriented format that uses fewer tokens than JSON; it is the default in agent mode.
Required-input prompts and guided configure / auth login forms are enabled by default. Required-input prompts require an interactive terminal; off-TTY forms read line input from stdin. Use --no-interactive to force flag-only execution.
# Prompt for missing command inputs
wistia account get --interactive
# Open the guided configuration form
wistia configure --interactive
# Explicitly launch the terminal command explorer
wistia exploreAgent mode turns on automatically when a known agent environment is detected (CLAUDECODE, CURSOR_AGENT, CODEX, AIDER, CLINE, WINDSURF_AGENT, GITHUB_COPILOT, AMAZON_Q, GEMINI_CODE_ASSIST, SRC_CODY) or with --agent-mode (--agent-mode=false disables detection).
In agent mode interactive prompts never launch, output defaults to TOON, and every failure — API errors and CLI usage errors alike — is one JSON envelope on stderr:
Outside agent mode, explicit JSON and --jq preserve the compatibility envelope without classification; enable agent mode to request the classified contract.
{
"error": "...",
"error_type": "validation_error",
"error_reason": "CLI_VALIDATION",
"exit_code": 2,
"message": "human-readable message",
"hints": ["what to try next"]
}error_type is one of authentication_error, authorization_error, not_found, validation_error, rate_limit_error, server_error, api_error, connection_error, protocol_error, runtime_error, unsupported_error, async_failed, async_timeout, async_unknown_state. Classification derives from the HTTP status and transport evidence; error_reason is absent for API errors. Status-less local failures may use CLI_VALIDATION, CLI_CONNECTION, CLI_PROTOCOL, CLI_RUNTIME, CLI_UNAVAILABLE, CLI_AUTHENTICATION, or the async polling reasons CLI_ASYNC_FAILED, CLI_ASYNC_TIMEOUT, and CLI_ASYNC_UNKNOWN_STATE. hints preserves server guidance first, adds the most specific local taxonomy guidance, then typed CLI and command-specific guidance, removing exact duplicates. exit_code is always the code for the final error_type shown in the envelope: 1 runtime, 2 usage, or 3 authentication/authorization.
Authentication credentials can be configured in four ways (in order of priority):
Pass credentials directly as flags to any command:
wistia --bearer-auth "$WISTIA_CLI_BEARER_AUTH" account getSet credentials via environment variables:
| Variable | Description |
|---|---|
WISTIA_CLI_BEARER_AUTH |
HTTP Bearer |
Credentials are stored securely in your operating system's keychain when you run:
wistia configureSecret credentials (tokens, API keys, passwords) are automatically stored in:
- macOS: Keychain
- Linux: GNOME Keyring / KWallet (via D-Bus Secret Service)
- Windows: Windows Credential Locker
If no keychain is available (e.g., in CI environments), credentials fall back to the config file.
Run the interactive configure command to store non-secret settings:
wistia configureConfiguration is stored in ~/.config/wistia/config.yaml.
Available commands
upload-or-import-media- Operations for upload-or-import-mediapost-form- Upload or Import Mediapost-multipart- Upload or Import Media
push-devices- Operations for push-devicesreview-bundles- Operations for review-bundlescustom-metadata-field-definitions- Operations for custom-metadata-field-definitionsget- List Custom Metadata Field Definitionspost- Create Custom Metadata Field Definitionget-custom-metadata-field-definitions-key- Show Custom Metadata Field Definitionput-custom-metadata-field-definitions-key- Update Custom Metadata Field Definitiondelete-custom-metadata-field-definitions-key- Archive Custom Metadata Field Definitionpost-custom-metadata-field-definitions-key-restore- Restore Custom Metadata Field Definition
deleted-media- Operations for deleted-mediaget- List Deleted Mediapost-deleted-media-restore- Restore Deleted Media
media- Operations for medialist- List Mediaget- Show Mediaupdate- Update Mediadelete- Delete Mediacopy- Copy Mediaswap- Swap Mediaget-stats- Show Media Aggregated Statstranslate- Translate Mediaimport-url- Import Media from URLarchive- Archive Mediamove- Move Mediarestore- Restore Mediabulk-copy- Bulk Copy Media
customizations- Operations for customizationsget- Show Customizationscreate- Create Customizationsupdate- Update Customizationsdelete- Delete Customizationsget-appearance- Show Appearance Customizationsupdate-appearance- Update Appearance Customizationsget-playback- Show Playback Customizationsupdate-playback- Update Playback Customizationsget-thumbnail- Show Thumbnail Customizationsupdate-thumbnail- Update Thumbnail Customizationsget-accessibility- Show Accessibility Customizationsupdate-accessibility- Update Accessibility Customizationsget-chapters- Show Chapters Customizationsupdate-chapters- Update Chapters Customizationsget-engagement- Show Engagement Customizationsupdate-engagement- Update Engagement Customizationsget-related-media- Show Related Media Customizationsupdate-related-media- Update Related Media Customizationsget-sharing- Show Sharing Customizationsupdate-sharing- Update Sharing Customizationsget-lead-capture- Show Lead Capture Customizationsupdate-lead-capture- Update Lead Capture Customizationsget-access- Show Access Customizationsupdate-access- Update Access Customizations
share-links- Operations for share-linkscaptions- Operations for captionslist- List Captions by Mediacreate- Create Captionscreate-multipart- Create Captionslist-all- List Captionsfind-matches- Find Caption Matchespurchase- Purchase Captionsget- Show Captionsupdate- Update Captionsupdate-multipart- Update Captionsdelete- Delete Captionsedit- Edit Captions Text
speakers- Operations for speakerslocalizations- Operations for localizationscustom-metadata-field-values- Operations for custom-metadata-field-valuesget-medias-media-hashed-id- List Custom Metadata Field Valuesput-medias-media-hashed-id-custom-metadata-field-values-key- Set Custom Metadata Field Valuedelete-medias-media-hashed-id-custom-metadata-field-values-key- Clear Custom Metadata Field Valueget-medias-media-hashed-id-custom-metadata-field-values-key- Show Custom Metadata Field Value
trims- Operations for trimscreate- Create Media from Trims
media-extended-audio-descriptions- Operations for media-extended-audio-descriptionsget- List Media Extended Audio Descriptionsget-media-extended-audio-descriptions-id- Show Media Extended Audio Descriptiondelete-media-extended-audio-descriptions-id- Delete Media Extended Audio Descriptionpost-media-extended-audio-descriptions-order- Order Extended Audio Descriptionget-media-extended-audio-descriptions-order-status-id- Get Order Status
brands- Operations for brandstags- Operations for tagsbulk-actions- Operations for bulk-actionspost-bulk- Create Bulk Actions
bulk- Operations for bulkpurchase- Create Bulk Purchase
taggings- Operations for taggingsbulk-create- Bulk Tag Media
folders- Operations for foldersfolder-sharings- Operations for folder-sharingssubfolders- Operations for subfolderschannels- Operations for channelschannel-episodes- Operations for channel-episodeschannel-collaborators- Operations for channel-collaboratorswebinars- Operations for webinarswebinar-registrations- Operations for webinar-registrationsget-webinars-webinar-id-registrations- List Webinar Registrationscreate- Create Webinar Registration
webinar-collaborators- Operations for webinar-collaboratorsaccount- Operations for accountget- Get Current Accountget-usage- Get Account Usageget-credit-balance- Get Credit Balanceget-brand-preload- Get Brand Preloadupdate-brand-preload- Update Brand Preloadget-brand-kit-colors- Get Brand Kit Colorsget-token-details- Get Current Token
contacts- Operations for contactscreate- Invite Contacts
contact- Operations for contactdismiss-desktop-install-prompt- Dismiss Desktop Install Prompt
account-trials- Operations for account-trialscreate- Start Account Trial
search- Operations for searchsearch- Search
resource-urls- Operations for resource-urlsresolve- Resolve Resource URLs
expiring-access-tokens- Operations for expiring-access-tokenscreate- Create Expiring Access Token
background-job-status- Operations for background-job-statusget- Show Background Job Status
allowed-domains- Operations for allowed-domainsremix- Operations for remixpost-remixes- Create Remixget-remixes-remix-hashed-id- Get Remixpost-remixes-remix-hashed-id-continue- Continue Remixpost-remixes-remix-hashed-id-export- Export Remixget-remix-account-status- Get Remix Account Status
stats-account- Operations for stats-accountget- Show Current Account Statsget-stats-account-by-date- Show Account Stats by Date
stats-projects- Operations for stats-projectsget- Show Project Stats
stats-media- Operations for stats-mediaget- Show Media Statsget-by-date- Show Media Stats by Dateget-engagement- Show Media Engagement
stats-visitors- Operations for stats-visitorsstats-events- Operations for stats-eventsanalytics-account- Operations for analytics-accountget- Show Account Analyticsget-timeseries- Show Account Analytics Timeseriesget-top-content- Show Account Top Contentget-embed-locations- Show Account Embed Locationsfind-media-by-embed-location- Find Media By Embed Location
analytics-media- Operations for analytics-mediaget- Show Media Analyticsget-timeseries- Show Media Analytics Timeseriesget-embed-locations- Show Media Embed Locationsget-embed-locations-timeseries- Show Media Embed Locations Timeseriesget-traffic- Show Media Traffic Breakdownget-conversions- Show Media Form Conversionsget-languages- Show Media Languages
analytics-webinar- Operations for analytics-webinarget- Show Webinar Analyticsget-registration- Show Webinar Registration Timeseriesget-traffic- Show Webinar Traffic Breakdownget-audience- Show Webinar Audienceget-histograms- Show Webinar Histograms
Commands that accept a request body take it three ways, with a clear priority chain. The examples use wistia folders create; every body-bearing command works the same way.
Each top-level body field is a flag:
wistia folders create --name 'My New Folder' --admin-email 'admin@example.com'Provide the entire request body as a JSON string:
wistia folders create --body '{"name":"My New Folder","adminEmail":"admin@example.com","description":"My New Folder Description","public":false,"personalLibrary":false}'Individual flags override --body values:
# Sends {"name":"My New Folder (updated)","adminEmail":"admin@example.com","description":"My New Folder Description","public":false,"personalLibrary":false}
wistia folders create --body '{"name":"My New Folder","adminEmail":"admin@example.com","description":"My New Folder Description","public":false,"personalLibrary":false}' --name 'My New Folder (updated)'Pipe JSON into any command that accepts a request body:
echo '{"name":"My New Folder","adminEmail":"admin@example.com","description":"My New Folder Description","public":false,"personalLibrary":false}' | wistia folders createIndividual flags override stdin values:
# Sends {"name":"My New Folder (updated)","adminEmail":"admin@example.com","description":"My New Folder Description","public":false,"personalLibrary":false}
echo '{"name":"My New Folder","adminEmail":"admin@example.com","description":"My New Folder Description","public":false,"personalLibrary":false}' | wistia folders create --name 'My New Folder (updated)'This is useful for chaining commands, reading from files, or scripting:
# Read body from a file
wistia folders create < request.json
# Pipe from another command
curl -s https://example.com/request.json | wistia folders createWhen multiple input methods are used, the priority is:
| Priority | Source | Description |
|---|---|---|
| 1 (highest) | Individual flags | --name ... always wins |
| 2 | --body flag |
Whole-body JSON via flag |
| 3 (lowest) | Stdin | Piped JSON input |
Use --server-url to override the server URL entirely, bypassing any named or indexed server selection:
wistia --server-url https://custom-api.example.com account getPrecedence: --server-url > --server > default
Every command supports a --output-format flag that controls how the response is rendered to stdout.
| Format | Flag | Description |
|---|---|---|
| Pretty | --output-format pretty (default) |
Aligned key-value pairs with color, nested indentation. Human-readable at a glance. |
| JSON | --output-format json |
JSON output. Passthrough when the response is already JSON (preserves original field order and numeric precision). Falls back to typed marshaling otherwise. |
| YAML | --output-format yaml |
YAML output via standard marshaling. |
| Table | --output-format table |
Tabular output for array responses. |
| TOON | --output-format toon |
Token-Oriented Object Notation — a compact, line-oriented format that typically uses 30–60% fewer tokens than JSON. Well-suited for piping responses into LLM prompts. |
# Default pretty output
wistia account get
# Machine-readable JSON
wistia account get --output-format json
# TOON for LLM-friendly compact output
wistia account get --output-format toon
# Pipe JSON to jq without using --output-format
wistia account get --output-format json | jq '.'Use --jq to filter or transform the response inline using a jq expression. This always outputs JSON and overrides --output-format:
# Extract a single field
wistia account get --jq '.'
# Reshape with any jq program; --raw-output prints string results as plain text (like jq -r)
wistia account get --jq '.' --raw-outputUse --color to control terminal colors:
| Value | Behavior |
|---|---|
auto (default) |
Color when stdout is a TTY, plain text otherwise |
always |
Always colorize |
never |
Never colorize |
The NO_COLOR and FORCE_COLOR environment variables are also respected.
When using --all (pagination) or streaming operations, output is written incrementally as items arrive:
| Format | Streaming behavior |
|---|---|
json |
One compact JSON object per line (NDJSON) |
yaml |
YAML documents separated by --- |
toon |
One TOON-encoded object per block, separated by blank lines |
pretty (default) |
Pretty-printed items separated by blank lines |
The CLI uses standard exit codes to indicate success or failure:
| Exit Code | Meaning |
|---|---|
0 |
Success |
1 |
Runtime/API failure |
2 |
Usage or input failure |
3 |
Authentication or authorization failure |
On success, the response data is printed to stdout as JSON. On failure, error details are printed to stderr.
# Capture output and handle errors
wistia account get --output-format json > output.json 2> error.log
if [ $? -ne 0 ]; then
echo "Error occurred, see error.log"
fiThis CLI uses unclassified error rendering outside agent mode: pretty and TOON print the API error text as received, while --output-format json and --jq emit the unclassified envelope (including the configure _hint for HTTP 401/403) plus exit_code. Agent mode always emits the classified JSON envelope with exit_code, error_type, optional error_reason, message, hints, and optional status_code — see For AI agents.
The CLI includes two diagnostic flags available on all commands:
Preview what would be sent without making any network calls:
wistia account get --dry-runIn human output modes, stdout is empty and the [DRY-RUN] block goes to stderr. It includes:
- HTTP method and URL
- Request headers (sensitive values redacted)
- Request body preview (sensitive fields redacted)
With --output-format json, or with a caller-explicit --jq, stderr is silent and stdout is NDJSON: one compact preview object per would-be request. The jq filter is not applied, and command-declared jq presets do not select the JSON protocol.
{"dry_run":true,"request":{"method":"POST","url":"https://…","headers":{"Accept":["application/json"],…},"body":<JSON value | string | null>}}JSON bodies remain structured; text bodies are strings; binary bodies are "<bytes:N>"; absent bodies are null. Headers retain all values as arrays, with credentials replaced by [REDACTED]. Dry-run never reads the OS keychain, but credentials supplied by flag, environment, or config file still appear redacted. The command exits successfully without contacting the API.
Local mutation commands emit one {"dry_run":true,"local":true,"command":"…","message":"…"} object in place of a preview; filter with select(.request) or select(.local).
Log request and response diagnostics while running normally:
wistia account get --debugDebug output goes to stderr and includes:
- Request method, URL, headers, and body preview
- Response status, headers, and body preview
- Transport errors (if any)
The command still executes normally and produces its regular output on stdout.
If both --dry-run and --debug are set, --dry-run takes precedence and no network calls are made.
Sensitive information is automatically redacted in diagnostic output:
- Headers:
Authorization,Cookie,Set-Cookie,X-API-Key, and other security headers show[REDACTED] - Body: JSON fields named
password,secret,token,api_key,client_secret, etc. show[REDACTED] - Binary data: binary media and canonical base64 strings are replaced with
<bytes:N> - URL query: credential-like query parameters are replaced with
[REDACTED]
Diagnostic output should still be treated as potentially sensitive operational data.
While we value open-source contributions to this CLI, this library is generated programmatically. Any manual changes added to internal files will be overwritten on the next generation. We look forward to hearing your feedback. Feel free to open a PR or an issue with a proof of concept and we'll do our best to include it in a future release.