diff --git a/bindings/pyroot_experimental/PyROOT/CMakeLists.txt b/bindings/pyroot_experimental/PyROOT/CMakeLists.txt
index b63f0f3b77178efb2ed5dd5aadd51d16d71de700..e2329f764a5df34bac173ee947e8277eaf9e42b9 100644
--- a/bindings/pyroot_experimental/PyROOT/CMakeLists.txt
+++ b/bindings/pyroot_experimental/PyROOT/CMakeLists.txt
@@ -11,6 +11,7 @@ set(sources
   src/PyROOTWrapper.cxx
   src/TTreePyz.cxx
   src/PrettyPrintingPyz.cxx
+  src/ArrayInterfacePyz.cxx
 )
 
 file(COPY python/ROOT DESTINATION ${localruntimedir})
diff --git a/bindings/pyroot_experimental/PyROOT/python/ROOT/pythonization/_rvec.py b/bindings/pyroot_experimental/PyROOT/python/ROOT/pythonization/_rvec.py
new file mode 100644
index 0000000000000000000000000000000000000000..c302381a8992d4fae68c450a5a91f83fc575c977
--- /dev/null
+++ b/bindings/pyroot_experimental/PyROOT/python/ROOT/pythonization/_rvec.py
@@ -0,0 +1,51 @@
+from ROOT import pythonization
+from libROOTPython import GetEndianess, GetVectorDataPointer, GetSizeOfType
+
+_array_interface_dtypes = [
+    "float", "double", "int", "long", "unsigned int", "unsigned long"
+]
+
+_array_interface_dtype_map = {
+    "float": "f",
+    "double": "f",
+    "int": "i",
+    "long": "i",
+    "unsigned int": "u",
+    "unsigned long": "u"
+}
+
+
+def get_array_interface(self):
+    cppname = type(self).__cppname__
+    for dtype in _array_interface_dtypes:
+        if cppname.endswith("<{}>".format(dtype)):
+            dtype_numpy = _array_interface_dtype_map[dtype]
+            dtype_size = GetSizeOfType(dtype)
+            endianess = GetEndianess()
+            size = self.size()
+            pointer = GetVectorDataPointer(self, cppname)
+            return {
+                "shape": (size, ),
+                "typestr": "{}{}{}".format(endianess, dtype_numpy, dtype_size),
+                "version": 3,
+                "data": (pointer, False)
+            }
+
+
+def add_array_interface_property(klass, name):
+    if True in [
+            "<{}>".format(dtype) in name for dtype in _array_interface_dtypes
+    ]:
+        klass.__array_interface__ = property(get_array_interface)
+
+
+@pythonization
+def pythonize_rvec(klass, name):
+    # Parameters:
+    # klass: class to be pythonized
+    # name: string containing the name of the class
+
+    # Add numpy array interface
+    add_array_interface_property(klass, name)
+
+    return True
diff --git a/bindings/pyroot_experimental/PyROOT/python/ROOT/pythonization/_stl_vector.py b/bindings/pyroot_experimental/PyROOT/python/ROOT/pythonization/_stl_vector.py
new file mode 100644
index 0000000000000000000000000000000000000000..7cb7cef7b3da6eba94b87cb7e7e3973710c92690
--- /dev/null
+++ b/bindings/pyroot_experimental/PyROOT/python/ROOT/pythonization/_stl_vector.py
@@ -0,0 +1,15 @@
+from ROOT import pythonization
+from ROOT.pythonization._rvec import add_array_interface_property
+
+
+@pythonization
+def pythonize_stl_vector(klass, name):
+    # Parameters:
+    # klass: class to be pythonized
+    # name: string containing the name of the class
+
+    # Add numpy array interface
+    # NOTE: The pythonization is reused from ROOT::VecOps::RVec
+    add_array_interface_property(klass, name)
+
+    return True
diff --git a/bindings/pyroot_experimental/PyROOT/src/ArrayInterfacePyz.cxx b/bindings/pyroot_experimental/PyROOT/src/ArrayInterfacePyz.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..4ce721edac302ce7b5f7584ab59c08425ec1cd20
--- /dev/null
+++ b/bindings/pyroot_experimental/PyROOT/src/ArrayInterfacePyz.cxx
@@ -0,0 +1,78 @@
+#include "CPyCppyy.h"
+#include "CPPInstance.h"
+#include "PyROOTPythonize.h"
+#include "RConfig.h"
+#include "TInterpreter.h"
+
+#include <sstream>
+
+////////////////////////////////////////////////////////////////////////////
+/// \brief Get size of C++ data-type
+/// \param[in] self Always null, since this is a module function.
+/// \param[in] args C++ data-type as Python string
+///
+/// This function returns the length of a C++ data-type in bytes
+/// as a Python integer.
+PyObject *PyROOT::GetSizeOfType(PyObject * /*self*/, PyObject *args)
+{
+   // Get name of data-type
+   PyObject *pydtype = PyTuple_GetItem(args, 0);
+   std::string dtype = CPyCppyy_PyUnicode_AsString(pydtype);
+
+   // Call interpreter to get size of data-type using `sizeof`
+   long size;
+   std::stringstream code;
+   code << "*((long*)" << &size << ") = (long)sizeof(" << dtype << ")";
+   gInterpreter->Calc(code.str().c_str());
+
+   // Return size of data-type as integer
+   PyObject *pysize = PyInt_FromLong(size);
+   return pysize;
+}
+
+////////////////////////////////////////////////////////////////////////////
+/// \brief Get pointer to the data of a vector
+/// \param[in] self Always null, since this is a module function.
+/// \param[in] args[0] Data-type of the C++ object as Python string
+/// \param[in] args[1] Python representation of the C++ object.
+///
+/// This function returns the pointer to the data of a vector as an Python
+/// integer.
+PyObject *PyROOT::GetVectorDataPointer(PyObject * /*self*/, PyObject *args)
+{
+   // Get pointer of C++ object
+   PyObject *pyobj = PyTuple_GetItem(args, 0);
+   auto instance = (CPyCppyy::CPPInstance *)(pyobj);
+   auto cppobj = instance->GetObject();
+
+   // Get name of C++ object as string
+   PyObject *pycppname = PyTuple_GetItem(args, 1);
+   std::string cppname = CPyCppyy_PyUnicode_AsString(pycppname);
+
+   // Call interpreter to get pointer to data (using `data` method)
+   long pointer;
+   std::stringstream code;
+   code << "*((long*)" << &pointer << ") = reinterpret_cast<long>(reinterpret_cast<" << cppname << "*>(" << cppobj
+        << ")->data())";
+   gInterpreter->Calc(code.str().c_str());
+
+   // Return pointer as integer
+   PyObject *pypointer = PyInt_FromLong(pointer);
+   return pypointer;
+}
+
+////////////////////////////////////////////////////////////////////////////
+/// \brief Get endianess of the system
+/// \param[in] self Always null, since this is a module function.
+/// \param[out] Endianess as Python string
+///
+/// This function returns endianess of the system as a Python integer. The
+/// return value is either '<' or '>' for little or big endian, respectively.
+PyObject *PyROOT::GetEndianess(PyObject * /* self */)
+{
+#ifdef R__BYTESWAP
+   return CPyCppyy_PyUnicode_FromString("<");
+#else
+   return CPyCppyy_PyUnicode_FromString(">");
+#endif
+}
diff --git a/bindings/pyroot_experimental/PyROOT/src/PyROOTModule.cxx b/bindings/pyroot_experimental/PyROOT/src/PyROOTModule.cxx
index 5c6db005adebeff31f09413e28c112fed8592827..a2cbbfa509e4ae6eb897f55dc2ee2becf83878c5 100644
--- a/bindings/pyroot_experimental/PyROOT/src/PyROOTModule.cxx
+++ b/bindings/pyroot_experimental/PyROOT/src/PyROOTModule.cxx
@@ -31,6 +31,12 @@ static PyMethodDef gPyROOTMethods[] = {{(char *)"PythonizeTTree", (PyCFunction)P
                                         (char *)"Pythonizations for class TTree"},
                                        {(char *)"AddPrettyPrintingPyz", (PyCFunction)PyROOT::AddPrettyPrintingPyz, METH_VARARGS,
                                         (char *)"Add pretty printing pythonization"},
+                                       {(char *)"GetEndianess", (PyCFunction)PyROOT::GetEndianess, METH_NOARGS,
+                                        (char *)"Get endianess of the system"},
+                                       {(char *)"GetVectorDataPointer", (PyCFunction)PyROOT::GetVectorDataPointer, METH_VARARGS,
+                                        (char *)"Get pointer to data of vector"},
+                                       {(char *)"GetSizeOfType", (PyCFunction)PyROOT::GetSizeOfType, METH_VARARGS,
+                                        (char *)"Get size of data-type"},
                                        {NULL, NULL, 0, NULL}};
 
 #if PY_VERSION_HEX >= 0x03000000
