diff --git a/client-py/client-py b/client-py/client-py
new file mode 120000
index 0000000000000000000000000000000000000000..59b9c1929bc9c08625d623096ae3aef5d9f3eda6
--- /dev/null
+++ b/client-py/client-py
@@ -0,0 +1 @@
+client.py
\ No newline at end of file
diff --git a/client-py/client.py b/client-py/client.py
new file mode 100755
index 0000000000000000000000000000000000000000..1b527b460ec2ea733c83f7f16b46d1e895171da1
--- /dev/null
+++ b/client-py/client.py
@@ -0,0 +1,35 @@
+#!/usr/bin/env python3
+import zmq
+import protocol.hello_pb2 as proto
+
+def main():
+    context = zmq.Context()
+    socket = context.socket(zmq.REP)
+    socket.bind("tcp://*:28000")
+
+    # Round 1: hello
+    msg = socket.recv()
+
+    order = proto.Order()
+    order.hello.name = 'client-py'
+    msg_str = order.SerializeToString()
+    socket.send(msg_str)
+
+    # Round2: compute flops
+    msg = socket.recv()
+
+    order = proto.Order()
+    order.compute_flops.flop_amount = 1e9
+    msg_str = order.SerializeToString()
+    socket.send(msg_str)
+
+    # Round3: bye
+    msg = socket.recv()
+
+    order = proto.Order()
+    order.bye.SetInParent()
+    msg_str = order.SerializeToString()
+    socket.send(msg_str)
+
+if __name__ == '__main__':
+    main()
diff --git a/client-py/setup.py b/client-py/setup.py
new file mode 100644
index 0000000000000000000000000000000000000000..5e7563d49399405f380065d5ab7cde409ce8f0e8
--- /dev/null
+++ b/client-py/setup.py
@@ -0,0 +1,24 @@
+#!/usr/bin/env python
+from distutils.core import setup
+
+setup(name='client-py',
+    version='0.1.0',
+    py_modules=['client'],
+    scripts=['client-py'],
+
+    python_requires='>=3.6',
+    install_requires=[
+        'pyzmq>=22.3.0',
+        'protobuf>=3.19.4',
+    ],
+
+    description="Example python client for DZ2 toy CI prototype.",
+    author='Millian Poquet',
+    author_email='millian.poquet@irit.fr',
+    url='https://gitlab.irit.fr/sepia/datazero/ci-prototype',
+    license='MIT',
+    classifiers=[
+        "Programming Language :: Python :: 3",
+        "License :: OSI Approved :: MIT License",
+    ],
+)
diff --git a/default.nix b/default.nix
index 75e049a47a2f0395989396554bb1c753f58646c8..48de43eed22de3fea61bcdfe26210b750cd27e8b 100644
--- a/default.nix
+++ b/default.nix
@@ -77,6 +77,18 @@ let self = rec {
       zeromq
     ];
   };
+  client-py = pythonPackages.buildPythonPackage {
+    name = "client-py-0.1.0";
+    src = pkgs.lib.sourceByRegex ./client-py [
+      "setup\.py"
+      "client\.py"
+    ];
+    propagatedBuildInputs = [
+      protocol-python
+      pythonPackages.protobuf
+      pythonPackages.pyzmq
+    ];
+  };
 
   integration_shell_cpp = pkgs.mkShell {
     buildInputs = [
@@ -84,6 +96,12 @@ let self = rec {
       client-cpp
     ];
   };
+  integration_shell_py = pkgs.mkShell {
+    buildInputs = [
+      simgrid-simulator
+      client-py
+    ];
+  };
 };
 in
   self
diff --git a/test/simulator-py-client.expected.err b/test/simulator-py-client.expected.err
new file mode 100644
index 0000000000000000000000000000000000000000..ee331788ee783563366fd13d2aea9eb6d1824cf1
--- /dev/null
+++ b/test/simulator-py-client.expected.err
@@ -0,0 +1,9 @@
+[Fafard:orchestrator:(1) 0.000000] [orchestrator/INFO] started!
+[Fafard:orchestrator:(1) 0.000000] [orchestrator/INFO] connecting to endpoint 'tcp://localhost:28000'...
+[Fafard:orchestrator:(1) 0.000000] [orchestrator/INFO] connected!
+[Fafard:orchestrator:(1) 0.000000] [orchestrator/INFO] sending what you wanna do
+[Fafard:orchestrator:(1) 0.000000] [orchestrator/INFO] received hello from client-py
+[Fafard:orchestrator:(1) 0.000000] [orchestrator/INFO] sending what you wanna do
+[Fafard:orchestrator:(1) 0.000000] [orchestrator/INFO] computing 1e+09 flops
+[Fafard:orchestrator:(1) 13.106847] [orchestrator/INFO] sending what you wanna do
+[Fafard:orchestrator:(1) 13.106847] [orchestrator/INFO] received bye
diff --git a/test/simulator-py-client.sh b/test/simulator-py-client.sh
new file mode 100755
index 0000000000000000000000000000000000000000..6f68883bdaf677dd51d11f2aa5b69cb74ab8aef2
--- /dev/null
+++ b/test/simulator-py-client.sh
@@ -0,0 +1,25 @@
+#!/usr/bin/env sh
+set -eu
+
+simulator simgrid-simulator/platforms/small_platform.xml 1>simulator.out 2>simulator.err &
+simulator_pid=$!
+
+client-py 1>client-py.out 2>client-py.err &
+client_pid=$!
+
+wait ${client_pid}
+wait ${simulator_pid}
+
+script_dir=$(dirname "$0")
+diff simulator.err "${script_dir}/simulator-py-client.expected.err" > simulator.err.diff || true
+
+if [ -s simulator.err.diff ]; then
+    # file not empty, there is a diff!
+    echo "simulation output is not the expected one, aborting!"
+    cat simulator.err.diff
+    exit 1
+else
+    # file is empty, everything is fine
+    echo "simulation output is the expected one"
+    exit 0
+fi