The encoding looks good to me, it matches what Include/object.h says:
typedef struct _object {
_PyObject_HEAD_EXTRA
Py_ssize_t ob_refcnt;
struct _typeobject *ob_type;
} PyObject;
The _PyObject_HEAD_EXTRA part is a macro that is only used in debug builds of Python. In a normal build it expands to nothing and doesn't add anything to the struct. The first actual field is ob_refcnt, which has type Py_ssize_t, which is the signed version of size_t, which has the same size as an int on 32-bit devices, so it is encoded as i. The next field after that is ob_type, which points to a struct _typeobject.
The encoding for struct _typeobject isn't included anymore, even though struct _typeobject is defined later in the header file. I think that is because at that point the pointer is two levels deep (a pointer to a struct containing a pointer), the Objective-C runtime guide says that pointers to pointers to structs don't include the full struct encoding anymore, maybe the same is the case for pointers in pointers to structs.
Yes, POINTER(py_object) is not going to work. Python objects are always passed around in C code as a PyObject * (a pointer to a PyObject, or at least something similar), so the py_object type is actually a pointer already. You could of course define a Structure for PyObject and use POINTER(PyObjectStruct) to access an object's internal data, but that's not very useful in most cases.