JSON Output Guide
August 9, 2026 ยท View on GitHub
Birda supports structured JSON output for programmatic integration with GUIs, web applications, and automation scripts.
Overview
There are two JSON-related features:
- CLI Output Mode (
--output-mode json|ndjson) - Controls how birda communicates progress and results to stdout - JSON File Format (
-f json) - Writes detection results to.BirdNET.jsonfiles
CLI Output Mode
Use --output-mode to get structured JSON output instead of human-readable text.
Output Modes
| Mode | Description | Use Case |
|---|---|---|
human | Default. Progress bars, colors, human-readable text | Interactive CLI use |
json | Buffered JSON array at completion | Simple integrations, single result parsing |
ndjson | Newline-delimited JSON, one event per line | Streaming, real-time progress, GUI apps |
Basic Usage
# Get JSON output for any command
birda --output-mode json config show
birda --output-mode json models list
birda --output-mode json providers
# NDJSON for real-time streaming
birda --output-mode ndjson recording.wav
Environment Variable
Set the default output mode:
export BIRDA_OUTPUT_MODE=json
birda config show # Now outputs JSON by default
Configuration File
Set in config.toml:
[output]
default_format = "json" # or "ndjson" or "human"
JSON Envelope Format
All JSON output follows a consistent envelope structure:
{
"spec_version": "1.0",
"timestamp": "2025-01-11T12:34:56.789Z",
"event": "result",
"payload": { ... }
}
| Field | Type | Description |
|---|---|---|
spec_version | string | API version for compatibility checking |
timestamp | string | ISO 8601 UTC timestamp |
event | string | Event type (see below) |
payload | object | Event-specific data |
Event Types
Pipeline Events (Analysis)
| Event | Description |
|---|---|
pipeline_started | Analysis beginning, includes total files and model info |
file_started | Starting to process a file |
progress | Periodic progress update |
file_completed | File finished (success, failed, or skipped) |
pipeline_completed | All files processed, includes summary |
Result Events (Commands)
| Event | Description |
|---|---|
result | Command result with result_type discriminator |
error | Error occurred |
cancelled | Operation was cancelled |
Result Types
The result event includes a result_type field:
| Result Type | Command |
|---|---|
config | birda config show |
model_list | birda models list |
model_info | birda models info <id> |
model_manifest | birda models manifest <id> |
providers | birda providers |
species_list | birda species |
clip_extraction | birda clip |
Example: Real-Time Progress with NDJSON
For GUI applications that need real-time progress:
birda --output-mode ndjson recording.wav 2>/dev/null
Output (one JSON object per line):
{"spec_version":"1.0","timestamp":"...","event":"pipeline_started","payload":{"total_files":1,"model":"birdnet-v24","min_confidence":0.1}}
{"spec_version":"1.0","timestamp":"...","event":"file_started","payload":{"file":"recording.wav","index":0,"estimated_segments":100}}
{"spec_version":"1.0","timestamp":"...","event":"progress","payload":{"file":{"path":"recording.wav","segments_done":50,"segments_total":100,"percent":50.0}}}
{"spec_version":"1.0","timestamp":"...","event":"file_completed","payload":{"file":"recording.wav","status":"processed","detections":42,"duration_ms":1234}}
{"spec_version":"1.0","timestamp":"...","event":"pipeline_completed","payload":{"status":"success","files_processed":1,"files_failed":0,"total_detections":42,"duration_ms":1234,"realtime_factor":85.2}}
Example: Command Results
Config Show
birda --output-mode json config show
{
"spec_version": "1.0",
"timestamp": "2025-01-11T12:34:56.789Z",
"event": "result",
"payload": {
"result_type": "config",
"config_path": "/home/user/.config/birda/config.toml",
"config": {
"defaults": {
"model": "birdnet-v24",
"min_confidence": 0.1
},
"models": { ... }
}
}
}
Models List
birda --output-mode json models list
{
"spec_version": "1.0",
"timestamp": "2025-01-11T12:34:56.789Z",
"event": "result",
"payload": {
"result_type": "model_list",
"models": [
{
"id": "birdnet-v24",
"model_type": "birdnet-v24",
"is_default": true,
"path": "/home/user/.local/share/birda/models/birdnet.onnx",
"labels_path": "/home/user/.local/share/birda/models/labels.txt",
"registry_id": "birdnet-v24"
},
{
"id": "birdnet-v30-nordic",
"model_type": "birdnet-v30",
"is_default": false,
"path": "/home/user/.local/share/birda/models/birdnet-v3.0-preview3.1-nordic-fp16-b1.onnx",
"labels_path": "/home/user/.local/share/birda/models/birdnet-v3.0-preview3.1-nordic-labels-b1.txt",
"registry_id": "birdnet-v30",
"installed_version": "3.0-preview3.1",
"installed_build": 1,
"region": "nordic",
"variant": "fp16"
}
]
}
}
Install provenance (registry_id, installed_version, installed_build, region, variant) lets a consumer recover what a model was installed from, and detect when a newer build has superseded it, without parsing the id string. Each field is omitted when it does not apply: a model added with models add has no registry_id, and a global install has no region or variant. Feature-detect by field presence.
Model Manifest
birda --output-mode json models manifest birdnet-v30
A documented projection of one registry model, for building a region-aware model gallery. It lists every region and variant with class count, download size, resolved URLs, and the countries each region covers. A legacy single-file model (birdnet-v24) projects as one synthetic global variant, so a consumer never branches on an empty list.
{
"spec_version": "1.1",
"timestamp": "2025-01-11T12:34:56.789Z",
"event": "result",
"payload": {
"result_type": "model_manifest",
"manifest": {
"id": "birdnet-v30",
"name": "BirdNET v3.0",
"version": "3.0-preview3.1",
"build": 1,
"model_type": "birdnet-v30",
"license": {
"type": "CC-BY-NC-SA-4.0",
"url": "https://creativecommons.org/licenses/by-nc-sa/4.0/",
"commercial_use": false,
"attribution_required": true,
"share_alike": true
},
"default_variant": "fp32",
"selection": { "cuda": "fp16", "tensorrt": "fp16" },
"variants": [
{
"id": "fp32",
"group_order": 0,
"classes": 11560,
"size_bytes": 123456789,
"model_url": "https://huggingface.co/tphakala/BirdNET-v3.0-Models/resolve/main/full/birdnet-v3.0-preview3.1-fp32-b1.onnx",
"labels_url": "https://huggingface.co/tphakala/BirdNET-v3.0-Models/resolve/main/full/birdnet-v3.0-preview3.1-labels-b1.txt"
},
{
"id": "fp32",
"region": "amazonia",
"region_name": "Amazonia",
"group": "south-america",
"group_name": "South America",
"group_order": 3,
"classes": 809,
"size_bytes": 156000000,
"model_url": "https://huggingface.co/tphakala/BirdNET-v3.0-Models/resolve/main/regional/amazonia/birdnet-v3.0-preview3.1-amazonia-fp32-b1.onnx",
"labels_url": "https://huggingface.co/tphakala/BirdNET-v3.0-Models/resolve/main/regional/amazonia/birdnet-v3.0-preview3.1-amazonia-labels-b1.txt",
"countries": {
"core": ["Brazil", "Colombia", "Ecuador", "Peru", "Venezuela"],
"partial": ["Bolivia", "Panama"]
}
}
]
}
}
}
Notes:
model_urlandlabels_urlare resolved throughHF_ENDPOINT, so a mirror rewrite happens once in birda rather than being reimplemented by every consumer. The per-region coverage map lives beside the model in the same directory, asregional/<region>/coverage.png.region,region_name,group,group_name, andcountriesare absent on the global model, which is not a region.countriessplits intocore(wholly covered) andpartial, each omitted when empty.selectionmaps a hardware key to a variant id. A consumer need not evaluate these keys against the host:models installechoes the variant it resolved (region,variant,selection_reason) once the download completes.
Providers
birda --output-mode json providers
{
"spec_version": "1.0",
"timestamp": "2025-01-11T12:34:56.789Z",
"event": "result",
"payload": {
"result_type": "providers",
"providers": [
{"id": "cpu", "name": "CPU", "description": "CPU (always available)"},
{"id": "cuda", "name": "CUDA", "description": "CUDA (NVIDIA GPU acceleration)"}
]
}
}
Species List
birda --output-mode json species --lat 60.17 --lon 24.94 --week 24
{
"spec_version": "1.0",
"timestamp": "2025-01-11T12:34:56.789Z",
"event": "result",
"payload": {
"result_type": "species_list",
"lat": 60.17,
"lon": 24.94,
"week": 24,
"threshold": 0.03,
"species_count": 150,
"species": [
{"scientific_name": "Turdus merula", "common_name": "Eurasian Blackbird", "frequency": 0.92},
{"scientific_name": "Parus major", "common_name": "Great Tit", "frequency": 0.89}
]
}
}
Clip Extraction
birda --output-mode json clip results.csv -c 0.7
{
"spec_version": "1.0",
"timestamp": "2025-01-11T12:34:56.789Z",
"event": "result",
"payload": {
"result_type": "clip_extraction",
"output_dir": "clips",
"total_clips": 15,
"total_files": 1,
"clips": [
{
"source_audio": "recording.wav",
"scientific_name": "Turdus merula",
"confidence": 0.95,
"start_time": 12.0,
"end_time": 18.0,
"output_file": "clips/Turdus merula/recording_12.0-18.0.wav"
}
]
}
}
total_files counts only the detection files that were processed successfully. When one or more files fail, the payload carries a failed_files array, each entry { "file": <path>, "error": <message> }; the field is omitted entirely when every file succeeded, so an all-success payload is unchanged from earlier versions.
In ndjson mode birda clip also emits a per-file error event (severity warning) as each failure occurs; in json mode the output stays a single document, so the failures are conveyed only through failed_files. Its exit status reflects the batch outcome: it exits non-zero only when every detection file failed. A batch where at least one file was processed exits zero even if others failed, so a machine consumer should read failed_files to detect partial failures rather than relying on the exit code alone. A direct-extraction range that decodes no audio (past the end of the file, or too short to hold a frame) is an error, not an empty clip.
JSON Detection File Format
Use -f json to write detection results to JSON files:
birda -f json recording.wav
# Creates: recording.BirdNET.json
File Structure
{
"source_file": "recording.wav",
"analysis_date": "2025-01-11T12:34:56.789Z",
"model": "birdnet-v24",
"settings": {
"min_confidence": 0.1,
"overlap": 0.0,
"lat": 60.17,
"lon": 24.94,
"week": 24
},
"detections": [
{
"start_time": 0.0,
"end_time": 3.0,
"scientific_name": "Turdus merula",
"common_name": "Eurasian Blackbird",
"confidence": 0.95
}
],
"summary": {
"total_detections": 42,
"unique_species": 8,
"audio_duration_seconds": 3600.0
}
}
Integration Examples
Python
import subprocess
import json
# Get models list
result = subprocess.run(
["birda", "--output-mode", "json", "models", "list"],
capture_output=True, text=True
)
data = json.loads(result.stdout)
models = data["payload"]["models"]
for model in models:
print(f"{model['id']}: {model['model_type']}")
Node.js (Streaming NDJSON)
const { spawn } = require('child_process');
const readline = require('readline');
const birda = spawn('birda', ['--output-mode', 'ndjson', 'recording.wav']);
const rl = readline.createInterface({ input: birda.stdout });
rl.on('line', (line) => {
const event = JSON.parse(line);
switch (event.event) {
case 'pipeline_started':
console.log(`Processing ${event.payload.total_files} files...`);
break;
case 'progress':
if (event.payload.file) {
console.log(`Progress: ${event.payload.file.percent}%`);
}
break;
case 'file_completed':
console.log(`Found ${event.payload.detections} detections`);
break;
}
});
Shell Script
#!/bin/bash
# Parse species list JSON with jq
birda --output-mode json species --lat 60.17 --lon 24.94 --week 24 | \
jq -r '.payload.species[] | "\(.scientific_name): \(.frequency * 100 | floor)%"'
Error Handling
Errors are reported as JSON events:
{
"spec_version": "1.0",
"timestamp": "2025-01-11T12:34:56.789Z",
"event": "error",
"payload": {
"code": "file_not_found",
"severity": "fatal",
"message": "Audio file not found: recording.wav",
"suggestion": "Check that the file path is correct"
}
}
Error severities:
fatal- Operation cannot continuewarning- Operation continues with issues
Notes
- Logs are written to stderr, JSON output to stdout - use
2>/dev/nullto suppress logs - The
spec_versionfield enables backwards-compatible API evolution - All timestamps are UTC in ISO 8601 format
- File paths in output are absolute paths