diff --git a/pyalex/api.py b/pyalex/api.py
index 034bafe..c2e95b4 100644
--- a/pyalex/api.py
+++ b/pyalex/api.py
@@ -21,21 +21,55 @@ class AlexConfig(dict):
 config = AlexConfig(email=None, api_key=None, openalex_url="https://api.openalex.org")
 
 
-def _flatten_kv(k, v):
+def _flatten_kv(d, prefix=""):
 
-    if isinstance(v, dict):
+    if isinstance(d, dict):
 
-        if len(v.keys()) > 1:
-            raise ValueError()
+        t = []
+        for k, v in d.items():
+            if isinstance(v, list):
+                t.extend([f"{prefix}.{k}:{i}" for i in v])
+            else:
+                new_prefix = f"{prefix}.{k}" if prefix else f"{k}"
+                x = _flatten_kv(v, prefix=new_prefix)
+                t.append(x)
 
-        k_0 = list(v.keys())[0]
-        return str(k) + "." + _flatten_kv(k_0, v[k_0])
+        return ",".join(t)
     else:
 
         # workaround for bug https://groups.google.com/u/1/g/openalex-users/c/t46RWnzZaXc
-        v = str(v).lower() if isinstance(v, bool) else v
-
-        return str(k) + ":" + str(v)
+        d = str(d).lower() if isinstance(d, bool) else d
+
+        return f"{prefix}:{d}"
+
+
+def _params_merge(params, add_params):
+
+    for k, v in add_params.items():
+        if (
+            k in params
+            and isinstance(params[k], dict)
+            and isinstance(add_params[k], dict)
+        ):
+            _params_merge(params[k], add_params[k])
+        elif (
+            k in params
+            and not isinstance(params[k], list)
+            and isinstance(add_params[k], list)
+        ):
+            # example: params="a" and add_params=["b", "c"]
+            params[k] = [params[k]] + add_params[k]
+        elif (
+            k in params
+            and isinstance(params[k], list)
+            and not isinstance(add_params[k], list)
+        ):
+            # example: params=["b", "c"] and add_params="a"
+            params[k] = params[k] + [add_params[k]]
+        elif k in params:
+            params[k] = [params[k], add_params[k]]
+        else:
+            params[k] = add_params[k]
 
 
 def invert_abstract(inv_index):
@@ -104,6 +138,7 @@ class Publisher(OpenAlexEntity):
 
 # deprecated
 
+
 def Venue(*args, **kwargs):
 
     # warn about deprecation
@@ -152,7 +187,7 @@ class BaseOpenAlex(object):
 
     """Base class for OpenAlex objects."""
 
-    def __init__(self, params={}):
+    def __init__(self, params=None):
 
         self.params = params
 
@@ -185,7 +220,7 @@ class BaseOpenAlex(object):
         res = requests.get(
             url,
             headers={"User-Agent": "pyalex/" + __version__, "email": config.email},
-            params=params
+            params=params,
         )
         res.raise_for_status()
         res_json = res.json()
@@ -195,10 +230,13 @@ class BaseOpenAlex(object):
     @property
     def url(self):
 
+        if not self.params:
+            return self.url_collection
+
         l = []
         for k, v in self.params.items():
             if k in ["filter", "sort"]:
-                l.append(k + "=" + ",".join(_flatten_kv(k, d) for k, d in v.items()))
+                l.append(k + "=" + _flatten_kv(v))
             elif v is None:
                 pass
             else:
@@ -214,21 +252,24 @@ class BaseOpenAlex(object):
         if per_page is not None and (per_page < 1 or per_page > 200):
             raise ValueError("per_page should be a number between 1 and 200.")
 
-        self.params["per-page"] = per_page
-        self.params["page"] = page
-        self.params["cursor"] = cursor
+        self._add_params("per-page", per_page)
+        self._add_params("page", page)
+        self._add_params("cursor", cursor)
 
         params = {"api_key": config.api_key} if config.api_key else {}
         res = requests.get(
             self.url,
             headers={"User-Agent": "pyalex/" + __version__, "email": config.email},
-            params=params
+            params=params,
         )
 
         # handle query errors
         if res.status_code == 403:
             res_json = res.json()
-            if isinstance(res_json["error"], str) and "query parameters" in res_json["error"]:
+            if (
+                isinstance(res_json["error"], str)
+                and "query parameters" in res_json["error"]
+            ):
                 raise QueryError(res_json["message"])
         res.raise_for_status()
 
@@ -254,68 +295,40 @@ class BaseOpenAlex(object):
 
         return self.__getitem__("random")
 
-    def filter(self, **kwargs):
-
-        p = self.params.copy()
+    def _add_params(self, argument, new_params):
 
-        if "filter" in p:
-            p["filter"] = {**p["filter"], **kwargs}
+        if self.params is None:
+            self.params = {argument: new_params}
+        elif argument in self.params and isinstance(self.params[argument], dict):
+            _params_merge(self.params[argument], new_params)
         else:
