Library / SDK
fronzbot/blinkpy avatar
fronzbot/blinkpy

blinkpy: a Python library for the Blink camera system

A Python library for the Blink Camera system

814 stars170 forksPythonMIT

At a glance

What is it?
blinkpy speaks to Blink cameras over the network protocol the vendor never documented, and exists mainly so that Home Assistant's Blink component has something to talk to. It handles login, two factor prompts, request throttling, and cached media, and it is a third party library with no affiliation to Blink or Immedia.
Who is it for?
blinkpy fits a Home Assistant user who wants Blink cameras in an automation and a Python developer who wants to script against the camera system directly, and who is comfortable with an unofficial client and an account that must handle a two factor prompt. It does not fit anyone who needs a supported vendor interface or a guaranteed protocol, and it does not fit a deployment that expects the bundled container image to run on the Python version the library itself requires.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 4 days ago.
What is it written in?
Mainly Python, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.

Editorial analysis

An unofficial client, and a library that exists for one consumer

blinkpy is a Python library for the Blink camera system, and the repository makes its purpose unusually clear: it was built with the intention of allowing easy communication with Blink camera systems, specifically to support the Blink component in Home Assistant. Everything else follows from that, including the shape of the API and the care taken around request pacing.

The protocol is not Blink's own. The README credits the original protocol hacking to MattTW and links to that earlier work, and the project describes itself as MIT licensed with a disclaimer that Blink Wire-Free is a trademark owned by Immedia Inc., and that the author is in no way affiliated with Blink or Immedia. Read that as an unofficial client, because it is one, and the practical consequences are that the server side can change without notice and there is no vendor support when it does.

The library is Python 3.10 and newer, and the packaging is modern: a pyproject.toml with a setuptools build backend, a version currently at 0.26.0b0, ruff configured with a broad lint rule set, and the runtime dependencies declared dynamically from a requirements.txt that lists aiohttp, aiofiles, requests, python-dateutil, python-slugify, and sortedcontainers.

The repository is laid out with the library itself in blinkpy, a sibling blinksync package, a blinkapp directory, a tests directory, and the usual support files including API.md, CHANGES.rst, tox.ini, pytest.ini, and a pylintrc.

Installing from pip or from the dev branch

The normal install is a single pip command, `pip install blinkpy`, and that is all the package index needs.

For the development version, the README gives a four step sequence that clones the repository into your home directory and installs it from the checkout.

bash
    $ cd ~
    $ git clone https://github.com/fronzbot/blinkpy.git
    $ cd blinkpy
    $ pip install .

Two details are worth noticing. The clone URL points at the repository root rather than a branch, and the default branch of the project is dev, so a fresh clone gives you the development line rather than a tagged release. The published releases on the package index are a separate stream, with v0.25.9 on 2026-07-21, v0.25.8 on 2026-07-20, and v0.25.7 on 2026-06-27.

The last push to the repository was on 2026-10-19, and the working version in the project metadata is a beta, 0.26.0b0, which tells you the development line is ahead of the published one.

Contributors are pointed at a CONTRIBUTING.rst in the repository, and the test infrastructure is pytest with tox, so a local change can be run against the same suite the project uses.

Starting the session prompts for credentials and throttles itself

The simplest use from a terminal is to construct a Blink object and start it, and the start call prompts for a username and password and then logs in.

python
    import asyncio
    from aiohttp import ClientSession
    from blinkpy.blinkpy import Blink

    async def start():
        blink = Blink(session=ClientSession())
        try:
            await blink.start()
        except BlinkTwoFARequiredError:
            await blink.prompt_2fa()
        return blink

    blink = asyncio.run(start())

Two mechanisms in that snippet are worth pulling apart. The session is an aiohttp ClientSession that you pass in, so the caller owns the HTTP connection pool and the event loop, which is the arrangement a Home Assistant integration needs. The two factor path is the try and except: if the account requires a pin, start raises BlinkTwoFARequiredError and the caller handles it by calling prompt_2fa.

The pin itself is sent to the email address on the account, and the README describes entering it at the prompt after which the library continues with setup.

