Library / SDK
krypton-byte/neonize avatar
krypton-byte/neonize

neonize: a WhatsApp automation library with a Go core under ctypes

A high-performance Python SDK for building real-time WhatsApp applications with a Go-powered core, event-driven APIs, and production-ready reliability.

760 stars141 forksPythonApache-2.0

At a glance

What is it?
neonize is a Python library for building real-time WhatsApp applications, with the multidevice protocol implemented in Go, compiled to a shared library and called from Python through ctypes, so there is no subprocess and no HTTP bridge in the path. It suits a developer who needs a typed event-driven client rather than a script that drives a browser, and it is the wrong choice if you need an official API.
Who is it for?
neonize fits a Python team that needs a real-time WhatsApp client with typed events, persistent sessions and both sync and async surfaces, and the ctypes bridge to a Go core is a defensible reason to pick it over driving a browser, because the protocol handling stays in a compiled language while your application code stays in Python.
Can I use it commercially?
Yes. Apache-2.0 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 8 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 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Why the Go core exists at all

neonize describes itself as a Python library for WhatsApp automation, and the architectural decision that defines it is stated in the first paragraph: a native Go core built on whatsmeow is compiled into a shared library and exposed to Python through a zero-copy FFI layer.

The reason to do that is on the table of reasons in the readme. The WhatsApp multidevice protocol runs in Go, called directly through ctypes, with no subprocesses and no HTTP bridges. That rules out two common designs. A subprocess means you are shipping a separate binary and talking to it over pipes, with serialisation at every boundary. An HTTP bridge means the same thing with a network stack in the middle. Both add a failure mode and a latency hop to a protocol that is already the hard part.

What is left is a shared library loaded into the Python process and called across the FFI boundary. The readme calls that zero-copy, and the claim is about the boundary rather than about the data: a Python string and a Go string are not the same memory, so the marshalling still happens, but there is no process boundary and no protocol serialisation.

The layer above it is the part a Python developer touches, and it is where the readme spends its detail. The API is fully typed and idiomatic, with sync and async clients that have identical surfaces. Sessions persist in SQLite or PostgreSQL, so you pair once and reconnect. Errors are a structured hierarchy, media operations have FFmpeg diagnostics, and the FFI calls have retry logic.

The protocol implementation itself, whatsmeow, is a separate project. neonize is the Python surface over it, plus the packaging work that makes the Go side shippable as a Python wheel.

The event model, and a first client in twenty lines

The client is event-driven, with 37 or more typed event classes, and the shape of a handler is a decorator.

The quick start looks like this:

python
from neonize.client import NewClient
from neonize.events import ConnectedEv, MessageEv

client = NewClient("session.db")


@client.event(ConnectedEv)
def on_connected(client: NewClient, _: ConnectedEv) -> None:
    print("Connected")


@client.event(MessageEv)
def on_message(client: NewClient, message: MessageEv) -> None:
    text = message.Message.conversation or message.Message.extendedTextMessage.text
    if text == "ping":
        client.reply_message("pong", message)


client.connect()

A few details in that snippet carry the design. The session is a file path passed to the constructor, so a NewClient with session.db is a persistent session and the file is the state. The handler receives the client and the event, and the event type in the decorator decides which events it sees. And the message body is a nested structure, which is why reading the text means checking message.Message.conversation and falling back to message.Message.extendedTextMessage.text.

That last point is the shape of the underlying protocol objects, and it is a fair representation of what you get: a typed event hierarchy that mirrors the wire format rather than a flattened convenience model. A helper exists for the common case, since the handling example uses extract_text from neonize.utils.message, and the project also has a neonize.utils.jid module for building and parsing the identifiers that address users and groups.

The async client mirrors this exactly. The async quick start imports from neonize.aioze.client and neonize.aioze.events, uses NewAClient, and the handlers are async functions. The difference in structure is that an async session needs an idle loop, so the example ends with await client.connect() and await client.idle(), wrapped in an asyncio.run call.

