diff --git a/CHANGELOG.md b/CHANGELOG.md
index 77a43b848d74a382cab75fc85db6abb384314fd6..e75607c919fdc7fdb7ce91c8c321b391e1637d06 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -55,6 +55,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
   entities.
 * Test for [caosdb-server#268](https://gitlab.com/linkahead/linkahead-server/-/issues/268): Unexpected Server Error when non-existent file shall be inserted.
 * test_profile for running the tests locally.
+* Tests for pylib's high-level API
 
 ### Changed (for changes in existing functionality)
 
diff --git a/tests/test_high_level.py b/tests/test_high_level.py
new file mode 100644
index 0000000000000000000000000000000000000000..1b45cefbd6fb8d749fc201df049c634756ab4df5
--- /dev/null
+++ b/tests/test_high_level.py
@@ -0,0 +1,124 @@
+# encoding: utf-8
+#
+# This file is a part of the LinkAhead Project.
+#
+# Copyright (C) 2025 Indiscale GmbH <info@indiscale.com>
+# Copyright (C) 2025 Daniel Hornung <d.hornung@indiscale.com>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+"""Testing the high-level api.
+"""
+
+import pytest
+
+import linkahead as db
+from linkahead import high_level_api as hl
+from linkahead import (
+    Container,
+    Property,
+    Record,
+    RecordType,
+)
+from linkahead.utils.register_tests import set_test_key, clear_database
+
+set_test_key("_CAOSDB_PYINTTEST_SUITE")
+
+
+@pytest.fixture(autouse=True)
+def setup(clear_database):
+    cont = Container()
+    rt1 = RecordType(name="RT1")
+    cont.append(rt1)
+    rt2 = RecordType(name="RT2")
+    cont.append(rt2)
+    rt1.add_property(rt2)
+    prop2 = Property("comment", datatype=db.TEXT)
+    cont.append(prop2)
+    rt2.add_property(prop2)
+
+    cont.insert()
+
+
+def test_convert_to_py_object():
+    """Basic conversion and retrieval."""
+    # Create data
+    rec2_inserted = (Record("rec2").add_parent("RT2")
+                     .add_property("comment", value="a nice record")
+                     .insert())
+    rec1_inserted = (Record("rec1").add_parent("RT1")
+                     .add_property("RT2", value=rec2_inserted)
+                     .insert())
+
+    # Create Record objects
+    rec1_offline = Record("rec1")
+    rec1_retrieved = db.get_entity_by_name("rec1")
+
+    # Convert to Python objects
+    rec1_offl_unresolved = hl.convert_to_python_object(rec1_offline)
+    rec1_retrieved_unresolved = hl.convert_to_python_object(rec1_retrieved)
+    rec1_offl_resolved = hl.convert_to_python_object(rec1_offline, resolve_references=True)
+    rec1_retrieved_resolved = hl.convert_to_python_object(rec1_retrieved, resolve_references=True)
+
+    # Also: retrieve by `query`
+    rec1_query_unresolved = hl.query("FIND rec1", resolve_references=False)[0]
+    rec1_query_resolved = hl.query("FIND rec1")[0]
+
+    # Serialize and remove ephemeral components
+    json_unresolved = rec1_retrieved_unresolved.serialize()
+    json_resolved = rec1_retrieved_resolved.serialize()
+    json_query_unresolved = rec1_query_unresolved.serialize()
+    json_query_resolved = rec1_query_resolved.serialize()
+
+    for data in (json_unresolved, json_resolved, json_query_unresolved, json_query_resolved):
+        data.pop("id")
+        data.pop("version")
+        data["properties"]["RT2"].pop("id")
+        data["parents"][0].pop("id")
+        data["metadata"]["RT2"].pop("id")
+    for data in (json_resolved, json_query_resolved):
+        data["properties"]["RT2"].pop("version")
+        data["properties"]["RT2"]["parents"][0].pop("id")
+        data["properties"]["RT2"]["metadata"]["comment"].pop("id")
+
+    # Test against known-good serialization
+    assert rec1_offl_resolved.serialize() == {'role': 'Record',
+                                              'name': 'rec1',
+                                              'properties': {},
+                                              'parents': [],
+                                              'metadata': {}}
+    assert rec1_offl_unresolved.serialize() == {'role': 'Record',
+                                                'name': 'rec1',
+                                                'properties': {},
+                                                'parents': [],
+                                                'metadata': {}}
+
+    assert json_unresolved == {'role': 'Record',
+                               'name': 'rec1',
+                               'properties': {'RT2': {'unresolved': True}},
+                               'parents': [{'name': 'RT1', 'unresolved': True}],
+                               'metadata': {'RT2': {'datatype': 'RT2', 'importance': 'FIX'}}
+                               }
+    assert json_resolved == {
+        'role': 'Record',
+        'name': 'rec1',
+        'properties': {'RT2': {'role': 'Record',
+                               'name': 'rec2',
+                               'properties': {'comment': 'a nice record'},
+                               'parents': [{'name': 'RT2', 'unresolved': True}],
+                               'metadata': {'comment': {'datatype': 'TEXT',
+                                                        'importance': 'FIX'}}}
+                       },
+        'parents': [{'name': 'RT1', 'unresolved': True}],
+        'metadata': {'RT2': {'datatype': 'RT2', 'importance': 'FIX'}}}