diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6969f647584c34742594b01228fbd17160aeb5e3..958933ba413af1b526e49b0fcacdaf2ffd7cf344 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,7 +5,7 @@ All notable changes to this project will be documented in this file.
 The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
 and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
 
-## [Unreleased] ##
+## [0.15.0] - 2024-07-09 ##
 
 ### Added ###
 
@@ -15,10 +15,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
   `Entity.update_acl` now supports optional `**kwargs` that are passed to the
   `Entity.update` method that is called internally, thus allowing, e.g.,
   updating the ACL despite possible naming collisions with `unique=False`.
+* a `role` argument for `get_entity_by_name` and `get_entity_by_id`
 
 ### Changed ###
 
-### Deprecated ###
+* Using environment variable PYLINKAHEADINI instead of PYCAOSDBINI.
 
 ### Removed ###
 
@@ -34,10 +35,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 * [#120](https://gitlab.com/linkahead/linkahead-pylib/-/issues/120) Unwanted
   subproperties in reference properties.
 
-### Security ###
-
 ### Documentation ###
 
+* Added documentation and a tutorial example for the usage of the `page_length`
+  argument of `execute_query`.
+
 ## [0.14.0] - 2024-02-20
 
 ### Added ###
diff --git a/CITATION.cff b/CITATION.cff
index cbcb570b27b7cd71f50645614222302bccc34805..148cccb1804f7ae254224074dfef408e014f5438 100644
--- a/CITATION.cff
+++ b/CITATION.cff
@@ -20,6 +20,6 @@ authors:
     given-names: Stefan
     orcid: https://orcid.org/0000-0001-7214-8125
 title: CaosDB - Pylib
-version: 0.14.0
+version: 0.15.0
 doi: 10.3390/data4020083
-date-released: 2024-02-20
+date-released: 2024-07-09
diff --git a/examples/pylinkahead.ini b/examples/pylinkahead.ini
index f37e24e0e5b754ec58a07b034ba2755096f0b441..84d1eb8526201c817d6614e7eb74f35a932c5d78 100644
--- a/examples/pylinkahead.ini
+++ b/examples/pylinkahead.ini
@@ -1,7 +1,7 @@
 # To be found be the caosdb package, the INI file must be located either in
 # - $CWD/pylinkahead.ini
 # - $HOME/.pylinkahead.ini
-# - the location given in the env variable PYCAOSDBINI
+# - the location given in the env variable PYLINKAHEADINI
 
 [Connection]
 # URL of the CaosDB server
diff --git a/setup.py b/setup.py
index ee2a5fb6fd7212acfc9ce9bc732fc9f2d4f345b4..1a8a754219ddf84c0b9e088a13fd0283fa63a00f 100755
--- a/setup.py
+++ b/setup.py
@@ -46,10 +46,10 @@ from setuptools import find_packages, setup
 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ########################################################################
 
-ISRELEASED = False
+ISRELEASED = True
 MAJOR = 0
-MINOR = 14
-MICRO = 1
+MINOR = 15
+MICRO = 0
 # Do not tag as pre-release until this commit
 # https://github.com/pypa/packaging/pull/515
 # has made it into a release. Probably we should wait for pypa/packaging>=21.4
diff --git a/src/doc/conf.py b/src/doc/conf.py
index 61a60d7c9e8d5c6b0959f4bba230cd483c06bc79..4a528d9e287daeeacd50e94cfba4e479b0430212 100644
--- a/src/doc/conf.py
+++ b/src/doc/conf.py
@@ -29,10 +29,10 @@ copyright = '2023, IndiScale GmbH'
 author = 'Daniel Hornung'
 
 # The short X.Y version
-version = '0.14.1'
+version = '0.15.0'
 # The full version, including alpha/beta/rc tags
 # release = '0.5.2-rc2'
-release = '0.14.1-dev'
+release = '0.15.0'
 
 
 # -- General configuration ---------------------------------------------------
diff --git a/src/doc/configuration.md b/src/doc/configuration.md
index 54ae251b9db9ef000545e701406b979aa58043f8..427551db4e1e97d7ca5f9820df6d5916e3496020 100644
--- a/src/doc/configuration.md
+++ b/src/doc/configuration.md
@@ -1,6 +1,6 @@
 # Configuration of PyLinkAhead #
 The behavior of PyLinkAhead is defined via a configuration that is provided using configuration files.
-PyLinkAhead tries to read from the inifile specified in the environment variable `PYCAOSDBINI` or
+PyLinkAhead tries to read from the inifile specified in the environment variable `PYLINKAHEADINI` or
 alternatively in `~/.pylinkahead.ini` upon import.  After that, the ini file `pylinkahead.ini` in the
 current working directory will be read additionally, if it exists.
 
diff --git a/src/doc/tutorials/index.rst b/src/doc/tutorials/index.rst
index 706e26c2b1b4876c29d43c2bddd9a5fe357a003d..e745482ace189e10a042975869cae6310f6ad703 100644
--- a/src/doc/tutorials/index.rst
+++ b/src/doc/tutorials/index.rst
@@ -15,6 +15,7 @@ advanced usage of the Python client.
    Data-Insertion
    errors
    Entity-Getters
+   paginated_queries
    caching
    data-model-interface
    complex_data_models
diff --git a/src/doc/tutorials/paginated_queries.rst b/src/doc/tutorials/paginated_queries.rst
new file mode 100644
index 0000000000000000000000000000000000000000..c250223f46405caa9d289ce4d8774daf06fdf366
--- /dev/null
+++ b/src/doc/tutorials/paginated_queries.rst
@@ -0,0 +1,63 @@
+Query pagination
+================
+
+When retrieving many entities, you may not want to retrieve all at once, e.g.,
+for performance reasons or to prevent connection timeouts, but rather in a
+chunked way. For that purpose, there is the ``page_length`` parameter in the
+:py:meth:`~linkahead.common.models.execute_query` function. If this is set to a
+non-zero integer, the behavior of the function changes in that it returns a
+Python `generator <https://docs.python.org/3/glossary.html#term-generator>`_
+which can be used, e.g., in loops or in list comprehension. The generator yields
+a :py:class:`~linkahead.common.models.Container` containing the next
+``page_length`` many entities from the query result.
+
+The following example illustrates this on the demo server.
+
+.. code-block:: python
+
+   import linkahead as db
+
+   # 10 at the time of writing of this example
+   print(db.execute_query("FIND MusicalInstrument"))
+
+   # Retrieve in pages of length 5 and iterate over the pages
+   for page in db.execute_query("FIND MusicalInstrument", page_length=5):
+       # each page is a container
+       print(type(page))
+       # exactly page_length=5 for the first N-1 pages,
+       # and possibly less for the last page
+       print(len(page))
+       # the items on each page are subclasses of Entity
+       print(type(page[0]))
+       # The id of the first entity on the page is different for all pages
+       print(page[0].id)
+
+   # You can use this in a list comprehension to fill a container
+   container_paginated = db.Container().extend(
+       [ent for page in db.execute_query("FIND MusicalInstrument", page_length=5) for ent in page]
+   )
+   # The result is the same as in the unpaginated case, but the
+   # following can cause connection timeouts in case of very large
+   # retrievals
+   container_at_once = db.execute_query("FIND MusicalInstrument")
+   for ent1, ent2 in zip(container_paginated, container_at_once):
+      print(ent1.id == ent2.id)  # always true
+
+As you can see, you can iterate over a paginated query and then access the
+entities on each page during the iteration.
+
+.. note::
+
+   The ``page_length`` keyword is ignored for ``COUNT`` queries where
+   :py:meth:`~linkahead.common.models.execute_query` always returns the integer
+   result and in case of ``unique=True`` where always exactly one
+   :py:class:`~linkahead.common.models.Entity` is returned.
+
+
+.. warning::
+
+   Be careful when combining query pagination with insert, update, or delete
+   operations. If your database changes while iterating over a paginated query,
+   the client will raise a
+   :py:exc:`~linkahead.exceptions.PagingConsistencyError` since the server
+   can't guarantee that the query results haven't changed in the meantime.
diff --git a/src/linkahead/__init__.py b/src/linkahead/__init__.py
index 3a8c5ba39c88deaa5dc945135e3828945fd39d58..cd54f8f4e05326579521fbbf226f027d32fa616e 100644
--- a/src/linkahead/__init__.py
+++ b/src/linkahead/__init__.py
@@ -24,7 +24,7 @@
 
 """LinkAhead Python bindings.
 
-Tries to read from the inifile specified in the environment variable `PYCAOSDBINI` or
+Tries to read from the inifile specified in the environment variable `PYLINKAHEADINI` or
 alternatively in `~/.pylinkahead.ini` upon import.  After that, the ini file `pylinkahead.ini` in
 the current working directory will be read additionally, if it exists.
 
diff --git a/src/linkahead/common/models.py b/src/linkahead/common/models.py
index 06f09057be50d76e1e73a95375030f13aaaced86..39b912fb0af211730b256023e4ad0b9b7fff1759 100644
--- a/src/linkahead/common/models.py
+++ b/src/linkahead/common/models.py
@@ -282,7 +282,7 @@ class Entity:
     @id.setter
     def id(self, new_id) -> None:
         if new_id is not None:
-            self.__id: Optional[int] = entity_id(new_id)
+            self.__id: Union[int, str, None] = entity_id(new_id)
         else:
             self.__id = None
 
@@ -2018,8 +2018,6 @@ class Property(Entity):
 
         Parameters
         ----------
-       Parameters
-        ----------
         parent : Entity or int or str or None
             The parent entity, either specified by the Entity object
             itself, or its id or its name. Default is None.
@@ -3215,9 +3213,15 @@ class Container(list):
         """Get an xml tree representing this Container or append all entities
         to the given xml element.
 
-        @param add_to_element=None: optional element to which all entities of this container is to
-               be appended.
-        @return xml element
+        Parameters
+        ----------
+        add_to_element : etree._Element, optional
+            optional element to which all entities of this container is to
+            be appended. Default is None
+
+        Returns
+        -------
+        xml_element : etree._Element
         """
         tmpid = 0
 
@@ -4153,13 +4157,21 @@ class Container(list):
         warnings as errors.  This prevents the server from inserting this entity if any warning
         occurs.
 
-        @param strict=False: Flag for strict mode.
-        @param sync=True: synchronize this container with the response from the server. Otherwise,
-                          this method returns a new container with the inserted entities and leaves
-                          this container untouched.
-        @param unique=True: Flag for unique mode. If set to True, the server will check if the name
-                            of the entity is unique. If not, the server will return an error.
-        @param flags=None: Additional flags for the server.
+        Parameters
+        ----------
+        strict : bool, optional
+            Flag for strict mode. Default is False.
+        sync : bool, optional
+            synchronize this container with the response from the
+            server. Otherwise, this method returns a new container with the
+            inserted entities and leaves this container untouched. Default is
+            True.
+        unique : bool, optional
+            Flag for unique mode. If set to True, the server will check if the
+            name of the entity is unique. If not, the server will return an
+            error. Default is True.
+        flags : dict, optional
+            Additional flags for the server. Default is None.
 
         """
 
@@ -5047,8 +5059,8 @@ def execute_query(
         Otherwise, paging is disabled, as well as for count queries and
         when unique is True. Defaults to None.
 
-    Raises:
-    -------
+    Raises
+    ------
     PagingConsistencyError
         If the database state changed between paged requests.
 
@@ -5110,6 +5122,17 @@ class DropOffBox(list):
 
 
 class UserInfo():
+    """User information from a server response.
+
+    Attributes
+    ----------
+    name : str
+        Username
+    realm : str
+        Realm in which this user lives, e.g., CaosDB or LDAP.
+    roles : list[str]
+        List of roles assigned to this user.
+    """
 
     def __init__(self, xml: etree._Element):
         self.roles = [role.text for role in xml.findall("Roles/Role")]
@@ -5118,6 +5141,21 @@ class UserInfo():
 
 
 class Info():
+    """Info about the LinkAhead instance that you are connected to. It has a
+    simple string representation in the form of "Connected to a LinkAhead with N
+    Records".
+
+    Attributes
+    ----------
+    messages : Messages
+        Collection of messages that the server's ``Info`` response contained.
+    user_info : UserInfo
+        Information about the user that is connected to the server, such as
+        name, realm or roles.
+    time_zone : TimeZone
+        The timezone information returned by the server.
+
+    """
 
     def __init__(self):
         self.messages = Messages()
@@ -5126,6 +5164,7 @@ class Info():
         self.sync()
 
     def sync(self):
+        """Retrieve server information from the server's ``Info`` response."""
         c = get_connection()
         try:
             http_response = c.retrieve(["Info"])
diff --git a/src/linkahead/configuration.py b/src/linkahead/configuration.py
index b020467c8c53e26d464a6a2fb473cc912b0e0612..f57289d7dcb6d7ab062024dc697dbda557670d7a 100644
--- a/src/linkahead/configuration.py
+++ b/src/linkahead/configuration.py
@@ -102,7 +102,7 @@ def validate_yaml_schema(valobj: dict[str, dict[str, Union[int, str, bool]]]):
 def _read_config_files() -> list[str]:
     """Read config files from different paths.
 
-    Read the config from either ``$PYCAOSDBINI`` or home directory (``~/.pylinkahead.ini``), and
+    Read the config from either ``$PYLINKAHEADINI`` or home directory (``~/.pylinkahead.ini``), and
     additionally adds config from a config file in the current working directory
     (``pylinkahead.ini``).
     If deprecated names are used (starting with 'pycaosdb'), those used in addition but the files
@@ -131,15 +131,18 @@ def _read_config_files() -> list[str]:
         warnings.warn("\n\nYou have a config file with the old naming scheme (pycaosdb.ini). "
                       f"Please use the new version and rename\n"
                       f"    {ini_cwd_caosdb}\nto\n    {ini_cwd}", DeprecationWarning)
+    if "PYCAOSDBINI" in environ:
+        warnings.warn("\n\nYou have an environment variable PYCAOSDBINI. "
+                      "Please rename it to PYLINKAHEADINI.")
     # End: LinkAhead rename block ##################################################
 
-    if "PYCAOSDBINI" in environ:
-        if not isfile(expanduser(environ["PYCAOSDBINI"])):
+    if "PYLINKAHEADINI" in environ:
+        if not isfile(expanduser(environ["PYLINKAHEADINI"])):
             raise RuntimeError(
-                f"No configuration file found at\n{expanduser(environ['PYCAOSDBINI'])}"
-                "\nwhich was given via the environment variable PYCAOSDBINI"
+                f"No configuration file found at\n{expanduser(environ['PYLINKAHEADINI'])}"
+                "\nwhich was given via the environment variable PYLINKAHEADINI"
             )
-        return_var.extend(configure(expanduser(environ["PYCAOSDBINI"])))
+        return_var.extend(configure(expanduser(environ["PYLINKAHEADINI"])))
     else:
         if isfile(ini_user_caosdb):
             return_var.extend(configure(ini_user_caosdb))
diff --git a/src/linkahead/utils/get_entity.py b/src/linkahead/utils/get_entity.py
index ce74089784525b1a38d9c5c5bbf3193a9ba94575..f84dc107e275390e53c6127834f53e5e5c6521cd 100644
--- a/src/linkahead/utils/get_entity.py
+++ b/src/linkahead/utils/get_entity.py
@@ -21,29 +21,33 @@
 
 """Convenience functions to retrieve a specific entity."""
 
-from typing import Union
+from typing import Union, Optional
 
 from ..common.models import Entity, execute_query
 from .escape import escape_squoted_text
 
 
-def get_entity_by_name(name: str) -> Entity:
+def get_entity_by_name(name: str, role: Optional[str] = None) -> Entity:
     """Return the result of a unique query that uses the name to find the correct entity.
 
     Submits the query "FIND ENTITY WITH name='{name}'".
     """
     name = escape_squoted_text(name)
+    if role is None:
+        role = "ENTITY"
     # type hint can be ignored, it's a unique query, so never Container or int
-    return execute_query(f"FIND ENTITY WITH name='{name}'", unique=True)  # type: ignore
+    return execute_query(f"FIND {role} WITH name='{name}'", unique=True)  # type: ignore
 
 
-def get_entity_by_id(eid: Union[str, int]) -> Entity:
+def get_entity_by_id(eid: Union[str, int], role: Optional[str] = None) -> Entity:
     """Return the result of a unique query that uses the id to find the correct entity.
 
     Submits the query "FIND ENTITY WITH id='{eid}'".
     """
+    if role is None:
+        role = "ENTITY"
     # type hint can be ignored, it's a unique query
-    return execute_query(f"FIND ENTITY WITH id='{eid}'", unique=True)  # type: ignore
+    return execute_query(f"FIND {role} WITH id='{eid}'", unique=True)  # type: ignore
 
 
 def get_entity_by_path(path: str) -> Entity:
diff --git a/unittests/test_configuration.py b/unittests/test_configuration.py
index 40506e878b18473587da8b694d9381c15bdbd860..95bc906c6c044c51548aa864326cc93f29a6042a 100644
--- a/unittests/test_configuration.py
+++ b/unittests/test_configuration.py
@@ -45,24 +45,24 @@ def temp_ini_files():
         remove("pylinkahead.ini")
     if created_temp_ini_home:
         remove(expanduser("~/.pylinkahead.ini"))
-    environ["PYCAOSDBINI"] = "~/.pylinkahead.ini"
+    environ["PYLINKAHEADINI"] = "~/.pylinkahead.ini"
 
 
 def test_config_ini_via_envvar(temp_ini_files):
 
     with raises(KeyError):
-        environ["PYCAOSDBINI"]
+        environ["PYLINKAHEADINI"]
 
-    environ["PYCAOSDBINI"] = "bla bla"
-    assert environ["PYCAOSDBINI"] == "bla bla"
+    environ["PYLINKAHEADINI"] = "bla bla"
+    assert environ["PYLINKAHEADINI"] == "bla bla"
     # test wrong configuration file in envvar
     with pytest.raises(RuntimeError):
         db.configuration._read_config_files()
     # test good configuration file in envvar
-    environ["PYCAOSDBINI"] = "~/.pylinkahead.ini"
+    environ["PYLINKAHEADINI"] = "~/.pylinkahead.ini"
     assert expanduser("~/.pylinkahead.ini") in db.configuration._read_config_files()
     # test without envvar
-    environ.pop("PYCAOSDBINI")
+    environ.pop("PYLINKAHEADINI")
     assert expanduser("~/.pylinkahead.ini") in db.configuration._read_config_files()
     # test configuration file in cwd
     assert join(getcwd(), "pylinkahead.ini") in db.configuration._read_config_files()