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:

  1. CLI Output Mode (--output-mode json|ndjson) - Controls how birda communicates progress and results to stdout
  2. JSON File Format (-f json) - Writes detection results to .BirdNET.json files

CLI Output Mode

Use --output-mode to get structured JSON output instead of human-readable text.

Output Modes

ModeDescriptionUse Case
humanDefault. Progress bars, colors, human-readable textInteractive CLI use
jsonBuffered JSON array at completionSimple integrations, single result parsing
ndjsonNewline-delimited JSON, one event per lineStreaming, 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": { ... }
}
FieldTypeDescription
spec_versionstringAPI version for compatibility checking
timestampstringISO 8601 UTC timestamp
eventstringEvent type (see below)
payloadobjectEvent-specific data

Event Types

Pipeline Events (Analysis)

EventDescription
pipeline_startedAnalysis beginning, includes total files and model info
file_startedStarting to process a file
progressPeriodic progress update
file_completedFile finished (success, failed, or skipped)
pipeline_completedAll files processed, includes summary

Result Events (Commands)

EventDescription
resultCommand result with result_type discriminator
errorError occurred
cancelledOperation was cancelled

Result Types

The result event includes a result_type field:

Result TypeCommand
configbirda config show
model_listbirda models list
model_infobirda models info <id>
model_manifestbirda models manifest <id>
providersbirda providers
species_listbirda species
clip_extractionbirda 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_url and labels_url are resolved through HF_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, as regional/<region>/coverage.png.
  • region, region_name, group, group_name, and countries are absent on the global model, which is not a region. countries splits into core (wholly covered) and partial, each omitted when empty.
  • selection maps a hardware key to a variant id. A consumer need not evaluate these keys against the host: models install echoes 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 continue
  • warning - Operation continues with issues

Notes

  • Logs are written to stderr, JSON output to stdout - use 2>/dev/null to suppress logs
  • The spec_version field enables backwards-compatible API evolution
  • All timestamps are UTC in ISO 8601 format
  • File paths in output are absolute paths