Skip to main content

Unofficial UniFi Protect Python API and CLI

CI Status Documentation Status Test coverage percentage CodSpeed Badge

Poetry Ruff pre-commit

PyPI Version Supported Python versions License


Documentation: https://uiprotect.readthedocs.io

Source Code: https://github.com/uilibs/uiprotect


About

Unofficial Python API and CLI for UniFi Protect, for any UniFi OS console that runs Protect. It is the library behind the Home Assistant UniFi Protect integration and can be used on its own. uiprotect is not affiliated with or endorsed by Ubiquiti.

The library is moving to Ubiquiti's documented Public Integration API, which authenticates with an API key. Features the public API does not offer use the undocumented private API, which can change between Protect releases.

Reporting issues and requesting features

Check the public API first

The Public Integration API documentation lists every device field and control the library can support. If a field or control is not there, uiprotect cannot add it as a new feature.

A field is not enough when only some models have the feature: there also has to be a feature flag (under featureFlags in the output) or another field that tells which devices support it. Otherwise Home Assistant would offer controls that do nothing on the other models.

To see what your console returns, query it with curl and an API key. Replace HOST with the IP address or hostname of your console and API_KEY with your key. The command saves the answer to cameras.json in the current folder.

Linux and macOS:

curl -k -H "X-API-KEY: API_KEY" -o cameras.json https://HOST/proxy/protect/integration/v1/cameras

Windows:

curl.exe -k -H "X-API-KEY: API_KEY" -o cameras.json https://HOST/proxy/protect/integration/v1/cameras

For other devices, replace cameras at the end of the URL with another endpoint from the documentation, such as lights, sensors or cameras/CAMERA_ID for a single camera, using its id from cameras.json. Doorbells are cameras. -k skips certificate verification. It is needed while the console uses its default self-signed certificate.

If the file only contains an error, check the key and HOST. Otherwise open it in a text editor and remove MAC addresses, IP addresses and serial numbers before you post it. Never post the command itself, it contains your API key.

Home Assistant diagnostics are no substitute. Their public_bootstrap section only contains fields uiprotect already reads, so a field the library does not know yet is missing there. The bootstrap section is the private API.

Requirements

  • UniFi Protect 7.2+, tested against the latest stable release
  • Python 3.11+
  • PyAV, installed as a dependency, for talkback audio

Windows is not actively supported and not tested in CI, although it should work. Issues about Windows are welcome as a hint, but there is no promise of a fix. The Docker container and WSL are the supported options there.

Installation

From PyPI

pip install uiprotect

To use the command-line interface, install the cli extra:

pip install "uiprotect[cli]"

From GitHub

pip install "uiprotect[cli] @ git+https://github.com/uilibs/uiprotect.git"

Using Docker Container

The Docker container needs no local Python. Add this to your .bashrc or similar. It passes the environment variables from your shell into the container:

function uiprotect() {
    docker run --rm -it \
      -e UFP_API_KEY \
      -e UFP_USERNAME \
      -e UFP_PASSWORD \
      -e UFP_ADDRESS \
      -e UFP_PORT \
      -e UFP_SSL_VERIFY \
      -e TZ \
      -v $PWD:/data ghcr.io/uilibs/uiprotect:latest "$@"
}
  • Update with docker pull ghcr.io/uilibs/uiprotect:latest.
  • $PWD is mounted to /data. Files written anywhere else are lost when the container exits.
  • Images exist for linux/amd64 and linux/arm64, so Docker Desktop on macOS and Windows works.
  • TZ sets the time zone the CLI uses for local times. Use the time zone name your Protect console uses. The command line docs list the other environment variables.

Authentication

API key

Create an API key on the console under Settings > Control Plane > Integrations with an administrator account. Ubiquiti's Getting Started guide describes it under "Obtaining an API Key". There is no read-only key, so treat it like a password.

Local user

Features the public API does not offer also need a local user with access to Protect.

Quickstart

CLI