Media, groups and multi-session, and what each costs you

The feature set covers the messaging surface you would expect. Text, images, videos, documents, audio, voice notes and stickers can be sent and received, along with replies, reactions, polls, interactive buttons and lists, plus receipt and presence tracking. Groups can be created and managed by name, description, photo and participants, and newsletter or channel operations cover fetching messages, updating the profile and sending media. On the contact side there is profile picture retrieval, push name updates, typing and recording indicators, contact sync and blocklist management.

Building a message is separate from sending one, which is the API design worth noticing. The media example reads a file and turns it into a message object, then passes that object to send_message:

python
from neonize.utils.jid import build_jid

jid = build_jid("1234567890", "s.whatsapp.net")

# Text
client.send_message(jid, "Hello from Neonize")

# Image with caption
with open("photo.jpg", "rb") as f:
    msg = client.build_image_message(f.read(), caption="Photo", mime_type="image/jpeg")
    client.send_message(jid, message=msg)

The build_image_message and build_document_message calls take the bytes, a caption or filename, and a MIME type, so you control the metadata rather than letting the library guess it.

Groups follow the same discipline with identifiers. Participants are built as JIDs, the created group returns an object whose JID you then use for the follow-up calls, and the action passed to update_group_participants is a string such as "add":

python
from neonize.utils.jid import build_jid

# Create group
participants = [build_jid("1234567890"), build_jid("0987654321")]
info = client.create_group("Project Team", participants)
print(f"Group created: {info.JID}")

# Update settings
client.set_group_name(info.JID, "New Name")
client.set_group_description(info.JID, "Updated description")

# Manage participants
client.update_group_participants(info.JID, [build_jid("5555555555")], "add")

Multi-session support is a factory rather than a client, since one database can hold several paired devices. ClientFactory loads the existing sessions from the store, and a new account is paired with a uuid and new_device set to true. Handlers are registered on the factory, so one function sees events from every client, which is the only sensible arrangement when a business runs several numbers.

The cost of all of this is FFmpeg. It is listed as a prerequisite and required for media operations, specifically stickers, audio, video and thumbnails.

The packaging problem, and the goneonize build

Shipping a Go shared library inside a Python package is the hard part of this project, and the repository shows the machinery for it.

There is a separate top-level directory called goneonize, and the task runner in pyproject.toml has a build task with the help text build goneonize. There is a version task that sets the neonize and goneonize versions, a download task that fetches goneonize from a GitHub release at a specific version, and a repack task that generates a wheel for a specific operating system and architecture. The build step for the Go side is a separate artefact from the Python wheel, and the repack step is what combines them.

That is the right approach in principle. A Python package with a compiled dependency has a combinatorial packaging problem: the shared library has to match the platform, the architecture and the Python ABI, and the wheel filename has to encode all of it. Generating a wheel per platform and architecture is the standard answer, and the tooling here does exactly that.

The Python side is otherwise a conventional modern package. The project metadata requires Python 3.10 or newer, the version is dynamic, and the runtime dependencies are httpx, requests, linkpreview, phonenumbers, pillow, protobuf, python-magic on non-Windows with python-magic-bin on Windows, segno for QR codes, and tqdm. The linkpreview and phonenumbers entries are worth noting, because they are the two things a WhatsApp client tends to need and the pure protocol library does not provide: link previews for shared links and phone number normalisation.

The development tooling is in dependency groups rather than the main dependency list, which keeps the install lean. There is a dev group with python-semantic-release, mypy-protobuf, taskipy and types-requests, and a docs group with zensical, mkdocs-material, mkdocstrings and mike for versioned documentation. mike comes from a git source rather than a release, pinned to the squiffunk repository, so the docs toolchain follows upstream.