diff --git a/bindings/pyroot_experimental/PyROOT/src/PyROOTPythonize.h b/bindings/pyroot_experimental/PyROOT/src/PyROOTPythonize.h
index c0473053efa94a0362fca162e17c174ca5ec1c8b..668418c0032d78718692876772bf0f042ee1355c 100644
--- a/bindings/pyroot_experimental/PyROOT/src/PyROOTPythonize.h
+++ b/bindings/pyroot_experimental/PyROOT/src/PyROOTPythonize.h
@@ -8,6 +8,9 @@ namespace PyROOT {
 
 PyObject *AddPrettyPrintingPyz(PyObject *self, PyObject *args);
 PyObject *PythonizeTTree(PyObject *self, PyObject *args);
+PyObject *GetEndianess(PyObject *self);
+PyObject *GetVectorDataPointer(PyObject *self, PyObject *args);
+PyObject *GetSizeOfType(PyObject *self, PyObject *args);
 
 } // namespace PyROOT
 
diff --git a/bindings/pyroot_experimental/PyROOT/test/CMakeLists.txt b/bindings/pyroot_experimental/PyROOT/test/CMakeLists.txt
index 0a59db7d6596b8ced162bd8344466c58e671c6a7..9a3d8dac57649ce4f687a0f01e0dd0dd1ba8653a 100644
--- a/bindings/pyroot_experimental/PyROOT/test/CMakeLists.txt
+++ b/bindings/pyroot_experimental/PyROOT/test/CMakeLists.txt
@@ -1,5 +1,6 @@
 # General pythonizations
 ROOT_ADD_PYUNITTEST(pyroot_pretty_printing pretty_printing.py)