export UFP_ADDRESS=YOUR_CONSOLE_IP
export UFP_API_KEY=YOUR_API_KEY
# set to true if the console has a certificate your machine trusts
export UFP_SSL_VERIFY=false

uiprotect cameras list-ids

In Windows PowerShell, set the variables with $env:UFP_API_KEY = "YOUR_API_KEY" and so on.

With only an API key, the CLI runs in public-only mode, and commands without a public equivalent, such as uiprotect nvr, stop with an error. With a local user set as well, the CLI logs in and the device commands read the private API instead:

export UFP_USERNAME=YOUR_USERNAME
export UFP_PASSWORD=YOUR_PASSWORD

uiprotect --help lists all commands and uiprotect <command> --help their options. The command line docs cover them in detail.

Python

The library is async. Its entry point is uiprotect.ProtectApiClient. A client with only an API key uses the public API:

import asyncio

from uiprotect import ProtectApiClient, ProtectDeviceChange


def on_device(change: ProtectDeviceChange) -> None:
    print(change.change, change.device_id, sorted(change.changed_fields))


async def main() -> None:
    protect = ProtectApiClient.public_only(
        "YOUR_CONSOLE_IP", 443, "YOUR_API_KEY", verify_ssl=False
    )
    try:
        await protect.subscribe_devices_and_prime(on_device)
        for camera in protect.public_bootstrap.cameras.values():
            print(camera.id, camera.name)
        await asyncio.sleep(60)
    finally:
        await protect.async_disconnect_ws()
        await protect.close_session()


asyncio.run(main())

In this mode the private entry points (update(), authenticate(), get_bootstrap()) raise PublicOnlyModeError. A rejected key raises NotAuthorized on HTTP calls and turns the websockets to WebsocketState.AUTH_FAILED.

For features that still need the private API, pass the local user as well and call both update() and update_public():

protect = ProtectApiClient(
    "YOUR_CONSOLE_IP",
    443,
    "YOUR_USERNAME",
    "YOUR_PASSWORD",
    api_key="YOUR_API_KEY",
    verify_ssl=False,
)

Subscriptions

subscribe_events delivers typed (ProtectEvent, EventChange) pairs for detection, sensor, alarm hub and access events. subscribe_devices delivers a ProtectDeviceChange for each added, updated or removed device. Devices that already exist are not announced, read them from public_bootstrap after priming. Both need a prior update_public(). subscribe_events_and_prime() and subscribe_devices_and_prime() do both in the right order.

Callbacks are plain functions called on the event loop. They must not block and must not be async def. Exceptions in a callback are logged and swallowed.

The usage docs describe events, device changes, reconnects and the other public API features in detail.

Contributing

Please open an issue and agree on the approach before implementing anything.

Using AI is fine, but you have to drive it. We use AI tools ourselves, so an unreviewed AI-generated PR or issue only moves the review work to us. AI-assisted contributions are welcome when you understand the architecture (AGENTS.md) and the approach was agreed in an issue first. Anything else will be closed.

What helps most is what AI cannot supply: tests on devices we do not own, sanitized payloads from them, and first-hand knowledge of how they behave.

Developer Setup

The recommended way to develop is using the provided devcontainer with VS Code:

  1. Install VS Code and the Dev Containers extension
  2. Open the project in VS Code
  3. When prompted, click "Reopen in Container" (or use Command Palette: "Dev Containers: Reopen in Container")
  4. The devcontainer will automatically set up Python, Poetry, pre-commit hooks, and all dependencies

Alternatively, if you want to develop natively without devcontainer:

# Install dependencies (--all-extras installs the cli extra for CLI tests)
poetry install --with dev --all-extras

# Install pre-commit hooks
poetry run pre-commit install --install-hooks

# Run tests
poetry run pytest

# Run pre-commit checks manually
poetry run pre-commit run --all-files

History

This project was split off from pyunifiprotect because that project changed its license to one that would not be accepted in Home Assistant. This project is committed to keeping the MIT license.

