Backwards-compatibility policy

websockets is intended for production use. Therefore, stability is a goal.

websockets also aims at providing the best API for WebSocket in Python.

While we value stability, we value progress more. When an improvement requires changing the API, we make the change and document it below.

When possible with reasonable effort, we preserve backwards-compatibility for five years after the release that introduced the change.

When a release contains backwards-incompatible API changes, the major version is increased, else the minor version is increased. Patch versions are only for fixing regressions shortly after a release.


In development


Version 9.0 moves or deprecates several APIs.

Aliases provide backwards compatibility for all previously public APIs.

  • Added compatibility with Python 3.9.

  • Added support for IRIs in addition to URIs.

  • Added close codes 1012, 1013, and 1014.

  • Raised an error when passing a dict to send().

  • Fixed sending fragmented, compressed messages.

  • Fixed Host header sent when connecting to an IPv6 address.

  • Fixed starting a Unix server listening on an existing socket.

  • Aligned maximum cookie size with popular web browsers.

  • Ensured cancellation always propagates, even on Python versions where CancelledError inherits Exception.

  • Improved error reporting.


November 1, 2019

  • Added compatibility with Python 3.8.


July 31, 2019

  • Restored the ability to pass a socket with the sock parameter of serve().

  • Removed an incorrect assertion when a connection drops.


July 21, 2019

  • Restored the ability to import WebSocketProtocolError from websockets.


July 7, 2019


Version 8.0 drops compatibility with Python 3.4 and 3.5.


Version 8.0 expects process_request to be a coroutine.

Previously, it could be a function or a coroutine.

If you’re passing a process_request argument to serve() or WebSocketServerProtocol, or if you’re overriding process_request() in a subclass, define it with async def instead of def.

For backwards compatibility, functions are still mostly supported, but mixing functions and coroutines won’t work in some inheritance scenarios.


Version 8.0 changes the behavior of the max_queue parameter.

If you were setting max_queue=0 to make the queue of incoming messages unbounded, change it to max_queue=None.


Version 8.0 deprecates the host , port , and secure attributes of WebSocketCommonProtocol.

Use local_address in servers and remote_address in clients instead of host and port.


Version 8.0 renames the WebSocketProtocolError exception to ProtocolError .

A WebSocketProtocolError alias provides backwards compatibility.


Version 8.0 adds the reason phrase to the return type of the low-level API read_response() .


  • send(), ping(), and pong() support bytes-like types bytearray and memoryview in addition to bytes.

  • Added ConnectionClosedOK and ConnectionClosedError subclasses of ConnectionClosed to tell apart normal connection termination from errors.

  • Added basic_auth_protocol_factory() to enforce HTTP Basic Auth on the server side.

  • connect() handles redirects from the server during the handshake.

  • connect() supports overriding host and port.

  • Added unix_connect() for connecting to Unix sockets.

  • Improved support for sending fragmented messages by accepting asynchronous iterators in send().

  • Prevented spurious log messages about ConnectionClosed exceptions in keepalive ping task. If you were using ping_timeout=None as a workaround, you can remove it.

  • Changed WebSocketServer.close() to perform a proper closing handshake instead of failing the connection.

  • Avoided a crash when a extra_headers callable returns None.

  • Improved error messages when HTTP parsing fails.

  • Enabled readline in the interactive client.

  • Added type hints (PEP 484).

  • Added a FAQ to the documentation.

  • Added documentation for extensions.

  • Documented how to optimize memory usage.

  • Improved API documentation.


November 1, 2018


websockets now sends Ping frames at regular intervals and closes the connection if it doesn’t receive a matching Pong frame.

See WebSocketCommonProtocol for details.


Version 7.0 changes how a server terminates connections when it’s closed with WebSocketServer.close() .

Previously, connections handlers were canceled. Now, connections are closed with close code 1001 (going away). From the perspective of the connection handler, this is the same as if the remote endpoint was disconnecting. This removes the need to prepare for CancelledError in connection handlers.

You can restore the previous behavior by adding the following line at the beginning of connection handlers:

def handler(websocket, path):
    closed = asyncio.ensure_future(websocket.wait_closed())
    closed.add_done_callback(lambda task: task.cancel())


