diff --git a/pyotrs/lib.py b/pyotrs/lib.py
index 92114d0fee8147e6dde40e09adc562c2c0eeacf0..c070a3d904b1d5c835a0930485e0539cc582c6bf 100644
--- a/pyotrs/lib.py
+++ b/pyotrs/lib.py
@@ -1421,7 +1421,7 @@ class Client(object):
         if not self.session_id_store.value:
             raise SessionNotCreated("Call session_create() or "
                                     "session_restore_or_set_up_new() first")
-        self.operation = "PublicCategoryList    "
+        self.operation = "PublicCategoryList"
 
         payload = {
             "SessionID": self.session_id_store.value
@@ -1432,7 +1432,17 @@ class Client(object):
             return self.result
 
     def faq_public_faq_get(self, item_ids=None, attachment_contents=True):
-        """faq_public_category_list"""
+        """faq_public_category_list
+
+        Args:
+            item_ids (list): list of item IDs
+            attachment_contents (bool): whether to retrieve content of FAQ attachments
+
+        Returns:
+            **list**: of **dict** containing FAQ data
+
+        """
+
         if not self.session_id_store.value:
             raise SessionNotCreated("Call session_create() or "
                                     "session_restore_or_set_up_new() first")
@@ -1454,20 +1464,65 @@ class Client(object):
         if not attachment_contents:
             payload.update({"GetAttachmentContents": 0})
 
-        return self._parse_and_validate_response(self._send_request(payload))
+        if self._parse_and_validate_response(self._send_request(payload)):
+            return self.result
+
+    def faq_public_faq_search(self, what=None, number=None, title=None, search_dict=None):
+        """faq_public_category_list
+
+        Args:
+            what (str):
+            number (str):
+            title (str):
+            search_dict (dict):
+
+        Returns:
+            **list**: of found FAQ item IDs
+
+        # Original documentation:
+        # perform PublicFAQSearch Operation. This will return a list of public FAQ entries.
+        #       Number = > '*134*',              # (optional)
+        #       Title = > '*some title*',        # (optional)
+        #
+        #       # is searching in Number, Title, Keyword and Field1-6
+        #       What = > '*some text*',          # (optional)
+        #
+        #       Keyword = > '*webserver*',       # (optional)
+        #       LanguageIDs = > [4, 5, 6],       # (optional)
+        #       CategoryIDs = > [7, 8, 9],       # (optional)
+
+        """
 
-    def faq_public_faq_search(self):
-        """faq_public_category_list"""
         if not self.session_id_store.value:
             raise SessionNotCreated("Call session_create() or "
                                     "session_restore_or_set_up_new() first")
         self.operation = "PublicFAQSearch"
 
         payload = {
-            "SessionID": self.session_id_store.value
+            "SessionID": self.session_id_store.value,
         }
 
-        return self._parse_and_validate_response(self._send_request(payload))
+        if what:
+            payload.update({"What": what})
+
+        if number:
+            payload.update({"Number": number})
+
+        if title:
+            payload.update({"Title": title})
+
+        if search_dict:
+            if not isinstance(search_dict, dict):
+                raise ArgumentInvalidError("Expecting dict for search_dict!")
+            payload.update(search_dict)
+
+        if self._parse_and_validate_response(self._send_request(payload)):
+            if not self.result:
+                return []
+            elif len(self.result) == 1:
+                return [self.result]
+            else:
+                return self.result
 
     """
     GenericInterface::Operation::Link::LinkAdd
@@ -1761,8 +1816,6 @@ class Client(object):
             elif route in self.routes_link:
                 self._url = ("{0}/otrs/nph-genericinterface.pl/Webservice/"
                              "{1}{2}".format(self.baseurl, self.ws_link, route))
-            else:
-                raise NotImplementedError("could not _build_url for {0}".format(self))
 
         return self._url
 
@@ -1876,6 +1929,17 @@ class Client(object):
                 self.result = _link_list
                 return True
 
+        # PublicFAQSearch result can be empty
+        if self.operation in "PublicFAQSearch":
+            _public_faq_search_result_list = self.result_json.get(self._result_type, None)
+            if not _public_faq_search_result_list:
+                if self.result_json["Error"]["ErrorCode"] == "PublicFAQSearch.NotFAQData":
+                    self.result = []
+                    return True
+            else:
+                self.result = _public_faq_search_result_list
+                return True
+
         # now handle other operations
         if self.result_json.get(self._result_type, None):
             self._result_error = False
diff --git a/tests/test_article.py b/tests/test_article.py
index f642188fe137a93430fcedf6252eeff5da760d52..7a7ba7bce909c431dc09b90fbcd3da51dbb5a7df 100644
--- a/tests/test_article.py
+++ b/tests/test_article.py
@@ -453,6 +453,7 @@ class ArticleTests(unittest.TestCase):
 def main():
     unittest.main()
 
+
 if __name__ == '__main__':
     main()
 
diff --git a/tests/test_attachment.py b/tests/test_attachment.py
index 2195a0e05c145106046edf6bacdde749eb3aa5f6..031a039ac000ea96050ffbfeba373de0c9d33e26 100644
--- a/tests/test_attachment.py
+++ b/tests/test_attachment.py
@@ -127,6 +127,7 @@ class AttachmentTests(unittest.TestCase):
 def main():
     unittest.main()
 
+
 if __name__ == '__main__':
     main()
 
diff --git a/tests/test_client.py b/tests/test_client.py
index 06c54e7b01380e113eda02000cbb4a05bcd3c4ff..d543d1be918129a63a9d31fb6c4a3abe145e3b06 100644
--- a/tests/test_client.py
+++ b/tests/test_client.py
@@ -954,18 +954,34 @@ class ClientTests(unittest.TestCase):
                                'Call session_create.*',
                                obj.faq_public_faq_get)
 
-    def test_faq_public_faq_get_no_item_ids(self):
-        """Test faq_public_faq_get - no item_ids"""
+    def test_faq_public_faq_get_no_item_id(self):
+        """Test faq_public_faq_get - no item id"""
         obj = Client(baseurl="http://fqdn")
         obj.session_id_store.value = "some_session_id"
+
         self.assertRaisesRegex(ArgumentMissingError,
                                'item_ids is required',
                                obj.faq_public_faq_get)
 
     @mock.patch('pyotrs.Client._send_request')
     @mock.patch('pyotrs.Client._parse_and_validate_response', autospec=True)
-    def test_faq_public_faq_get_list_of_ids(self, mock_parse_validate, mock_send_req):
-        """Tests faq_public_faq_get - ok"""
+    def test_faq_public_faq_get(self, mock_parse_validate, mock_send_req):
+        """Tests faq_public_faq_get - with str, no attachment content - ok"""
+        # create object
+        obj = Client(baseurl="http://fqdn")
+        obj.session_id_store.value = "some_session_id"
+        mock_parse_validate.return_value = True
+        mock_send_req.return_value = "mock"
+
+        obj.faq_public_faq_get("1", attachment_contents=False)
+
+        self.assertEqual(mock_parse_validate.call_count, 1)
+        self.assertEqual(mock_send_req.call_count, 1)
+
+    @mock.patch('pyotrs.Client._send_request')
+    @mock.patch('pyotrs.Client._parse_and_validate_response', autospec=True)
+    def test_faq_public_faq_get_list(self, mock_parse_validate, mock_send_req):
+        """Tests faq_public_faq_get - with list - ok """
         # create object
         obj = Client(baseurl="http://fqdn")
         obj.session_id_store.value = "some_session_id"
@@ -977,54 +993,80 @@ class ClientTests(unittest.TestCase):
         self.assertEqual(mock_parse_validate.call_count, 1)
         self.assertEqual(mock_send_req.call_count, 1)
 
+    def test_faq_public_faq_search_no_session_created(self):
+        """Test faq_public_faq_search - no session"""
+        obj = Client(baseurl="http://fqdn")
+        self.assertRaisesRegex(SessionNotCreated,
+                               'Call session_create.*',
+                               obj.faq_public_faq_search)
+
+    def test_faq_public_faq_search_invalid_search_dict(self):
+        """Test faq_public_faq_search - invalid search dict"""
+        obj = Client(baseurl="http://fqdn")
+        obj.session_id_store.value = "some_session_id"
+        self.assertRaisesRegex(ArgumentInvalidError,
+                               'Expecting dict for search_dict!',
+                               obj.faq_public_faq_search, search_dict="this is no dict")
+
     @mock.patch('pyotrs.Client._send_request')
     @mock.patch('pyotrs.Client._parse_and_validate_response', autospec=True)
-    def test_faq_public_faq_get_single_id(self, mock_parse_validate, mock_send_req):
-        """Tests faq_public_faq_get - ok"""
+    def test_faq_public_faq_search(self, mock_parse_validate, mock_send_req):
+        """Tests faq_public_faq_search - ok"""
         # create object
         obj = Client(baseurl="http://fqdn")
         obj.session_id_store.value = "some_session_id"
         mock_parse_validate.return_value = True
         mock_send_req.return_value = "mock"
 
-        obj.faq_public_faq_get(4711)
+        obj.faq_public_faq_search("foobar")
 
         self.assertEqual(mock_parse_validate.call_count, 1)
         self.assertEqual(mock_send_req.call_count, 1)
 
     @mock.patch('pyotrs.Client._send_request')
     @mock.patch('pyotrs.Client._parse_and_validate_response', autospec=True)
-    def test_faq_public_faq_get_list_no_content(self, mock_parse_validate, mock_send_req):
-        """Tests faq_public_faq_get - ok"""
+    def test_faq_public_faq_search_number(self, mock_parse_validate, mock_send_req):
+        """Tests faq_public_faq_search - number - ok"""
         # create object
         obj = Client(baseurl="http://fqdn")
         obj.session_id_store.value = "some_session_id"
         mock_parse_validate.return_value = True
         mock_send_req.return_value = "mock"
+        obj.result = []
 
-        obj.faq_public_faq_get([1, 2], attachment_contents=False)
+        obj.faq_public_faq_search(number="21")
 
         self.assertEqual(mock_parse_validate.call_count, 1)
         self.assertEqual(mock_send_req.call_count, 1)
 
-    def test_faq_public_faq_search_no_session_created(self):
-        """Test faq_public_faq_search - no session"""
+    @mock.patch('pyotrs.Client._send_request')
+    @mock.patch('pyotrs.Client._parse_and_validate_response', autospec=True)
+    def test_faq_public_faq_search_title(self, mock_parse_validate, mock_send_req):
+        """Tests faq_public_faq_search - title - ok"""
+        # create object
         obj = Client(baseurl="http://fqdn")
-        self.assertRaisesRegex(SessionNotCreated,
-                               'Call session_create.*',
-                               obj.faq_public_faq_search)
+        obj.session_id_store.value = "some_session_id"
+        mock_parse_validate.return_value = True
+        mock_send_req.return_value = "mock"
+        obj.result = [u'1']
+
+        obj.faq_public_faq_search(title="Test-Title")
+
+        self.assertEqual(mock_parse_validate.call_count, 1)
+        self.assertEqual(mock_send_req.call_count, 1)
 
     @mock.patch('pyotrs.Client._send_request')
     @mock.patch('pyotrs.Client._parse_and_validate_response', autospec=True)
-    def test_faq_public_faq_search(self, mock_parse_validate, mock_send_req):
-        """Tests faq_public_faq_search - ok"""
+    def test_faq_public_faq_search_search_dict(self, mock_parse_validate, mock_send_req):
+        """Tests faq_public_faq_search - search_dict - ok"""
         # create object
         obj = Client(baseurl="http://fqdn")
         obj.session_id_store.value = "some_session_id"
         mock_parse_validate.return_value = True
         mock_send_req.return_value = "mock"
+        obj.result = [u'3', u'4']
 
-        obj.faq_public_faq_search()
+        obj.faq_public_faq_search(search_dict={"Keyword": "Password"})
 
         self.assertEqual(mock_parse_validate.call_count, 1)
         self.assertEqual(mock_send_req.call_count, 1)
@@ -1340,6 +1382,24 @@ class ClientTests(unittest.TestCase):
                                "could not _build_url for.*",
                                obj._build_url)
 
+    def test__build_url_faq_language_list(self):
+        """Test _build_url for faq_language_list"""
+        obj = Client(baseurl="http://fqdn")
+        obj.operation = "PublicCategoryList"
+
+        self.assertEqual("http://fqdn/otrs/nph-genericinterface.pl/Webservice/"
+                         "GenericFAQConnectorREST/PublicCategoryList",
+                         obj._build_url())
+
+    def test__build_url_faq_category_list(self):
+        """Test _build_url for faq_category_list"""
+        obj = Client(baseurl="http://fqdn")
+        obj.operation = "LanguageList"
+
+        self.assertEqual("http://fqdn/otrs/nph-genericinterface.pl/Webservice/"
+                         "GenericFAQConnectorREST/LanguageList",
+                         obj._build_url())
+
     def test__build_url_link_add(self):
         """Test _build_url for link_add"""
         obj = Client(baseurl="http://fqdn")
@@ -1370,38 +1430,33 @@ class ClientTests(unittest.TestCase):
 
     def test__send_request_invalid_method(self):
         # ""Test _send_request with invalid http method ""
-
-        my_ticket_connector_cfg = {
+        operation_mapping_invalid = {
             'Name': 'GenericTicketConnectorREST',
             'Config': {
-                'SessionCreate': {'RequestMethod': 'POST',
-                                  'Route': '/Session',
-                                  'Result': 'SessionID'},
-                'TicketCreate': {'RequestMethod': 'POST',
-                                 'Route': '/Ticket',
-                                 'Result': 'TicketID'},
-                'TicketGet': {'RequestMethod': 'FOOBAR',
-                              'Route': '/Ticket/:TicketID',
-                              'Result': 'Ticket'},
-                'TicketGetList': {'RequestMethod': 'GET',
-                                  'Route': '/TicketList',
-                                  'Result': 'Ticket'},
-                'TicketSearch': {'RequestMethod': 'GET',
-                                 'Route': '/Ticket',
-                                 'Result': 'TicketID'},
-                'TicketUpdate': {'RequestMethod': 'PATCH',
-                                 'Route': '/Ticket/:TicketID',
-                                 'Result': 'TicketID'},
+                'SessionCreate':
+                    {'Result': 'SessionID', 'RequestMethod': 'FB', 'Route': '/Session'},
+                'TicketCreate':
+                    {'Result': 'TicketID', 'RequestMethod': 'FB', 'Route': '/Ticket'},
+                'TicketGet':
+                    {'Result': 'Ticket', 'RequestMethod': 'FB', 'Route': '/Ticket/:TicketID'},
+                'TicketGetList':
+                    {'Result': 'Ticket', 'RequestMethod': 'FB', 'Route': '/TicketList'},
+                'TicketSearch':
+                    {'Result': 'TicketID', 'RequestMethod': 'FB', 'Route': '/Ticket'},
+                'TicketUpdate':
+                    {'Result': 'TicketID', 'RequestMethod': 'FB', 'Route': '/Ticket/:TicketID'}
             }
         }
 
-        obj = Client(baseurl="http://fqdn", webservice_config_ticket=my_ticket_connector_cfg)
-        obj.operation = "TicketGet"
+        obj = Client(baseurl="http://fqdn", webservice_config_ticket=operation_mapping_invalid)
+
+        obj.operation = "TicketSearch"
+        obj._result_type = obj.ws_config[obj.operation]["Result"]
+
         self.assertRaisesRegex(ValueError,
                                'invalid http_method',
                                obj._send_request,
-                               payload={"foo": "bar"},
-                               ticket_id=1)
+                               payload={"foo": "bar"})
 
     @mock.patch('pyotrs.lib.requests.request')
     def test__send_request_ok(self, mock_requests_req):
@@ -1595,6 +1650,39 @@ class ClientTests(unittest.TestCase):
         self.assertTrue(obj._result_error)
         self.assertDictEqual(obj.result_json, {u'FooBar': [u'1', u'3']})
 
+    def test__validate_response_operation_faq_public_faq_search_ok(self):
+        """Test _validate_response with faq_public_faq_search ok"""
+        obj = Client(baseurl="http://localhost")
+        obj.operation = "PublicFAQSearch"
+        obj._result_type = obj.ws_config[obj.operation]["Result"]
+
+        mocked_response = mock.Mock(spec=requests.Response)
+        mocked_response.status_code = 200
+        mocked_response.json.return_value = {"ID": [u"3", u"2", u"1"]}
+
+        obj._parse_and_validate_response(mocked_response)
+        self.assertEqual(obj._result_type, 'ID')
+        self.assertDictEqual(obj.result_json, {"ID": [u"3", u"2", u"1"]})
+
+    def test__validate_response_operation_faq_public_faq_search_no_result(self):
+        """Test _validate_response with faq_public_faq_search no result"""
+        obj = Client(baseurl="http://localhost")
+        obj.operation = "PublicFAQSearch"
+        obj._result_type = obj.ws_config[obj.operation]["Result"]
+
+        mocked_response = mock.Mock(spec=requests.Response)
+        mocked_response.status_code = 200
+        _dct = {u'Error': {u'ErrorCode': u'PublicFAQSearch.NotFAQData',
+                           u'ErrorMessage': u'PublicFAQSearch: Could not get FAQ data in '
+                                            u'Kernel::GenericInterface::Operation::FAQ::'
+                                            u'PublicFAQSearch::Run()'}}
+        mocked_response.json.return_value = _dct
+
+        obj._parse_and_validate_response(mocked_response)
+        self.assertEqual(obj._result_type, 'ID')
+
+        self.assertDictEqual(obj.result_json, _dct)
+
     def test__validate_response_operation_link_add_ok(self):
         """Test _validate_response with link_add ok"""
         obj = Client(baseurl="http://localhost")
@@ -1665,6 +1753,7 @@ class ClientTests(unittest.TestCase):
 def main():
     unittest.main()
 
+
 if __name__ == '__main__':
     main()
 
diff --git a/tests/test_dynamic_field.py b/tests/test_dynamic_field.py
index 642c361799d138ddcdb124356df1b31c495d5fc5..78004b7d8c985161a5566e9b8ead8eb5488a4ebc 100644
--- a/tests/test_dynamic_field.py
+++ b/tests/test_dynamic_field.py
@@ -97,6 +97,7 @@ class DynamicFieldTests(unittest.TestCase):
 def main():
     unittest.main()
 
+
 if __name__ == '__main__':
     main()
 
diff --git a/tests/test_session_store.py b/tests/test_session_store.py
index c1f6714aee37b2fc3c454b3984c03761e36c6910..53fca073cbf19280cb3f4d38a52e1c6b38a08f43 100644
--- a/tests/test_session_store.py
+++ b/tests/test_session_store.py
@@ -309,6 +309,7 @@ class SessionStoreTests(unittest.TestCase):
 def main():
     unittest.main()
 
+
 if __name__ == '__main__':
     main()
 
diff --git a/tests/test_ticket.py b/tests/test_ticket.py
index eb6bd14fc209d302791f10f7ba8efe152be74860..900536dfa6da470f922a74ad83607e7d53a9e8e3 100644
--- a/tests/test_ticket.py
+++ b/tests/test_ticket.py
@@ -610,6 +610,7 @@ class TicketTests(unittest.TestCase):
 def main():
     unittest.main()
 
+
 if __name__ == '__main__':
     main()
 
diff --git a/tox.ini b/tox.ini
index 6c006600e3af5aa8fa019fe16c82758f8fbf26d2..ac9ea849a076f018337258402ca0223f6ef40ad3 100644
--- a/tox.ini
+++ b/tox.ini
@@ -75,9 +75,9 @@ deps =
 
 commands =
     flake8 \
-      --max-complexity=15 \
-      --exclude=./build,.venv,.tox,dist,docs, \
-      --ignore=Q000,E305 \
+      --max-complexity=20 \
+      --exclude=./build,.venv,.tox,.eggs,dist,docs, \
+      --ignore=Q000 \
       --max-line-length=99 \
       []