Credits

  • Bjarne Riis (@briis) for the original pyunifiprotect package
  • Christopher Bailey (@AngellusMortis) for maintaining the pyunifiprotect package

Metadata

Release files for uiprotect 23.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for uiprotect 23.2.0
File Size Uploaded
uiprotect-23.2.0.tar.gz 196.9 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for uiprotect 23.2.0
File Interpreter ABI Platform
uiprotect-23.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 415.5 kB

Release files / uiprotect-23.2.0.tar.gz

Download URL uiprotect-23.2.0.tar.gz
Size 196.9 kB
Tags Source
SHA-256 checksum
How to use checksums
e6389028ec24807667a1dfab730d5defb3f5a20401dbc9080150a9274e8121b0
BLAKE2b-256 checksum
How to use checksums
45d68b57cddde7533605f423382cb50af83dedbd9ab3420ac37a7991268e572b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 18, 2026.

Transparency log

Release files / uiprotect-23.2.0-py3-none-any.whl

Download URL uiprotect-23.2.0-py3-none-any.whl
Size 218.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
857de4c1bc20a2bbe343aad03711552adf2f53b9c32f9435b1d918960de96311
BLAKE2b-256 checksum
How to use checksums
55c941fedfdb3d2e61e06a0cb579abfab496dad8e21098e4adb3534e1ebb2996
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 18, 2026.

Transparency log

Release history Release notifications | RSS feed

23.3.0

2 release files

23.2.2

2 release files

23.2.1

2 release files

This release

23.2.0 This release

2 release files

23.1.3

2 release files

23.1.2

2 release files

23.1.1

2 release files

23.1.0

2 release files

17.7.0

2 release files

17.6.0

2 release files

17.5.0

2 release files

17.4.2

2 release files

17.4.1

2 release files

17.4.0

2 release files

17.3.1

2 release files

17.3.0

2 release files

17.2.0

2 release files

17.1.1

2 release files

17.1.0

2 release files

17.0.1

2 release files

17.0.0

2 release files

16.6.1

2 release files

16.6.0

2 release files

16.5.0

2 release files

16.4.0

2 release files

16.3.0

2 release files

16.2.0

2 release files

16.1.0

2 release files

16.0.0

2 release files

15.9.0

2 release files

15.8.0

2 release files

15.7.1

2 release files

15.7.0

2 release files

15.6.0

2 release files

15.3.0

2 release files

15.2.0

2 release files

15.1.0

2 release files

15.0.0

2 release files

14.3.0

2 release files

14.2.0

2 release files

14.1.1

2 release files

14.1.0

2 release files

14.0.0

2 release files

13.5.1

2 release files

13.5.0

2 release files

13.4.0

2 release files

13.3.0

2 release files

13.2.0

2 release files

13.1.2

2 release files

13.1.1

2 release files

13.1.0

2 release files

13.0.0

2 release files

10.8.0

2 release files

10.7.0

2 release files

10.6.0

2 release files

10.5.1

2 release files

10.5.0

2 release files

10.4.1

2 release files

10.4.0

2 release files

10.3.1

2 release files

10.3.0

2 release files

10.2.6

2 release files

10.2.5

2 release files

10.2.4

2 release files

10.2.3

2 release files

10.2.2

2 release files

10.2.1

2 release files

10.2.0

2 release files

10.1.0

2 release files

10.0.1

2 release files

10.0.0

2 release files

9.0.0

2 release files

8.1.1

2 release files

8.0.0

2 release files

7.33.3

2 release files

7.33.0

2 release files

7.32.0

2 release files

7.31.0

2 release files

7.30.1

2 release files

7.30.0

2 release files

7.29.0

2 release files

7.28.0

2 release files

7.27.0

2 release files

7.26.0

2 release files

7.25.0

2 release files

7.24.0

2 release files

7.23.0

2 release files

7.22.0

2 release files

7.21.1

2 release files

7.20.0

2 release files

7.19.0

2 release files

7.18.1

2 release files

7.18.0

2 release files

7.17.0

