GetWebP CLI JSON Output Reference
Machine-readable output schemas for every GetWebP CLI command.
GetWebP CLI JSON Output Reference
Machine-readable output schemas for every GetWebP CLI command. Use --json on any command to emit structured NDJSON on stdout (human messages go to stderr).
See also: Commands Reference | Exit Codes | CI Integration
Overview#
With --json, the CLI emits NDJSON (newline-delimited JSON) — one JSON object per line. Every line is an envelope:
interface Envelope {
'@timestamp': string // RFC3339 UTC with ms precision, e.g. "2026-04-12T10:00:00.123Z"
'@level': 'info' | 'warn' | 'error'
'@message': string // single-line human-readable summary
'@module': string // e.g. "getwebp.convert"
type: string // event discriminator (see per-command sections)
data: object // event payload (always present, {} if empty)
}Key conventions:
- Each line is valid JSON. The stream as a whole is not a JSON array.
- The first line of every
--jsoninvocation is always aversionpreamble. - Warnings and progress messages are written to stderr, never mixed into the NDJSON stream.
- Every command exits with a numeric exit code regardless of
--json. - Fields prefixed with
@are reserved for the envelope. Command-specific payload lives indata.
Version preamble (first line, every command)#
{"@timestamp":"2026-04-12T10:00:00.000Z","@level":"info","@message":"GetWebP CLI 1.3.0","@module":"getwebp.cli","type":"version","data":{"getwebp":"1.3.0","ui":"1"}}getwebp <path> --json#
The convert command emits a convert.completed event after all files have been processed, preceded by the version preamble. On the Free plan, when the file limit is exceeded, a convert.truncated event is emitted instead.
Event: convert.completed#
// data shape for convert.completed
interface ConvertCompletedData {
processed: number // total files attempted
total: number // same as processed
successCount: number // files converted successfully
failedCount: number // files that errored
results: FileResult[] // per-file details (see below)
}Event: convert.truncated (Free plan, limit hit)#
// data shape for convert.truncated
interface ConvertTruncatedData {
processed: number // files actually converted
skipped: number // files skipped due to Free plan limit
limit: number // plan file limit (20 for Free)
upgrade_url: string // "https://getwebp.com/pricing"
}Event: convert.failed (usage error)#
Emitted when the command cannot start (e.g. bad --format value, missing input).
interface ConvertFailedData {
code: string // machine-readable error code, e.g. "usage_error", "missing_input"
hint?: string // optional human-readable correction hint
}FileResult union#
Each element in the results array is one of three shapes:
// Successfully converted
interface SuccessResult {
file: string // input file path
outputPath: string // absolute path to the written output file (.webp or .avif)
originalSize: number // bytes
newSize: number // bytes
savedRatio: number // 0 to 1 (negative if output is larger than original)
saved: string // human-friendly percentage, e.g. "35.0%"
quality: number // actual quality used
qualityMode: 'auto' | 'fixed' // auto = SSIM binary search; fixed = --quality or AVIF
ssim?: number // SSIM score (auto mode only)
status: "success"
}
// Conversion failed
interface ErrorResult {
file: string
status: "error"
error: string // e.g. "Decode failed: Unsupported format"
}
// Skipped (--skip-existing or output-equals-input)
interface SkipResult {
file: string
status: "skipped"
reason: "existing"
}Examples#
All files succeed (Pro plan):
{"@timestamp":"2026-04-12T10:00:00.000Z","@level":"info","@message":"GetWebP CLI 1.3.0","@module":"getwebp.cli","type":"version","data":{"getwebp":"1.3.0","ui":"1"}}
{"@timestamp":"2026-04-12T10:00:01.234Z","@level":"info","@message":"Converted 2/2 files","@module":"getwebp.convert","type":"convert.completed","data":{"processed":2,"total":2,"successCount":2,"failedCount":0,"results":[{"file":"photos/hero.jpg","outputPath":"/abs/photos/hero.webp","originalSize":204800,"newSize":133120,"savedRatio":0.35,"saved":"35.0%","quality":82,"qualityMode":"auto","status":"success"},{"file":"photos/logo.png","outputPath":"/abs/photos/logo.webp","originalSize":51200,"newSize":20480,"savedRatio":0.6,"saved":"60.0%","quality":85,"qualityMode":"auto","status":"success"}]}}
Mixed success and failure:
{"@timestamp":"2026-04-12T10:00:00.000Z","@level":"info","@message":"GetWebP CLI 1.3.0","@module":"getwebp.cli","type":"version","data":{"getwebp":"1.3.0","ui":"1"}}
{"@timestamp":"2026-04-12T10:00:01.000Z","@level":"info","@message":"Converted 1/2 files","@module":"getwebp.convert","type":"convert.completed","data":{"processed":2,"total":2,"successCount":1,"failedCount":1,"results":[{"file":"photo.jpg","outputPath":"/abs/photo.webp","originalSize":204800,"newSize":133120,"savedRatio":0.35,"saved":"35.0%","quality":82,"qualityMode":"auto","status":"success"},{"file":"corrupt.png","status":"error","error":"Decode failed: Invalid PNG header"}]}}
AVIF output (--format avif):
{"@timestamp":"2026-04-12T10:00:00.000Z","@level":"info","@message":"GetWebP CLI 1.3.0","@module":"getwebp.cli","type":"version","data":{"getwebp":"1.3.0","ui":"1"}}
{"@timestamp":"2026-04-12T10:00:01.500Z","@level":"info","@message":"Converted 1/1 files","@module":"getwebp.convert","type":"convert.completed","data":{"processed":1,"total":1,"successCount":1,"failedCount":0,"results":[{"file":"photo.jpg","outputPath":"/abs/photo.avif","originalSize":204800,"newSize":98304,"savedRatio":0.52,"saved":"52.0%","quality":55,"qualityMode":"fixed","status":"success"}]}}
Free plan, file limit hit:
{"@timestamp":"2026-04-12T10:00:00.000Z","@level":"info","@message":"GetWebP CLI 1.3.0","@module":"getwebp.cli","type":"version","data":{"getwebp":"1.3.0","ui":"1"}}
{"@timestamp":"2026-04-12T10:00:05.000Z","@level":"warn","@message":"Processed 20/25 images — Free plan limit reached. 5 remaining.","@module":"getwebp.convert","type":"convert.truncated","data":{"processed":20,"skipped":5,"limit":20,"upgrade_url":"https://getwebp.com/pricing"}}
Usage error (bad --format):
{"@timestamp":"2026-04-12T10:00:00.000Z","@level":"info","@message":"GetWebP CLI 1.3.0","@module":"getwebp.cli","type":"version","data":{"getwebp":"1.3.0","ui":"1"}}
{"@timestamp":"2026-04-12T10:00:00.005Z","@level":"error","@message":"Unknown --format \"gif\". Supported: webp, avif","@module":"getwebp.convert","type":"convert.failed","data":{"code":"usage_error","hint":"Use --format webp or --format avif"}}
Missing input path:
{"@timestamp":"2026-04-12T10:00:00.000Z","@level":"info","@message":"GetWebP CLI 1.3.0","@module":"getwebp.cli","type":"version","data":{"getwebp":"1.3.0","ui":"1"}}
{"@timestamp":"2026-04-12T10:00:00.003Z","@level":"error","@message":"Please specify an input path","@module":"getwebp.convert","type":"convert.failed","data":{"code":"missing_input"}}
getwebp auth --json#
Schema#
// Success
interface AuthSuccessResponse {
success: true
data: {
message: string // e.g. "Activated — Pro plan unlocked"
}
}
// Failure
interface AuthErrorResponse {
success: false
status: "error"
error: "unknown_error"
message: string // server-provided error description
}Examples#
Successful activation:
{
"success": true,
"data": {
"message": "Activated — Pro plan unlocked"
}
}Failed activation:
{
"success": false,
"status": "error",
"error": "unknown_error",
"message": "Invalid license key"
}getwebp status --json#
The shape of the data object varies depending on the license state.
Schema#
// Free plan (never activated)
interface StatusFreeResponse {
success: true
data: {
version: string // e.g. "1.0.1"
mode: "free"
}
}
// Active license, online (full status from server)
interface StatusOnlineResponse {
success: true
data: {
version: string
mode: "pro"
licenseKeySuffix: string // last 4 characters, e.g. "A1B2"
expiresAt: string // ISO 8601, e.g. "2027-04-01T00:00:00.000Z"
devicesUsed: number
devicesLimit: number
}
}
// Active license, server cache (server returned data with cached flag)
interface StatusCachedResponse {
success: true
data: {
version: string
mode: "pro"
licenseKeySuffix: string
expiresAt: string
cached: true // indicates data is from local cache
}
}
// Token exists but server unreachable and no status cache (JWT-only fallback)
interface StatusOfflineResponse {
success: true
data: {
version: string
mode: "pro"
cached: true
expiresAt?: string // ISO 8601 (from JWT exp claim)
}
}Note on
cached: Thecachedfield is only present (and set totrue) when the data comes from a local cache rather than a live server response. When the server responds successfully,cachedis omitted anddevicesUsed/devicesLimitare included instead.
Examples#
Free plan:
{
"success": true,
"data": {
"version": "1.0.1",
"mode": "free"
}
}Active Pro license (online):
{
"success": true,
"data": {
"version": "1.0.1",
"mode": "pro",
"licenseKeySuffix": "A1B2",
"expiresAt": "2027-04-01T00:00:00.000Z",
"devicesUsed": 1,
"devicesLimit": 5
}
}Cached status (offline):
{
"success": true,
"data": {
"version": "1.0.1",
"mode": "pro",
"licenseKeySuffix": "X9Z7",
"expiresAt": "2027-04-01T00:00:00.000Z",
"cached": true
}
}JWT-only fallback (offline, no status cache):
{
"success": true,
"data": {
"version": "1.0.1",
"mode": "pro",
"cached": true,
"expiresAt": "2027-04-01T00:00:00.000Z"
}
}getwebp logout --json#
When --json is used, the interactive confirmation prompt is automatically skipped (same behavior as --force).
Schema#
// Success
interface LogoutSuccessResponse {
success: true
data: {
message: "Device unbound. Switched to Free plan."
}
}
// Failure
interface LogoutErrorResponse {
success: false
status: "error"
error: string // machine-readable code (see table below)
message: string // human-readable description
}Error codes#
error value | message | Retryable |
|---|---|---|
network_unreachable | Cannot reach server. Retry later or unbind via dashboard. | Yes |
not_activated | No active license on this device. | No |
device_not_found | Device not found. It may have already been unbound. | No |
invalid_token | Token is invalid or expired. | No |
| Other | Passed through from server | Varies |
Examples#
Successful logout:
{
"success": true,
"data": {
"message": "Device unbound. Switched to Free plan."
}
}Network failure:
{
"success": false,
"status": "error",
"error": "network_unreachable",
"message": "Cannot reach server. Retry later or unbind via dashboard."
}No active license:
{
"success": false,
"status": "error",
"error": "not_activated",
"message": "No active license on this device."
}Error Codes Summary#
Machine-readable error codes that appear in data.code (convert) or error (auth/logout/status):
| Code | Commands | Meaning | Retryable |
|---|---|---|---|
missing_input | convert | No input path specified | No |
usage_error | convert | Bad flag value (e.g. unknown --format) | No |
unknown_error | auth | Unclassified activation failure | Varies |
network_unreachable | logout | Cannot reach API server | Yes |
not_activated | logout | No active license on device | No |
device_not_found | logout | Device already unbound | No |
invalid_token | logout | Token expired or revoked | No |
jq Recipes#
The CLI emits NDJSON (one JSON object per line). Use grep or jq to select lines by event type, then parse the data field.
Convert command#
# Extract the convert.completed event
getwebp ./images --json | grep '"convert.completed"'
# Check if all conversions succeeded (failedCount == 0)
getwebp ./images --json \
| grep '"convert.completed"' \
| jq '.data.failedCount == 0'
# Get total bytes saved across all successful files
getwebp ./images --json \
| grep '"convert.completed"' \
| jq '[.data.results[] | select(.status == "success") | (.originalSize - .newSize)] | add'
# List failed files
getwebp ./images --json \
| grep '"convert.completed"' \
| jq '[.data.results[] | select(.status == "error") | .file]'
# Get output paths for all successfully converted files
getwebp ./images --json \
| grep '"convert.completed"' \
| jq '[.data.results[] | select(.status == "success") | .outputPath]'
# Get average compression ratio
getwebp ./images --json \
| grep '"convert.completed"' \
| jq '[.data.results[] | select(.status == "success") | .savedRatio] | add / length'
# Exit with error if any file failed
getwebp ./images --json \
| grep '"convert.completed"' \
| jq -e '.data.failedCount == 0' > /dev/null
# Check if Free plan limit was hit
getwebp ./images --json \
| grep '"convert.truncated"' \
| jq '.data.skipped'
# Extract file names and sizes as CSV
getwebp ./images --json \
| grep '"convert.completed"' \
| jq -r '.data.results[] | select(.status == "success") | [.file, .outputPath, .originalSize, .newSize, .saved] | @csv'Status command#
# Get current plan
getwebp status --json | grep '"status.reported"' | jq -r '.data.mode'
# Check if license is active (not free)
getwebp status --json | grep '"status.reported"' | jq '.data.mode != "free"'
# Get expiry date
getwebp status --json | grep '"status.reported"' | jq -r '.data.expiresAt // "N/A"'
# Check device usage
getwebp status --json | grep '"status.reported"' | jq '"\(.data.devicesUsed // "?") / \(.data.devicesLimit // "?")"'Auth and logout#
# Activate and check result
getwebp auth XXXX-XXXX-XXXX-XXXX --json | grep '"auth.completed"' | jq -e '.'
# Logout (--json skips confirmation automatically)
getwebp logout --json | grep '"logout.completed"' | jq -e '.'CI pipeline pattern#
#!/usr/bin/env bash
set -euo pipefail
getwebp ./src/images -o ./dist/images --quality 85 --json > /tmp/gw_out.ndjson
completed=$(grep '"convert.completed"' /tmp/gw_out.ndjson)
if [ -z "$completed" ]; then
echo "Conversion did not complete" >&2
cat /tmp/gw_out.ndjson >&2
exit 1
fi
failed=$(echo "$completed" | jq '.data.failedCount')
if [ "$failed" -gt 0 ]; then
echo "Conversion failed for $failed file(s):" >&2
echo "$completed" | jq -r '.data.results[] | select(.status == "error") | " \(.file): \(.error)"' >&2
exit 1
fi
total=$(echo "$completed" | jq -r '.data.total')
saved=$(echo "$completed" | jq '[.data.results[] | select(.status == "success") | (.originalSize - .newSize)] | add')
echo "Converted $total images, saved $saved bytes total"Stderr Warnings#
Regardless of --json, the CLI may emit warnings to stderr. These are not part of the NDJSON stream and should not be parsed. Examples:
warn: Free plan: max 20 files, 3s delay between each.
warn: No --output specified: converted files will be written next to source files.
warn: Conflict: a.png, a.jpg all map to a.webp — only the last processed will survive
warn: --format avif: auto-quality is not supported for AVIF. Using quality=55. Pass --quality <N> to override.
To suppress warnings in scripts, redirect stderr:
getwebp ./images --json 2>/dev/nullAI Agent Integration#
For LLMs and AI agents consuming GetWebP output:
- Always use
--jsonto get structured NDJSON on stdout. - Each line is an independent JSON object — parse line-by-line, not as a JSON array.
- The first line is always a
versionpreamble (type"version"). Skip it or use it to confirm CLI version. - Find the line with
"type":"convert.completed"to get the full result summary in.data. - Check
data.failedCount == 0for full success. - If
"convert.truncated"appears instead of"convert.completed", the Free plan file limit was hit — checkdata.skipped. - Per-file errors are in
data.results[].error— human-readable strings, not error codes. data.results[].outputPathcontains the absolute path of each generated file (.webpor.avif).- Combine
--jsonwith exit codes for robust error handling: exit code0= full success, non-zero = some failure.