The other mechanism is invisible in the snippet and is the most important line of documentation in the README. HTTP requests are throttled internally through a Blink.refresh_rate variable, which can be set at initialization and defaults to 30 seconds. On top of that, the README states plainly that API calls faster than 60 seconds is not recommended because it can overwhelm Blink's servers, and asks that the module be used responsibly. So there are two numbers in play, the 30 second internal default and the 60 second advisory, and the library is built to sit above the first and near the second.

Non-interactive sessions need a hand-built auth handler

A prompt is a problem in a service, a container, or a Home Assistant restart. The README has a documented path for that case, and it starts by setting Blink.auth.no_prompt to True, which stops the library asking, and then requires you to supply the login data yourself by instantiating your own auth handler with a dictionary that contains at least a username and a password.

python
    import asyncio
    from aiohttp import ClientSession
    from blinkpy.blinkpy import Blink
    from blinkpy.auth import Auth

    async def start():
        blink = Blink(session=ClientSession())
        # Can set no_prompt when initializing auth handler
        auth = Auth({"username": <your username>, "password": <your password>}, no_prompt=True)
        blink.auth = auth
        try:
            await blink.start()
        except BlinkTwoFARequiredError:
            await blink.prompt_2fa()
        return blink

Since no pin is ever typed at a prompt, the two factor step has to be driven by the application. The README is explicit that you must call the send_auth_key function on the auth handler, passing the blink object and the key you received from Blink, and then complete setup.

python
    await auth.send_auth_key(blink, <your key>)
    await blink.setup_post_verify()

The ordering matters, because the key call is what proves the second factor, and setup_post_verify is what finishes the login afterwards.

One wrinkle to plan for: the two factor flow still needs a human somewhere in the loop, because the pin arrives by email. A fully unattended deployment therefore needs the saved credential route rather than the non-prompt route, since saving a credential is what removes the second factor from later sessions.

Loading and saving credentials, and what the saved file holds

Credentials can also come from a file. The README states that such a file must be JSON formatted and must contain at least a username and a password, and it points at a helper in the blinkpy.helpers.util module for loading it.

python
    import asyncio
    from aiohttp import ClientSession
    from blinkpy.blinkpy import Blink
    from blinkpy.auth import Auth
    from blinkpy.helpers.util import json_load

    async def start():
        blink = Blink()
        auth = Auth(await json_load("<File Location>"))
        blink.auth = auth
        try:
            await blink.start()
        except BlinkTwoFARequiredError:
            await blink.prompt_2fa()
        return blink

The reverse operation is a single call, and it is the part worth understanding before you use it.

python
    await blink.save("<File location>")

What gets written is not just the password. The saved information includes authentication tokens as well as unique ids, and the README says that this should allow a more streamlined experience and limit the frequency of login requests. That is the point of the feature, since every new login is another authentication flow and another possible two factor prompt. The cost is that the file on disk now holds credentials that can be replayed, so it belongs in the same category as a password file: readable only by the account that runs the process, and excluded from any repository or backup that is not yours.

Once the file exists, the flow above loads it with json_load and the rest of the session proceeds as it would with a prompt.

Cameras hang off sync modules, and media arrives as bytes

The object model is two levels deep. Cameras are instantiated as individual BlinkCamera classes inside a BlinkSyncModule instance, and all of your sync modules are stored in the blink.sync dictionary, keyed by the sync module name, which is the name shown in the Blink app.

python
    for name, camera in blink.cameras.items():
      print(name)                   # Name of the camera
      print(camera.attributes)      # Print available attributes of camera

The media is cached locally as bytes objects, reachable through internal variables.

python
    camera = blink.cameras['SOME CAMERA NAME']
    await blink.refresh(force=True)  # force a cache update USE WITH CAUTION
    camera.image_from_cache  # bytes-like image object (jpg)
    camera.video_from_cache  # bytes-like video object (mp4)