2 release files

7.16.0

2 release files

7.15.1

2 release files

7.15.0

2 release files

7.14.1

2 release files

7.14.0

2 release files

7.11.0

2 release files

7.10.1

2 release files

7.10.0

2 release files

7.9.2

2 release files

7.9.1

2 release files

7.9.0

2 release files

7.8.0

2 release files

7.7.0

2 release files

7.6.1

2 release files

7.6.0

2 release files

7.5.6

2 release files

7.5.5

2 release files

7.5.4

2 release files

7.5.3

2 release files

7.5.2

2 release files

7.5.1

2 release files

7.5.0

2 release files

7.4.1

2 release files

7.4.0

2 release files

7.3.0

2 release files

7.2.0

2 release files

7.1.0

2 release files

7.0.2

2 release files

7.0.1

2 release files

7.0.0

2 release files

6.8.0

2 release files

6.7.0

2 release files

6.6.5

2 release files

6.6.4

2 release files

6.6.3

2 release files

6.6.2

2 release files

6.6.1

2 release files

6.6.0

2 release files

6.5.0

2 release files

6.4.0

2 release files

6.3.2

2 release files

6.3.1

2 release files

6.3.0

2 release files

6.2.0

2 release files

6.1.0

2 release files

6.0.2

2 release files

6.0.1

2 release files

6.0.0

2 release files

5.4.0

2 release files

5.3.0

2 release files

5.2.2

2 release files

5.2.1

2 release files

5.2.0

2 release files

5.1.0

2 release files

5.0.0

2 release files

4.2.0

2 release files

4.1.0

2 release files

4.0.0

2 release files

3.8.0

2 release files

3.7.0

2 release files

3.6.0

2 release files

3.5.0

2 release files

3.4.0

2 release files

3.3.1

2 release files

3.3.0

2 release files

3.2.0

2 release files

3.1.9

2 release files

3.1.8

2 release files

3.1.7

2 release files

3.1.6

2 release files

3.1.5

2 release files

3.1.4

2 release files

3.1.3

2 release files

3.1.2

2 release files

3.1.1

2 release files

3.1.0

2 release files

3.0.0

2 release files

2.3.0

2 release files

2.2.0

2 release files

2.1.0

2 release files

2.0.0

2 release files

1.20.0

2 release files

1.19.3

2 release files

1.19.2

2 release files

1.19.1

2 release files

1.19.0

2 release files

1.18.1

2 release files

1.18.0

2 release files

1.17.0

2 release files

1.16.0

2 release files

1.15.0

2 release files

1.14.0

2 release files

1.13.0

2 release files

1.12.1

2 release files

1.12.0

2 release files

1.11.1

2 release files

1.11.0

2 release files

1.10.0

2 release files

1.9.0

2 release files

1.8.0

2 release files

1.7.2

2 release files

1.7.1

2 release files

1.7.0

2 release files

1.6.0

2 release files

1.5.0

2 release files

1.4.1

2 release files

1.4.0

2 release files

1.3.0

2 release files

1.2.2

2 release files

1.2.1

2 release files

1.2.0

2 release files

1.1.0

2 release files

1.0.1

2 release files

1.0.0

2 release files

0.15.1

2 release files

0.15.0

2 release files

0.14.0

2 release files

0.13.0

2 release files

0.12.0

2 release files

0.11.0

2 release files

0.10.1

2 release files

0.10.0

2 release files

0.9.0

2 release files

0.8.0

2 release files

0.7.0

2 release files

0.6.0

2 release files

0.5.0

2 release files

0.4.1

2 release files

0.4.0

2 release files

0.3.10

2 release files

0.3.9

2 release files

0.3.8

2 release files

0.3.7

2 release files

0.3.6

2 release files

0.3.5

2 release files

0.3.4

2 release files

0.3.3

2 release files

0.3.2

2 release files

0.3.1

2 release files

0.3.0

2 release files

0.2.2

2 release files

0.2.1

2 release files

0.2.0

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page