aiortc: a readable WebRTC stack in pure Python, media stack aside
WebRTC and ORTC implementation for Python using asyncio
At a glance
- What is it?
- A Python implementation of WebRTC and ORTC built on asyncio, whose pitch is not raw performance but legibility: the ICE, DTLS, SRTP and SCTP layers are written out in Python you can read and modify.
- Who is it for?
- aiortc earns its place when you need media or data to pass through Python rather than past it. Running computer vision over video frames, terminating a WebRTC stream in a Python service, or building a signalling and data channel server all require the packets to be reachable in your own code, and that is exactly the boundary a browser or native library will not let you cross.
- Can I use it commercially?
- Yes. BSD-3-Clause 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 82 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 September 20, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The API mirrors the browser, with promises swapped for coroutines
The README states the design goal in two lines: the API closely follows its JavaScript counterpart while using pythonic constructs, with promises replaced by coroutines and events emitted through `pyee.EventEmitter`. That is a precise and useful promise to a reader who has written WebRTC in a browser. The object names you already know are the object names you get, and the programming model is asyncio rather than a callback API, so peer connections, media tracks and data channels all look familiar with the flow control changed.
The interesting part is what the mapping costs. JavaScript's WebRTC API is specified in terms of promises resolving at connection state changes, and asyncio coroutines are a close enough analogue that most documentation transfers. Where the model differs, the library leans on `pyee`, which appears in the dependency list as `pyee>=13.0.0`, so a subscription looks like the JavaScript event target rather than an asyncio queue.
Installation is the shortest section in the README:
pip install aiortcRequires Python 3.10 or newer, and the classifier list runs through 3.14. The package is marked production stable and OS independent, which is a claim worth taking seriously for a protocol stack that interoperates with real browsers.
What the dependency list tells you about the architecture
The dependencies in `pyproject.toml` map one to one onto the layers of the protocol, and reading them is the fastest way to understand what is implemented in Python and what is delegated.
dependencies = [
"aioice>=0.10.2,<1.0.0",
"av>=14.0.0,<18.0.0",
"cryptography>=44.0.0",
"google-crc32c>=1.1",
"pyee>=13.0.0",
"pylibsrtp>=0.10.0",
"pyopenssl>=25.0.0",
]`aioice` is the ICE implementation, a separate project by the same organisation, so candidate gathering and connectivity checking are handled rather than written here. `pylibsrtp` handles SRTP encryption and decryption for RTP and RTCP. `pyopenssl` plus `cryptography` cover DTLS key generation, certificates and the handshake, and `google-crc32c` is there for the checksum used in SCTP.
So the honest description is that transport security and connectivity are delegated to focused sibling libraries, while the protocol logic on top of them is Python. `av` is the exception and the important one. It is PyAV, the FFmpeg binding, and it is what makes this a media stack rather than a data channel library: encoding and decoding of Opus, PCMU, PCMA, VP8 and H.264 goes through FFmpeg rather than through hand written codecs. Read the code expecting to find protocol here, and expecting codecs to be somebody else's work.
The implementation status list is the honest feature summary
Rather than a marketing bullet list, the README gives a layer by layer inventory, and it is worth reading as a coverage map. SDP generation and parsing sit at the bottom. Interactive Connectivity Establishment is next, with half-trickle and mDNS support. Then DTLS key and certificate generation, the DTLS handshake with encryption and decryption for SCTP, and SRTP keying, encryption and decryption for RTP and RTCP.
Above that sits a pure Python SCTP implementation, which is the piece that makes data channels work at all, since SCTP is what carries them. Then data channels themselves, audio in Opus, PCMU and PCMA, video in VP8 and H.264, and bundling of audio, video and data over one transport. The last entry is RTCP reports including NACK and PLI for recovering from packet loss.
Read top to bottom, that list runs from signalling-adjacent plumbing to the media codecs, and it tells you where the sharp edges are. Nothing in the list covers a TURN relay server, which means connectivity checks rely on what the network gives you; half-trickle support helps, but a symmetric NAT behind a restrictive firewall is still a case worth testing before designing around it. The codec list, likewise, is a compatibility set rather than a complete one.
Interoperability against Chrome and Firefox is described as regularly tested, which is the statement that matters most for a WebRTC library.
Examples that show four different shapes of use
Seven example directories, and they are worth reading in a particular order because they build on each other.
`examples/datachannel-cli/` is the simplest: no media, just a data channel, which is the fastest way to confirm the stack works in your environment. `examples/datachannel-filexfer/` adds chunked transfer over that channel, which is where you find out how the library behaves under real backpressure. `examples/datachannel-vpn/` is the one to notice, because it forwards arbitrary IP traffic through data channels, effectively using the library as a tunnel.
`examples/webcam/` covers the local media case, sending your own camera into the connection. `examples/videostream-cli/` covers the more interesting one, a stream that runs in your process and can be processed on the way through. `examples/server/` is the architecture the README names explicitly: a full server handling both signalling and data channels in one process, which is the shape most Python applications end up wanting.
`examples/janus/` is the interoperability case. Janus is a general purpose WebRTC server, so pairing the two is how you check your client against something other than a browser.
Together they cover the two reasons the README gives for using aiortc at all: understanding how WebRTC works, and putting the Python ecosystem to work on media that passes through the process.
Type checking and lint rules that fit a protocol library
The mypy configuration in `pyproject.toml` is stricter than most projects adopt, and the settings make sense once you know what the code does:
[tool.mypy]
disallow_untyped_calls = true
disallow_untyped_decorators = true
disallow_untyped_defs = true
ignore_missing_imports = true
strict_optional = falseUntyped definitions, untyped calls and untyped decorators are all disallowed, so every function in a codebase where a wrong attribute name silently becomes a protocol bug is annotated. `ignore_missing_imports` is on because `av` and `pylibsrtp` do not ship complete stubs. `strict_optional` is off, which is the one relaxation, and `warn_redundant_casts` and `warn_unused_ignores` are both on to keep annotations honest rather than letting them accumulate.
Ruff handles the rest, with pycodestyle, Pyflakes, pycodestyle warnings and isort selected. The dev extra adds aiohttp, coverage with TOML support and numpy, and numpy in the dev dependencies tells you the test suite and examples process frames numerically.
Coverage is configured with the source set to the `aiortc` package and excludes only lines marked `pragma: no cover`. The README says a lot of effort went into the test suite, and a badge for it sits at the top of the page. Since this is a protocol implementation where an off-by-one in an SRTP sequence number stays invisible until it fails, that investment is the thing separating it from a weekend project.
Where the README ends and the documentation takes over
The README is short and unapologetic about it. It explains what aiortc is, why you might want it, gives the implementation status list, shows the install command, states the BSD licence and links to aiortc.readthedocs.io. There is no architecture diagram, no API tour and no configuration reference, which for a library this deep is a sensible division of labour rather than a gap.
The project metadata points at three separate documentation URLs: the readthedocs homepage, a stable changelog and the GitHub repository. The changelog link is the one to bookmark, given that this repository publishes no releases. Version is declared dynamic in `pyproject.toml`, meaning it is read from the package at build time rather than pinned in the project file.
The repository is not archived and the last push was on 2026-07-17. The listed author and lead is Jeremy Lainé, and the build uses a plain `setup.py` that calls `setuptools.setup()` with the real configuration in `pyproject.toml`, a split that is conventional and worth knowing if you package something that depends on it.
For an evaluation, the decisive questions are narrow and mostly answerable from the examples: does your use case need media frames inside Python, does it need data channels only, and are your peers on codecs from the VP8, H.264, Opus, PCMU and PCMA set. If the answers are yes, the stack is there. If you need a wide codec range or a bundled relay server, this is not the library for it.
Editorial conclusion
aiortc earns its place when you need media or data to pass through Python rather than past it. Running computer vision over video frames, terminating a WebRTC stream in a Python service, or building a signalling and data channel server all require the packets to be reachable in your own code, and that is exactly the boundary a browser or native library will not let you cross. Codec support is the honest limit: Opus and PCMU and PCMA for audio, VP8 and H.264 for video, which is enough for interoperability with browsers but not a full profile of every codec in circulation. Install it with `pip install aiortc`, work through `examples/datachannel-cli/` and `examples/server/` in the repository, and read the changelog at aiortc.readthedocs.io, since this repository publishes no releases of its own.
Frequently asked questions
What is aiortc used for?
aiortc is used when media or data has to pass through Python code rather than around it. The README names two cases directly: building a full server that handles both signalling and data channels, and applying computer vision algorithms to video frames using OpenCV. It is also positioned as a readable implementation for programmers who want to understand how WebRTC works or modify its internals.
How do I install aiortc?
The README's install section is a single command, `pip install aiortc`, and it requires Python 3.10 or newer. Dependencies installed alongside it include aioice for ICE, PyAV for media codecs, pylibsrtp for SRTP, pyopenssl and cryptography for DTLS, google-crc32c and pyee. No system packages beyond those Python dependencies are described in the README.
Is aiortc a pure Python implementation of WebRTC?
The protocol logic is Python, and the README calls the implementation fairly simple and readable, but the codec work is delegated. `av`, the FFmpeg binding, handles Opus, PCMU, PCMA, VP8 and H.264, while `pylibsrtp` handles SRTP and `aioice` handles ICE. SCTP, which data channels depend on, is described in the README as a pure Python implementation.
Official sources
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.
[](https://hysenlabs.com/projects/aiortc-aiortc)