Version 7.0 renames the timeout argument of serve() and connect() to close_timeout .

This prevents confusion with ping_timeout.

For backwards compatibility, timeout is still supported.


Version 7.0 changes how a ping() that hasn’t received a pong yet behaves when the connection is closed.

The ping — as in ping = await — used to be canceled when the connection is closed, so that await ping raised CancelledError. Now await ping raises ConnectionClosed like other public APIs.


Version 7.0 raises a RuntimeError exception if two coroutines call recv() concurrently.

Concurrent calls lead to non-deterministic behavior because there are no guarantees about which coroutine will receive which message.


  • Added process_request and select_subprotocol arguments to serve() and WebSocketServerProtocol to customize process_request() and select_subprotocol() without subclassing WebSocketServerProtocol.

  • Added support for sending fragmented messages.

  • Added the wait_closed() method to protocols.

  • Added an interactive client: python -m websockets <uri>.

  • Changed the origins argument to represent the lack of an origin with None rather than ''.

  • Fixed a data loss bug in recv(): canceling it at the wrong time could result in messages being dropped.

  • Improved handling of multiple HTTP headers with the same name.

  • Improved error messages when a required HTTP header is missing.


July 16, 2018


Version 6.0 introduces the Headers class for managing HTTP headers and changes several public APIs:

  • process_request() now receives a Headers instead of a http.client.HTTPMessage in the request_headers argument.

  • The request_headers and response_headers attributes of WebSocketCommonProtocol are Headers instead of http.client.HTTPMessage.

  • The raw_request_headers and raw_response_headers attributes of WebSocketCommonProtocol are removed. Use raw_items() instead.

  • Functions defined in the handshake module now receive Headers in argument instead of get_header or set_header functions. This affects libraries that rely on low-level APIs.

  • Functions defined in the http module now return HTTP headers as Headers instead of lists of (name, value) pairs.

Since Headers and http.client.HTTPMessage provide similar APIs, this change won’t affect most of the code dealing with HTTP headers.


  • Added compatibility with Python 3.7.


May 24, 2018

  • Fixed a regression in 5.0 that broke some invocations of serve() and connect().


May 22, 2018


Version 5.0 fixes a security issue introduced in version 4.0.

Version 4.0 was vulnerable to denial of service by memory exhaustion because it didn’t enforce max_size when decompressing compressed messages (CVE-2018-1000518).


Version 5.0 adds a user_info field to the return value of parse_uri() and WebSocketURI .

If you’re unpacking WebSocketURI into four variables, adjust your code to account for that fifth field.


  • connect() performs HTTP Basic Auth when the URI contains credentials.

  • Iterating on incoming messages no longer raises an exception when the connection terminates with close code 1001 (going away).

  • A plain HTTP request now receives a 426 Upgrade Required response and doesn’t log a stack trace.

  • unix_serve() can be used as an asynchronous context manager on Python ≥ 3.5.1.

  • Added the closed property to protocols.

  • If a ping() doesn’t receive a pong, it’s canceled when the connection is closed.

  • Reported the cause of ConnectionClosed exceptions.

  • Added new examples in the documentation.

  • Updated documentation with new features from Python 3.6.

  • Improved several other sections of the documentation.

  • Fixed missing close code, which caused TypeError on connection close.

  • Fixed a race condition in the closing handshake that raised InvalidState.

  • Stopped logging stack traces when the TCP connection dies prematurely.

  • Prevented writing to a closing TCP connection during unclean shutdowns.

  • Made connection termination more robust to network congestion.

  • Prevented processing of incoming frames after failing the connection.


November 2, 2017

  • Fixed issues with the packaging of the 4.0 release.


November 2, 2017


Version 4.0 drops compatibility with Python 3.3.


Version 4.0 enables compression with the permessage-deflate extension.

In August 2017, Firefox and Chrome support it, but not Safari and IE.

Compression should improve performance but it increases RAM and CPU use.

If you want to disable compression, add compression=None when calling serve() or connect().


Version 4.0 removes the state_name attribute of protocols.

