--- a/src/python/recoll/pyrclextract.cpp
+++ b/src/python/recoll/pyrclextract.cpp
@@ -51,7 +51,7 @@
 {
     LOGDEB(("Extractor_dealloc\n"));
     delete self->xtr;
-    self->ob_type->tp_free((PyObject*)self);
+    Py_TYPE(self)->tp_free((PyObject*)self);
 }
 
 static PyObject *
@@ -191,8 +191,8 @@
     }
     if (outfile.empty())
 	temp->setnoremove(1);
-    PyObject *result = outfile.empty() ? PyString_FromString(temp->filename()) :
-	PyString_FromString(outfile.c_str());
+    PyObject *result = outfile.empty() ? PyBytes_FromString(temp->filename()) :
+	PyBytes_FromString(outfile.c_str());
     return (PyObject *)result;
 }
 
@@ -211,8 +211,7 @@
 "object.\n"
 );
 static PyTypeObject rclx_ExtractorType = {
-    PyObject_HEAD_INIT(NULL)
-    0,                         /*ob_size*/
+    PyVarObject_HEAD_INIT(NULL, 0)
     "rclextract.Extractor",             /*tp_name*/
     sizeof(rclx_ExtractorObject), /*tp_basicsize*/
     0,                         /*tp_itemsize*/
@@ -253,17 +252,56 @@
 };
 
 ///////////////////////////////////// Module-level stuff
-static PyMethodDef rclxMethods[] = {
+static PyMethodDef rclextract_methods[] = {
     {NULL, NULL, 0, NULL}        /* Sentinel */
 };
 PyDoc_STRVAR(rclx_doc_string,
 	     "This is an interface to the Recoll text extraction features.");
 
-#ifndef PyMODINIT_FUNC	/* declarations for DLL import/export */
-#define PyMODINIT_FUNC void
-#endif
+struct module_state {
+    PyObject *error;
+};
+
+#if PY_MAJOR_VERSION >= 3
+#define GETSTATE(m) ((struct module_state*)PyModule_GetState(m))
+#else
+#define GETSTATE(m) (&_state)
+static struct module_state _state;
+#endif
+
+#if PY_MAJOR_VERSION >= 3
+static int rclextract_traverse(PyObject *m, visitproc visit, void *arg) {
+    Py_VISIT(GETSTATE(m)->error);
+    return 0;
+}
+
+static int rclextract_clear(PyObject *m) {
+    Py_CLEAR(GETSTATE(m)->error);
+    return 0;
+}
+
+static struct PyModuleDef moduledef = {
+        PyModuleDef_HEAD_INIT,
+        "rclextract",
+        NULL,
+        sizeof(struct module_state),
+        rclextract_methods,
+        NULL,
+        rclextract_traverse,
+        rclextract_clear,
+        NULL
+};
+
+#define INITERROR return NULL
+
+extern "C" PyObject *
+PyInit_rclextract(void)
+
+#else
+#define INITERROR return
 PyMODINIT_FUNC
 initrclextract(void)
+#endif
 {
     // We run recollinit. It's responsible for initializing some static data
     // which is distinct from pyrecoll's as we're separately dlopened
@@ -271,33 +309,53 @@
     rclconfig = recollinit(0, 0, reason, 0);
     if (rclconfig == 0) {
 	PyErr_SetString(PyExc_EnvironmentError, reason.c_str());
-	return;
+	INITERROR;
     }
     if (!rclconfig->ok()) {
 	PyErr_SetString(PyExc_EnvironmentError, 
 			"Recoll init error: bad environment ?");
-	return;
-    }
-
-    PyObject* m = Py_InitModule("rclextract", rclxMethods);
-    PyModule_AddStringConstant(m, "__doc__", rclx_doc_string);
+	INITERROR;
+    }
+
+#if PY_MAJOR_VERSION >= 3
+    PyObject *module = PyModule_Create(&moduledef);
+#else
+    PyObject *module = Py_InitModule("rclextract", rclextract_methods);
+#endif
+    if (module == NULL)
+        INITERROR;
+
+    struct module_state *st = GETSTATE(module);
+    // The first parameter is a char *. Hopefully we don't initialize
+    // modules too often...
+    st->error = PyErr_NewException(strdup("rclextract.Error"), NULL, NULL);
+    if (st->error == NULL) {
+        Py_DECREF(module);
+        INITERROR;
+    }
+
+    PyModule_AddStringConstant(module, "__doc__", rclx_doc_string);
 
     if (PyType_Ready(&rclx_ExtractorType) < 0)
-        return;
+        INITERROR;
     Py_INCREF(&rclx_ExtractorType);
-    PyModule_AddObject(m, "Extractor", (PyObject *)&rclx_ExtractorType);
-
-#if PY_MAJOR_VERSION >= 2 && PY_MINOR_VERSION >= 7
+    PyModule_AddObject(module, "Extractor", (PyObject *)&rclx_ExtractorType);
+
+#if PY_MAJOR_VERSION >= 3 || (PY_MAJOR_VERSION >= 2 && PY_MINOR_VERSION >= 7)
     recoll_DocType = (PyObject*)PyCapsule_Import(PYRECOLL_PACKAGE "recoll.doctypeptr", 0);
 #else
     PyObject *module = PyImport_ImportModule(PYRECOLL_PACKAGE "recoll");
     if (module != NULL) {
         PyObject *cobject = PyObject_GetAttrString(module, "_C_API");
         if (cobject == NULL)
-            return;
+            INITERROR;
         if (PyCObject_Check(cobject))
             recoll_DocType = (PyObject*)PyCObject_AsVoidPtr(cobject);
         Py_DECREF(cobject);
     }
 #endif
-}
+
+#if PY_MAJOR_VERSION >= 3
+    return module;
+#endif
+}