The documentation is on Read the Docs and is built with zensical, which reads the mkdocs.yml file, and there is a separate DOCUMENTATION_SETUP.md file in the repository root for the docs build. The examples/ directory holds seven runnable programs, including basic.py, async_basic.py, async_oneshot.py, interactive_async.py, multisession.py, multisession_async.py and paircode.py.

Where neonize is the wrong choice

The first limitation is not technical and it is the one to check first. WhatsApp operates an official API, the Cloud API, and the terms of service for automating a personal account are not the same as the terms for the official one. A library that connects to WhatsApp as a paired device through the multidevice protocol sits in the space between the official client and the official API, and whether your use is permitted depends on those terms rather than on anything in this repository. The topic list on the repository includes whatsapp-automation and whatsapp-bot, which describes the intended use, and the project is a library rather than a tool, so the responsibility for how it is used sits with whoever deploys it.

The second is the compiled dependency. If your deployment story is a pure Python environment, a pip install that pulls a platform-specific shared library built in Go, repacked per OS and architecture, is a different proposition from a wheel with no binary in it. The platform matrix in the repack task is the evidence: each combination needs its own artefact, and a platform the project does not build for is a platform you cannot install. The pyproject declares platform markers for python-magic, so Windows is a supported target, but the set of architectures is decided by what the release pipeline produces.

The third is the API surface being the protocol. The event classes mirror the wire format, so the convenience layer is thin, and every handler is coupled to the shape of WhatsApp's own objects. If WhatsApp changes a field, your handler breaks, and the type annotations do not protect you from that, they only describe the current shape.

The fourth is operational reality. A persistent session file is a credential equivalent, since anyone holding it can act as the paired device, and a service holding several of them needs the same care as a secrets store. The readme does not discuss key management for the session database, so that is a decision you have to make and document yourself.

The fifth is the version churn. The recent releases are 0.6.0 on 2026-10-15, 0.5.2 and 0.5.1 both on 2026-09-21, which is a project still finding its shape, and the version is dynamic and set by a taskipy command rather than being fixed in the manifest.

Editorial conclusion

neonize fits a Python team that needs a real-time WhatsApp client with typed events, persistent sessions and both sync and async surfaces, and the ctypes bridge to a Go core is a defensible reason to pick it over driving a browser, because the protocol handling stays in a compiled language while your application code stays in Python. It is the wrong choice if your use of WhatsApp is not permitted by Meta's terms, if you need a client for the official Cloud API, or if you need a Python-only build with no compiled artefact in the install path. Before adopting it, install FFmpeg and check that the shared library for your platform and architecture is available, since the wheel carries it per OS and architecture, and note that the last push was on 2026-10-15 with version 0.6.0 released the same day.

Frequently asked questions

How do I install neonize?

Run pip install neonize. The library requires Python 3.10 or newer and FFmpeg, which is needed for media operations such as stickers, audio, video and thumbnails.

What is the difference between the sync and async clients in neonize?

The sync client is NewClient from neonize.client with events from neonize.events, while the async one is NewAClient from neonize.aioze.client with events from neonize.aioze.events. Both expose the same API surface, and the async example additionally awaits client.idle() after connecting.

How does neonize handle multiple WhatsApp accounts?

Through ClientFactory, which keeps the sessions in one database. Existing sessions are loaded with get_all_devices, a new account is paired by passing a uuid with new_device set to true, and event handlers are registered on the factory so they see events from every client.

What is goneonize in the neonize repository?

It is the Go side of the library, built as a shared library and repacked into the Python wheel. The task runner has a build task for it, a version task that sets the neonize and goneonize versions, a download task that fetches it from a GitHub release, and a repack task that builds a wheel for a specific operating system and architecture.

What licence is neonize released under?

Apache-2.0, with the licence file at the root of the repository.

Official sources

  1. krypton-byte/neonize on GitHub
  2. License: Apache-2.0
  3. Project website
  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/krypton-byte-neonize.svg)](https://hysenlabs.com/projects/krypton-byte-neonize)

Was this useful?