diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractPythonCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractPythonCodegen.java
index 080112520cae4acf1c9109f1bfeb437708dad323..fa38462af854f1820eace915887529f34317a8d9 100644
--- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractPythonCodegen.java
+++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractPythonCodegen.java
@@ -637,6 +637,8 @@ public abstract class AbstractPythonCodegen extends DefaultCodegen implements Co
         String sanitizedName = sanitizeName(name); // FIXME: a parameter should not be assigned. Also declare the methods parameters as 'final'.
         // remove dollar sign
         sanitizedName = sanitizedName.replaceAll("$", "");
+        // remove whitespace
+        sanitizedName = sanitizedName.replaceAll("\\s+", "");
 
         String nameWithPrefixSuffix = sanitizedName;
         if (!StringUtils.isEmpty(modelNamePrefix)) {
diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PythonExperimentalClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PythonExperimentalClientCodegen.java
index 09708dd1daf9d2d13ad2dc559e9f6b83249acc28..dac8c99bc5680d720d6f6db2ac2e95ecca5bd366 100644
--- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PythonExperimentalClientCodegen.java
+++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PythonExperimentalClientCodegen.java
@@ -98,6 +98,14 @@ public class PythonExperimentalClientCodegen extends AbstractPythonCodegen {
     protected CodegenIgnoreProcessor ignoreProcessor;
     protected TemplateProcessor templateProcessor = null;
 
+    // for apis.tags imports
+    private Map<String, String> tagModuleNameToApiClassname = new LinkedHashMap<>();
+    // for apis.tags enum tag definition
+    private Map<String, String> enumToTag = new LinkedHashMap<>();
+    // for apis.tags tag api definition
+    private Map<String, String> tagEnumToApiClassname = new LinkedHashMap<>();
+
+
     public PythonExperimentalClientCodegen() {
         super();
         loadDeepObjectIntoItems = false;
@@ -473,6 +481,14 @@ public class PythonExperimentalClientCodegen extends AbstractPythonCodegen {
         }
     }
 
+    @Override
+    public String toApiName(String name) {
+        if (name.length() == 0) {
+            return "DefaultApi";
+        }
+        return toModelName(name) + apiNameSuffix;
+    }
+
     /*
     I made this method because endpoint parameters not contain a lot of needed metadata
     It is very verbose to write all of this info into the api template
@@ -491,11 +507,38 @@ public class PythonExperimentalClientCodegen extends AbstractPythonCodegen {
         List<List<Object>> testFiles = new ArrayList<>();
         String outputFilename;
 
+        // endpoint tags may not exist in the root of the spec file
+        // this is allowed per openapi
+        // because spec tags may be empty ro incomplete, tags are also accumulated from endpoints
+        List<Tag> tags = openAPI.getTags();
+        if (tags != null) {
+            for (Tag tag: tags) {
+                String tagName = tag.getName();
+                String tagModuleName = toApiFilename(tagName);
+                String apiClassname = toApiName(tagName);
+                tagModuleNameToApiClassname.put(tagModuleName, apiClassname);
+                String tagEnum = toEnumVarName(tagName, "str");
+                enumToTag.put(tagEnum, tagName);
+                tagEnumToApiClassname.put(tagEnum, apiClassname);
+            }
+        }
+
         OperationMap operations = objs.getOperations();
         List<CodegenOperation> codegenOperations = operations.getOperation();
         HashMap<String, String> pathModuleToPath = new HashMap<>();
         // paths.some_path.post.py (single endpoint definition)
         for (CodegenOperation co: codegenOperations) {
+            if (co.tags != null) {
+                for (Tag tag: co.tags) {
+                    String tagName = tag.getName();
+                    String tagModuleName = toApiFilename(tagName);
+                    String apiClassname = toApiName(tagName);
+                    tagModuleNameToApiClassname.put(tagModuleName, apiClassname);
+                    String tagEnum = toEnumVarName(tagName, "str");
+                    enumToTag.put(tagEnum, tagName);
+                    tagEnumToApiClassname.put(tagEnum, apiClassname);
+                }
+            }
             String path = co.path;
             String pathModuleName = co.nickname;
             if (!pathModuleToPath.containsKey(pathModuleName)) {
@@ -530,22 +573,6 @@ public class PythonExperimentalClientCodegen extends AbstractPythonCodegen {
             pathEnumToApiClassname.put(pathEnumVar, apiClassName);
             pathModuleToApiClassname.put(toVarName(path), apiClassName);
         }
-        List<Tag> tags = openAPI.getTags();
-        // for imports
-        Map<String, String> tagModuleNameToApiClassname = new LinkedHashMap<>();
-        // for enum tag definition
-        Map<String, String> enumToTag = new LinkedHashMap<>();
-        // for tag api definition
-        Map<String, String> tagEnumToApiClassname = new LinkedHashMap<>();
-        for (Tag tag: tags) {
-            String tagName = tag.getName();
-            String tagModuleName = toApiFilename(tagName);
-            String apiClassname = toApiName(tagName);
-            tagModuleNameToApiClassname.put(tagModuleName, apiClassname);
-            String tagEnum = toEnumVarName(tagName, "str");
-            enumToTag.put(tagEnum, tagName);
-            tagEnumToApiClassname.put(tagEnum, apiClassname);
-        }
         // Note: __init__apis.handlebars is generated as a supporting file
         // apis.tag_to_api.py
         Map<String, Object> tagToApiMap = new HashMap<>();
@@ -2617,6 +2644,17 @@ public class PythonExperimentalClientCodegen extends AbstractPythonCodegen {
         }
     }
 
+    /**
+     * Note: a custom version of this function is used so the original tag value can be used
+     *
+     * @param tag Tag
+     * @return the tag to use
+     */
+    @Override
+    public String sanitizeTag(String tag) {
+        return tag;
+    }
+
     @Override
     public void postProcess() {
         System.out.println("################################################################################");
diff --git a/samples/openapi3/client/petstore/python-experimental/petstore_api/apis/tag_to_api.py b/samples/openapi3/client/petstore/python-experimental/petstore_api/apis/tag_to_api.py
index 553855d9707d194ee5686cd687ad88969ddbcc98..c94ea467e25a7e37a1f84374849f9c7a650a5f4f 100644
--- a/samples/openapi3/client/petstore/python-experimental/petstore_api/apis/tag_to_api.py
+++ b/samples/openapi3/client/petstore/python-experimental/petstore_api/apis/tag_to_api.py
@@ -4,6 +4,10 @@ from petstore_api.apis.tags import TagValues
 from petstore_api.apis.tags.pet_api import PetApi
 from petstore_api.apis.tags.store_api import StoreApi
 from petstore_api.apis.tags.user_api import UserApi
+from petstore_api.apis.tags.another_fake_api import AnotherFakeApi
+from petstore_api.apis.tags.default_api import DefaultApi
+from petstore_api.apis.tags.fake_api import FakeApi
+from petstore_api.apis.tags.fake_classname_tags123_api import FakeClassnameTags123Api
 
 TagToApi = typing.TypedDict(
     'TagToApi',
@@ -11,6 +15,10 @@ TagToApi = typing.TypedDict(
         TagValues.PET: PetApi,
         TagValues.STORE: StoreApi,
         TagValues.USER: UserApi,
+        TagValues.ANOTHERFAKE: AnotherFakeApi,
+        TagValues.DEFAULT: DefaultApi,
+        TagValues.FAKE: FakeApi,
+        TagValues.FAKE_CLASSNAME_TAGS_123: FakeClassnameTags123Api,
     }
 )
 
@@ -19,5 +27,9 @@ tag_to_api = TagToApi(
         TagValues.PET: PetApi,
         TagValues.STORE: StoreApi,
         TagValues.USER: UserApi,
+        TagValues.ANOTHERFAKE: AnotherFakeApi,
+        TagValues.DEFAULT: DefaultApi,
+        TagValues.FAKE: FakeApi,
+        TagValues.FAKE_CLASSNAME_TAGS_123: FakeClassnameTags123Api,
     }
 )
diff --git a/samples/openapi3/client/petstore/python-experimental/petstore_api/apis/tags/__init__.py b/samples/openapi3/client/petstore/python-experimental/petstore_api/apis/tags/__init__.py
index d24757e1cbbb5ec1956f9660e7982abf9d9621a7..a8ec6fddd9a1bec65fb46c9e67399bc9c0e92205 100644
--- a/samples/openapi3/client/petstore/python-experimental/petstore_api/apis/tags/__init__.py
+++ b/samples/openapi3/client/petstore/python-experimental/petstore_api/apis/tags/__init__.py
@@ -9,3 +9,7 @@ class TagValues(str, enum.Enum):
     PET = "pet"
     STORE = "store"
     USER = "user"
+    ANOTHERFAKE = "$another-fake?"
+    DEFAULT = "default"
+    FAKE = "fake"
+    FAKE_CLASSNAME_TAGS_123 = "fake_classname_tags 123#$%^"