Use instead of protocol.state_name.


  • WebSocketCommonProtocol instances can be used as asynchronous iterators on Python ≥ 3.6. They yield incoming messages.

  • Added unix_serve() for listening on Unix sockets.

  • Added the sockets attribute to the return value of serve().

  • Reorganized and extended documentation.

  • Aborted connections if they don’t close within the configured timeout.

  • Rewrote connection termination to increase robustness in edge cases.

  • Stopped leaking pending tasks when cancel() is called on a connection while it’s being closed.

  • Reduced verbosity of “Failing the WebSocket connection” logs.

  • Allowed extra_headers to override Server and User-Agent headers.


August 20, 2017

  • Renamed serve() and connect()’s klass argument to create_protocol to reflect that it can also be a callable. For backwards compatibility, klass is still supported.

  • serve() can be used as an asynchronous context manager on Python ≥ 3.5.1.

  • Added support for customizing handling of incoming connections with process_request().

  • Made read and write buffer sizes configurable.

  • Rewrote HTTP handling for simplicity and performance.

  • Added an optional C extension to speed up low-level operations.

  • An invalid response status code during connect() now raises InvalidStatusCode with a code attribute.

  • Providing a sock argument to connect() no longer crashes.


March 29, 2017

  • Ensured compatibility with Python 3.6.

  • Reduced noise in logs caused by connection resets.

  • Avoided crashing on concurrent writes on slow connections.


August 17, 2016

  • Added timeout, max_size, and max_queue arguments to connect() and serve().

  • Made server shutdown more robust.


April 21, 2016

  • Avoided a warning when closing a connection before the opening handshake.

  • Added flow control for incoming data.


December 25, 2015


Version 3.0 introduces a backwards-incompatible change in the recv() API.

If you’re upgrading from 2.x or earlier, please read this carefully.

recv() used to return None when the connection was closed. This required checking the return value of every call:

message = await websocket.recv()
if message is None:

Now it raises a ConnectionClosed exception instead. This is more Pythonic. The previous code can be simplified to:

message = await websocket.recv()

When implementing a server, which is the more popular use case, there’s no strong reason to handle such exceptions. Let them bubble up, terminate the handler coroutine, and the server will simply ignore them.

In order to avoid stranding projects built upon an earlier version, the previous behavior can be restored by passing legacy_recv=True to serve(), connect(), WebSocketServerProtocol, or WebSocketClientProtocol. legacy_recv isn’t documented in their signatures but isn’t scheduled for deprecation either.


  • connect() can be used as an asynchronous context manager on Python ≥ 3.5.1.

  • Updated documentation with await and async syntax from Python 3.5.

  • ping() and pong() support data passed as str in addition to bytes.

  • Worked around an asyncio bug affecting connection termination under load.

  • Made state_name attribute on protocols a public API.

  • Improved documentation.


November 18, 2015

  • Added compatibility with Python 3.5.

  • Refreshed documentation.


August 18, 2015

  • Added local_address and remote_address attributes on protocols.

  • Closed open connections with code 1001 when a server shuts down.

  • Avoided TCP fragmentation of small frames.


July 28, 2015

  • Improved documentation.

  • Provided access to handshake request and response HTTP headers.

  • Allowed customizing handshake request and response HTTP headers.

  • Added support for running on a non-default event loop.

  • Returned a 403 status code instead of 400 when the request Origin isn’t allowed.

  • Canceling recv() no longer drops the next message.

  • Clarified that the closing handshake can be initiated by the client.

  • Set the close code and reason more consistently.

  • Strengthened connection termination by simplifying the implementation.

  • Improved tests, added tox configuration, and enforced 100% branch coverage.


January 31, 2015


November 3, 2014

  • Improved compliance of close codes.


July 28, 2014

  • Added support for limiting message size.


April 26, 2014

  • Added host, port and secure attributes on protocols.

  • Added support for providing and checking Origin.


February 16, 2014


Version 2.0 introduces a backwards-incompatible change in the send(), ping(), and pong() APIs.

If you’re upgrading from 1.x or earlier, please read this carefully.

These APIs used to be functions. Now they’re coroutines.

Instead of:


you must now write:

await websocket.send(message)


  • Added flow control for outgoing data.


November 14, 2013

  • Initial public release.