-            p["filter"] = kwargs
+            self.params[argument] = new_params
+
+        logging.debug("Params updated:", self.params)
 
-        self.params = p
-        logging.debug("Params updated:", p)
+    def filter(self, **kwargs):
 
+        self._add_params("filter", kwargs)
         return self
 
     def search_filter(self, **kwargs):
 
-        search_kwargs = {f"{k}.search": v for k, v in kwargs.items()}
-
-        p = self.params.copy()
-
-        if "filter" in p:
-            p["filter"] = {**p["filter"], **search_kwargs}
-        else:
-            p["filter"] = search_kwargs
-
-        self.params = p
-        logging.debug("Params updated:", p)
-
+        self._add_params("filter", {f"{k}.search": v for k, v in kwargs.items()})
         return self
 
     def sort(self, **kwargs):
 
-        p = self.params.copy()
-
-        if "sort" in p:
-            p["sort"] = {**p["sort"], **kwargs}
-        else:
-            p["sort"] = kwargs
-
-        self.params = p
-        logging.debug("Params updated:", p)
-
+        self._add_params("sort", kwargs)
         return self
 
     def group_by(self, group_key):
 
-        p = self.params.copy()
-        p["group-by"] = group_key
-        self.params = p
-
-        logging.debug("Params updated:", p)
-
+        self._add_params("group-by", group_key)
         return self
 
     def search(self, s):
 
-        p = self.params.copy()
-        p["search"] = s
-        self.params = p
-
-        logging.debug("Params updated:", p)
-
+        self._add_params("search", s)
         return self
 
 
@@ -357,6 +370,7 @@ class Publishers(BaseOpenAlex):
 
 # deprecated
 
+
 def Venues(*args, **kwargs):
 
     # warn about deprecation

diff --git a/tests/test_pyalex.py b/tests/test_pyalex.py
index 54a01ef..2ba60a8 100644
--- a/tests/test_pyalex.py
+++ b/tests/test_pyalex.py
@@ -348,3 +348,35 @@ def test_ngrams_with_metadata():
 def test_random_publishers():
 
     assert isinstance(Publishers().random(), dict)
+
+
+def test_and_operator():
+
+    # https://github.com/J535D165/pyalex/issues/11
+    url = "https://api.openalex.org/works?filter=institutions.country_code:tw,institutions.country_code:hk,institutions.country_code:us,publication_year:2022"  # noqa
+
+    assert (
+        url
+        == Works()
+        .filter(
+            institutions={"country_code": ["tw", "hk", "us"]}, publication_year=2022
+        )
+        .url
+    )
+    assert (
+        url
+        == Works()
+        .filter(institutions={"country_code": "tw"})
+        .filter(institutions={"country_code": "hk"})
+        .filter(institutions={"country_code": "us"})
+        .filter(publication_year=2022)
+        .url
+    )
+    assert (
+        url
+        == Works()
+        .filter(institutions={"country_code": ["tw", "hk"]})
+        .filter(institutions={"country_code": "us"})
+        .filter(publication_year=2022)
+        .url
+    )

Add example for boolean operators to documentation
Thanks for providing pyalex! It would be helpful to have an example for the use of different boolean filters in the Readme. 

Example URL: https://api.openalex.org/works?filter=institutions.country_code:tw,institutions.country_code:hk
tests/test_pyalex.py::test_and_operator
tests/test_pyalex.py::test_config tests/test_pyalex.py::test_meta_entities tests/test_pyalex.py::test_works_params tests/test_pyalex.py::test_works tests/test_pyalex.py::test_per_page tests/test_pyalex.py::test_W4238809453_works tests/test_pyalex.py::test_W4238809453_works_abstract tests/test_pyalex.py::test_W4238809453_works_no_abstract tests/test_pyalex.py::test_W3128349626_works_abstract tests/test_pyalex.py::test_W3128349626_works_no_abstract tests/test_pyalex.py::test_work_error tests/test_pyalex.py::test_random_works tests/test_pyalex.py::test_multi_works tests/test_pyalex.py::test_works_multifilter tests/test_pyalex.py::test_works_url tests/test_pyalex.py::test_works_multifilter_meta tests/test_pyalex.py::test_query_error tests/test_pyalex.py::test_data_publications tests/test_pyalex.py::test_search tests/test_pyalex.py::test_search_filter tests/test_pyalex.py::test_cursor_by_hand tests/test_pyalex.py::test_basic_paging tests/test_pyalex.py::test_cursor_paging tests/test_pyalex.py::test_cursor_paging_n_max tests/test_pyalex.py::test_cursor_paging_n_max_none tests/test_pyalex.py::test_referenced_works tests/test_pyalex.py::test_serializable tests/test_pyalex.py::test_ngrams_without_metadata tests/test_pyalex.py::test_ngrams_with_metadata tests/test_pyalex.py::test_random_publishers