The refresh call is conditional. The README says it will only make a request if motion has been detected or other changes have been found, which is the behaviour you want for a camera that sits idle for days. The force flag overrides that internal request throttling, and the documentation marks it as something for debugging only, since it overrides the pacing the library otherwise applies.

There is also a remote trigger. The library can save images and videos to a file and can ask a camera to take a new picture on demand, and then refresh the server state afterwards.

python
    camera = blink.cameras['SOME CAMERA NAME']
    await camera.snap_picture()       # Take a new picture with the camera
    await blink.refresh()             # Get new information from server

The bundled container image and the library disagree on Python

The repository ships a Dockerfile for running the blinkapp directory as a container, and there is an inconsistency in it worth knowing before you build the image.

dockerfile
FROM python:3.7-alpine
LABEL maintainer="Kevin Fronczak <[email protected]>"

VOLUME /media

RUN python -m pip install --upgrade pip
RUN pip3 install blinkpy

COPY blinkapp/ .

ENTRYPOINT ["python", "./blinkapp.py"]
CMD []

The base image is Python 3.7 on Alpine, while the library itself declares requires-python of 3.10.0 or newer and states Python 3.10 and above in the README. The image therefore starts an interpreter older than the one the package claims to support, and the pip install of the published package inside it will pull whatever the index resolves for that interpreter. Treat the Dockerfile as a demonstration of how to run the example app rather than as a supported deployment path, and if you containerise your own integration, pick a base image that matches the library's own requirement.

The image also exposes a volume at /media, which is where the example app would put saved images and video.

On maintenance, the project is not archived and the last push was on 2026-10-19, with published releases v0.25.9 on 2026-07-21, v0.25.8 on 2026-07-20, and v0.25.7 on 2026-06-27. The licence is MIT, and the README asks for a coffee rather than a sponsorship. The trademark position is the part to keep in mind: Blink Wire-Free is a trademark of Immedia Inc, the author is not affiliated with them, and the original reverse engineering was done by someone else. Use it for your own account, and expect the protocol to be a moving target.

Editorial conclusion

blinkpy fits a Home Assistant user who wants Blink cameras in an automation and a Python developer who wants to script against the camera system directly, and who is comfortable with an unofficial client and an account that must handle a two factor prompt. It does not fit anyone who needs a supported vendor interface or a guaranteed protocol, and it does not fit a deployment that expects the bundled container image to run on the Python version the library itself requires. Verify first that your Blink account still accepts the login flow the library implements, that you set the refresh rate so you stay inside the rate the README warns about, and that you store the saved credential file somewhere only you can read, since it holds authentication tokens.

Frequently asked questions

How do I install blinkpy?

The documented install is pip install blinkpy. For the development version, clone the repository, which is on the dev branch by default, and run pip install . inside the checkout.

How does blinkpy handle two factor authentication?

When the account requires a pin, the start call raises BlinkTwoFARequiredError, and the pin is sent to the account email address. In an interactive session you call prompt_2fa, and in a non-interactive session you call send_auth_key on the auth handler with the key and then setup_post_verify.

What is the refresh rate of the blinkpy client?

Requests are throttled internally through the Blink.refresh_rate variable, which can be set at initialization and defaults to 30 seconds. The README also advises against API calls faster than 60 seconds because that can overwhelm Blink's servers.

How do I get images and video from a Blink camera with blinkpy?

The most recent media is available as bytes objects through the image_from_cache and video_from_cache attributes of a BlinkCamera, and a refresh updates them, though a refresh only makes a request if motion has been detected or something else changed. A forced refresh bypasses that throttling and is documented as a debugging option, and snap_picture asks the camera to take a new picture.

Can I store my Blink credentials for later use with blinkpy?

Yes, blink.save writes the credentials to a file, and the saved data includes authentication tokens and unique ids, which reduces the frequency of login requests. The file is loaded again with the json_load helper, and because it holds replayable credentials it should be readable only by the account that runs the process.

Official sources

  1. fronzbot/blinkpy on GitHub
  2. Issues
  3. License: MIT
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/fronzbot-blinkpy.svg)](https://hysenlabs.com/projects/fronzbot-blinkpy)

Was this useful?