+ROOT_ADD_PYUNITTEST(pyroot_array_interface array_interface.py)
 
 # TTree pythonizations
 ROOT_ADD_PYUNITTEST(pyroot_pyz_ttree_branch_attr ttree_branch_attr.py
diff --git a/bindings/pyroot_experimental/PyROOT/test/array_interface.py b/bindings/pyroot_experimental/PyROOT/test/array_interface.py
new file mode 100644
index 0000000000000000000000000000000000000000..544682795feb08ac0b5694b79339324db104c9c2
--- /dev/null
+++ b/bindings/pyroot_experimental/PyROOT/test/array_interface.py
@@ -0,0 +1,50 @@
+import unittest
+import ROOT
+import numpy as np
+
+
+class ArrayInterface(unittest.TestCase):
+    # Helpers
+    dtypes = [
+        "int", "unsigned int", "long", "unsigned long", "float", "double"
+    ]
+
+    def get_maximum_for_dtype(self, dtype):
+        if np.issubdtype(dtype, np.integer):
+            return np.iinfo(dtype).max
+        if np.issubdtype(dtype, np.floating):
+            return np.finfo(dtype).max
+
+    def get_minimum_for_dtype(self, dtype):
+        if np.issubdtype(dtype, np.integer):
+            return np.iinfo(dtype).min
+        if np.issubdtype(dtype, np.floating):
+            return np.finfo(dtype).min
+
+    def check_memory_adoption(self, root_obj, np_obj):
+        root_obj[0] = self.get_maximum_for_dtype(np_obj.dtype)
+        root_obj[1] = self.get_minimum_for_dtype(np_obj.dtype)
+        self.assertEqual(root_obj[0], np_obj[0])
+        self.assertEqual(root_obj[1], np_obj[1])
+
+    def check_shape(self, expected_shape, np_obj):
+        self.assertEqual(expected_shape, np_obj.shape)
+
+    # Tests
+    def test_RVec(self):
+        for dtype in self.dtypes:
+            root_obj = ROOT.ROOT.VecOps.RVec(dtype)(2)
+            np_obj = np.asarray(root_obj)
+            self.check_memory_adoption(root_obj, np_obj)
+            self.check_shape((2, ), np_obj)
+
+    def test_STLVector(self):
+        for dtype in self.dtypes:
+            root_obj = ROOT.std.vector(dtype)(2)
+            np_obj = np.asarray(root_obj)
+            self.check_memory_adoption(root_obj, np_obj)
+            self.check_shape((2, ), np_obj)
+
+
+if __name__ == '__main__':
+    unittest.main()