diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 3289dd0..c3d1561 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -14,7 +14,7 @@ repos:
     -   id: end-of-file-fixer
     -   id: trailing-whitespace
 -   repo: https://github.com/astral-sh/ruff-pre-commit
-    rev: v0.2.0
+    rev: v0.4.9
     hooks:
     -   id: ruff
         args:
diff --git a/src/fastapi_cli/cli.py b/src/fastapi_cli/cli.py
index d5bcb8e..03522a7 100644
--- a/src/fastapi_cli/cli.py
+++ b/src/fastapi_cli/cli.py
@@ -62,7 +62,7 @@ def _run(
     proxy_headers: bool = False,
 ) -> None:
     try:
-        use_uvicorn_app = get_import_string(path=path, app_name=app)
+        use_uvicorn_app, is_factory = get_import_string(path=path, app_name=app)
     except FastAPICLIException as e:
         logger.error(str(e))
         raise typer.Exit(code=1) from None
@@ -97,6 +97,7 @@ def _run(
         workers=workers,
         root_path=root_path,
         proxy_headers=proxy_headers,
+        factory=is_factory,
     )
 
 
diff --git a/src/fastapi_cli/discover.py b/src/fastapi_cli/discover.py
index f442438..a835406 100644
--- a/src/fastapi_cli/discover.py
+++ b/src/fastapi_cli/discover.py
@@ -3,7 +3,7 @@
 from dataclasses import dataclass
 from logging import getLogger
 from pathlib import Path
-from typing import Union
+from typing import Any, Callable, Tuple, Union, get_type_hints
 
 from rich import print
 from rich.padding import Padding
@@ -98,7 +98,9 @@ def get_module_data_from_path(path: Path) -> ModuleData:
     )
 
 
-def get_app_name(*, mod_data: ModuleData, app_name: Union[str, None] = None) -> str:
+def get_app_name(
+    *, mod_data: ModuleData, app_name: Union[str, None] = None
+) -> Tuple[str, bool]:
     try:
         mod = importlib.import_module(mod_data.module_import_str)
     except (ImportError, ValueError) as e:
@@ -119,26 +121,38 @@ def get_app_name(*, mod_data: ModuleData, app_name: Union[str, None] = None) ->
                 f"Could not find app name {app_name} in {mod_data.module_import_str}"
             )
         app = getattr(mod, app_name)
+        is_factory = False
         if not isinstance(app, FastAPI):
-            raise FastAPICLIException(
-                f"The app name {app_name} in {mod_data.module_import_str} doesn't seem to be a FastAPI app"
-            )
-        return app_name
-    for preferred_name in ["app", "api"]:
+            is_factory = check_factory(app)
+            if not is_factory:
+                raise FastAPICLIException(
+                    f"The app name {app_name} in {mod_data.module_import_str} doesn't seem to be a FastAPI app"
+                )
+        return app_name, is_factory
+    for preferred_name in ["app", "api", "create_app", "create_api"]:
         if preferred_name in object_names_set:
             obj = getattr(mod, preferred_name)
             if isinstance(obj, FastAPI):
-                return preferred_name
+                return preferred_name, False
+            if check_factory(obj):
+                return preferred_name, True
     for name in object_names:
         obj = getattr(mod, name)
         if isinstance(obj, FastAPI):
-            return name
+            return name, False
     raise FastAPICLIException("Could not find FastAPI app in module, try using --app")
 
 
+def check_factory(fn: Callable[[], Any]) -> bool:
+    """Checks whether the return-type of a factory function is FastAPI"""
+    type_hints = get_type_hints(fn)
+    return_type = type_hints.get("return")
+    return return_type is not None and issubclass(return_type, FastAPI)
+
+
 def get_import_string(
     *, path: Union[Path, None] = None, app_name: Union[str, None] = None
-) -> str:
+) -> Tuple[str, bool]:
     if not path:
         path = get_default_path()
     logger.info(f"Using path [blue]{path}[/blue]")
@@ -147,7 +161,7 @@ def get_import_string(
         raise FastAPICLIException(f"Path does not exist {path}")
     mod_data = get_module_data_from_path(path)
     sys.path.insert(0, str(mod_data.extra_sys_path))
-    use_app_name = get_app_name(mod_data=mod_data, app_name=app_name)
+    use_app_name, is_factory = get_app_name(mod_data=mod_data, app_name=app_name)
     import_example = Syntax(
         f"from {mod_data.module_import_str} import {use_app_name}", "python"
     )
@@ -164,4 +178,4 @@ def get_import_string(
     print(import_panel)
     import_string = f"{mod_data.module_import_str}:{use_app_name}"
     logger.info(f"Using import string [b green]{import_string}[/b green]")
-    return import_string
+    return import_string, is_factory
diff --git a/tests/assets/factory_create_api.py b/tests/assets/factory_create_api.py
new file mode 100644
index 0000000..f477471
--- /dev/null
+++ b/tests/assets/factory_create_api.py
@@ -0,0 +1,11 @@
+from fastapi import FastAPI
+
+
+def create_api() -> FastAPI:
+    app = FastAPI()
+
+    @app.get("/")
+    def app_root():
+        return {"message": "single file factory app"}
+
+    return app
diff --git a/tests/assets/factory_create_app.py b/tests/assets/factory_create_app.py
new file mode 100644
index 0000000..9980d5b
--- /dev/null
+++ b/tests/assets/factory_create_app.py
@@ -0,0 +1,24 @@
+from fastapi import FastAPI
+
+
+class App(FastAPI): ...
+
+
+def create_app_other() -> App:
+    app = App()
+
+    @app.get("/")
+    def app_root():
+        return {"message": "single file factory app inherited"}
+
+    return app
+
+
+def create_app() -> FastAPI:
+    app = FastAPI()
+
+    @app.get("/")
+    def app_root():
+        return {"message": "single file factory app"}
+
+    return app
diff --git a/tests/assets/package/mod/factory_api.py b/tests/assets/package/mod/factory_api.py
new file mode 100644
index 0000000..e303195
--- /dev/null
+++ b/tests/assets/package/mod/factory_api.py
@@ -0,0 +1,11 @@
+from fastapi import FastAPI
+
+
+def create_api() -> FastAPI:
+    app = FastAPI()
+
+    @app.get("/")
+    def root():
+        return {"message": "package create_api"}
+
+    return app
diff --git a/tests/assets/package/mod/factory_app.py b/tests/assets/package/mod/factory_app.py
new file mode 100644
index 0000000..a652b6e
--- /dev/null
+++ b/tests/assets/package/mod/factory_app.py
@@ -0,0 +1,11 @@
+from fastapi import FastAPI
+
+
+def create_app() -> FastAPI:
+    app = FastAPI()
+
+    @app.get("/")
+    def root():
+        return {"message": "package create_app"}
+
+    return app
diff --git a/tests/assets/package/mod/factory_inherit.py b/tests/assets/package/mod/factory_inherit.py
new file mode 100644
index 0000000..590afdb
--- /dev/null
+++ b/tests/assets/package/mod/factory_inherit.py
@@ -0,0 +1,14 @@
+from fastapi import FastAPI
+
+
+class App(FastAPI): ...
+
+
+def create_app() -> App:
+    app = App()
+
+    @app.get("/")
+    def root():
+        return {"message": "package build_app"}
+
+    return app
diff --git a/tests/assets/package/mod/factory_other.py b/tests/assets/package/mod/factory_other.py
new file mode 100644
index 0000000..ccd0f79
--- /dev/null
+++ b/tests/assets/package/mod/factory_other.py
@@ -0,0 +1,11 @@
+from fastapi import FastAPI
+
+
+def build_app() -> FastAPI:
+    app = FastAPI()
+
+    @app.get("/")
+    def root():
+        return {"message": "package build_app"}
+
+    return app
diff --git a/tests/test_cli.py b/tests/test_cli.py
index 44c14d2..b64330e 100644
--- a/tests/test_cli.py
+++ b/tests/test_cli.py
@@ -29,6 +29,7 @@ def test_dev() -> None:
                 "workers": None,
                 "root_path": "",
                 "proxy_headers": True,
+                "factory": False,
             }
         assert "Using import string single_file_app:app" in result.output
         assert (
@@ -40,6 +41,33 @@ def test_dev() -> None:
         assert "│  fastapi run" in result.output
 
 
+def test_dev_factory() -> None:
+    with changing_dir(assets_path):
+        with patch.object(uvicorn, "run") as mock_run:
+            result = runner.invoke(app, ["dev", "factory_create_app.py"])
+            assert result.exit_code == 0, result.output
+            assert mock_run.called
+            assert mock_run.call_args
+            assert mock_run.call_args.kwargs == {
+                "app": "factory_create_app:create_app",
+                "host": "127.0.0.1",
+                "port": 8000,
+                "reload": True,
+                "workers": None,
+                "root_path": "",
+                "proxy_headers": True,
+                "factory": True,
+            }
+        assert "Using import string factory_create_app:create_app" in result.output
+        assert (
+            "╭────────── FastAPI CLI - Development mode ───────────╮" in result.output
+        )
+        assert "│  Serving at: http://127.0.0.1:8000" in result.output
+        assert "│  API docs: http://127.0.0.1:8000/docs" in result.output
+        assert "│  Running in development mode, for production use:" in result.output
+        assert "│  fastapi run" in result.output
+
+
 def test_dev_args() -> None:
     with changing_dir(assets_path):
         with patch.object(uvicorn, "run") as mock_run:
@@ -71,6 +99,7 @@ def test_dev_args() -> None:
                 "workers": None,
                 "root_path": "/api",
                 "proxy_headers": False,
+                "factory": False,
             }
         assert "Using import string single_file_app:api" in result.output
         assert (
@@ -97,6 +126,7 @@ def test_run() -> None:
                 "workers": None,
                 "root_path": "",
                 "proxy_headers": True,
+                "factory": False,
             }
         assert "Using import string single_file_app:app" in result.output
         assert (
@@ -108,6 +138,33 @@ def test_run() -> None:
         assert "│  fastapi dev" in result.output
 
 
+def test_run_factory() -> None:
+    with changing_dir(assets_path):
+        with patch.object(uvicorn, "run") as mock_run:
+            result = runner.invoke(app, ["run", "factory_create_app.py"])
+            assert result.exit_code == 0, result.output
+            assert mock_run.called
+            assert mock_run.call_args
+            assert mock_run.call_args.kwargs == {
+                "app": "factory_create_app:create_app",
+                "host": "0.0.0.0",
+                "port": 8000,
+                "reload": False,
+                "workers": None,
+                "root_path": "",
+                "proxy_headers": True,
+                "factory": True,
+            }
+        assert "Using import string factory_create_app:create_app" in result.output
+        assert (
+            "╭─────────── FastAPI CLI - Production mode ───────────╮" in result.output
+        )
+        assert "│  Serving at: http://0.0.0.0:8000" in result.output
+        assert "│  API docs: http://0.0.0.0:8000/docs" in result.output
+        assert "│  Running in production mode, for development use:" in result.output
+        assert "│  fastapi dev" in result.output
+
+
 def test_run_args() -> None:
     with changing_dir(assets_path):
         with patch.object(uvicorn, "run") as mock_run:
@@ -141,6 +198,7 @@ def test_run_args() -> None:
                 "workers": 2,
                 "root_path": "/api",
                 "proxy_headers": False,
+                "factory": False,
             }
         assert "Using import string single_file_app:api" in result.output
         assert (
diff --git a/tests/test_utils_check_factory.py b/tests/test_utils_check_factory.py
new file mode 100644
index 0000000..966d84c
--- /dev/null
+++ b/tests/test_utils_check_factory.py
@@ -0,0 +1,32 @@
+from fastapi import FastAPI
+from fastapi_cli.discover import check_factory
+
+
+def test_check_untyped_factory() -> None:
+    def create_app():  # type: ignore[no-untyped-def]
+        return FastAPI()  # pragma: no cover
+
+    assert check_factory(create_app) is False
+
+
+def test_check_typed_factory() -> None:
+    def create_app() -> FastAPI:
+        return FastAPI()  # pragma: no cover
+
+    assert check_factory(create_app) is True
+
+
+def test_check_typed_factory_inherited() -> None:
+    class MyApp(FastAPI): ...
+
+    def create_app() -> MyApp:
+        return MyApp()  # pragma: no cover
+
+    assert check_factory(create_app) is True
+
+
+def test_create_app_with_different_type() -> None:
+    def create_app() -> int:
+        return 1  # pragma: no cover
+
+    assert check_factory(create_app) is False
diff --git a/tests/test_utils_default_dir.py b/tests/test_utils_default_dir.py
index 2665203..09a3a4c 100644
--- a/tests/test_utils_default_dir.py
+++ b/tests/test_utils_default_dir.py
@@ -12,8 +12,9 @@
 
 def test_app_dir_main(capsys: CaptureFixture[str]) -> None:
     with changing_dir(assets_path / "default_files" / "default_app_dir_main"):
-        import_string = get_import_string()
+        import_string, is_factory = get_import_string()
         assert import_string == "app.main:app"
+        assert is_factory is False
 
     captured = capsys.readouterr()
     assert "Using path app/main.py" in captured.out
@@ -36,8 +37,9 @@ def test_app_dir_main(capsys: CaptureFixture[str]) -> None:
 
 def test_app_dir_app(capsys: CaptureFixture[str]) -> None:
     with changing_dir(assets_path / "default_files" / "default_app_dir_app"):
-        import_string = get_import_string()
+        import_string, is_factory = get_import_string()
         assert import_string == "app.app:app"
+        assert is_factory is False
 
     captured = capsys.readouterr()
     assert "Using path app/app.py" in captured.out
@@ -58,8 +60,9 @@ def test_app_dir_app(capsys: CaptureFixture[str]) -> None:
 
 def test_app_dir_api(capsys: CaptureFixture[str]) -> None:
     with changing_dir(assets_path / "default_files" / "default_app_dir_api"):
-        import_string = get_import_string()
+        import_string, is_factory = get_import_string()
         assert import_string == "app.api:app"
+        assert is_factory is False
 
     captured = capsys.readouterr()
     assert "Using path app/api.py" in captured.out
diff --git a/tests/test_utils_default_file.py b/tests/test_utils_default_file.py
index f5c87c8..5e67d0b 100644
--- a/tests/test_utils_default_file.py
+++ b/tests/test_utils_default_file.py
@@ -20,8 +20,9 @@ def test_single_file_main(capsys: CaptureFixture[str]) -> None:
         mod = importlib.import_module("main")
 
         importlib.reload(mod)
-        import_string = get_import_string()
+        import_string, is_factory = get_import_string()
         assert import_string == "main:app"
+        assert is_factory is False
 
     captured = capsys.readouterr()
     assert "Using path main.py" in captured.out
@@ -47,8 +48,9 @@ def test_single_file_app(capsys: CaptureFixture[str]) -> None:
         mod = importlib.import_module("app")
 
         importlib.reload(mod)
-        import_string = get_import_string()
+        import_string, is_factory = get_import_string()
         assert import_string == "app:app"
+        assert is_factory is False
 
     captured = capsys.readouterr()
     assert "Using path app.py" in captured.out
@@ -74,8 +76,9 @@ def test_single_file_api(capsys: CaptureFixture[str]) -> None:
         mod = importlib.import_module("api")
 
         importlib.reload(mod)
-        import_string = get_import_string()
+        import_string, is_factory = get_import_string()
         assert import_string == "api:app"
+        assert is_factory is False
 
     captured = capsys.readouterr()
     assert "Using path api.py" in captured.out
diff --git a/tests/test_utils_package.py b/tests/test_utils_package.py
index d5573db..a66d012 100644
--- a/tests/test_utils_package.py
+++ b/tests/test_utils_package.py
@@ -12,8 +12,9 @@
 
 def test_package_app_root(capsys: CaptureFixture[str]) -> None:
     with changing_dir(assets_path):
-        import_string = get_import_string(path=Path("package/mod/app.py"))
+        import_string, is_factory = get_import_string(path=Path("package/mod/app.py"))
         assert import_string == "package.mod.app:app"
+        assert is_factory is False
 
     captured = capsys.readouterr()
     assert "Using path package/mod/app.py" in captured.out
@@ -40,8 +41,9 @@ def test_package_app_root(capsys: CaptureFixture[str]) -> None:
 
 def test_package_api_root(capsys: CaptureFixture[str]) -> None:
     with changing_dir(assets_path):
-        import_string = get_import_string(path=Path("package/mod/api.py"))
+        import_string, is_factory = get_import_string(path=Path("package/mod/api.py"))
         assert import_string == "package.mod.api:api"
+        assert is_factory is False
 
     captured = capsys.readouterr()
     assert "Using path package/mod/api.py" in captured.out
@@ -68,8 +70,9 @@ def test_package_api_root(capsys: CaptureFixture[str]) -> None:
 
 def test_package_other_root(capsys: CaptureFixture[str]) -> None:
     with changing_dir(assets_path):
-        import_string = get_import_string(path=Path("package/mod/other.py"))
+        import_string, is_factory = get_import_string(path=Path("package/mod/other.py"))
         assert import_string == "package.mod.other:first_other"
+        assert is_factory is False
 
     captured = capsys.readouterr()
     assert "Using path package/mod/other.py" in captured.out
@@ -94,10 +97,135 @@ def test_package_other_root(capsys: CaptureFixture[str]) -> None:
     assert "Using import string package.mod.other:first_other" in captured.out
 
 
+def test_package_factory_app_root(capsys: CaptureFixture[str]) -> None:
+    with changing_dir(assets_path):
+        import_string, is_factory = get_import_string(
+            path=Path("package/mod/factory_app.py")
+        )
+        assert import_string == "package.mod.factory_app:create_app"
+        assert is_factory is True
+
+    captured = capsys.readouterr()
+    assert "Using path package/mod/factory_app.py" in captured.out
+    assert "Resolved absolute path" in captured.out
+    assert "tests/assets/package/mod/factory_app.py" in captured.out
+    assert (
+        "Searching for package file structure from directories with __init__.py files"
+        in captured.out
+    )
+    assert "Importing from" in captured.out
+    assert "tests/assets" in captured.out
+    assert "╭─ Python package file structure ─╮" in captured.out
+    assert "│  📁 package" in captured.out
+    assert "│  ├── 🐍 __init__.py" in captured.out
+    assert "│  └── 📁 mod" in captured.out
+    assert "│      ├── 🐍 __init__.py " in captured.out
+    assert "│      └── 🐍 factory_app.py" in captured.out
+    assert "Importing module package.mod.factory_app" in captured.out
+    assert "Found importable FastAPI app" in captured.out
+    assert "Importable FastAPI app" in captured.out
+    assert "from package.mod.factory_app import create_app" in captured.out
+    assert "Using import string package.mod.factory_app:create_app" in captured.out
+
+
+def test_package_factory_api_root(capsys: CaptureFixture[str]) -> None:
+    with changing_dir(assets_path):
+        import_string, is_factory = get_import_string(
+            path=Path("package/mod/factory_api.py")
+        )
+        assert import_string == "package.mod.factory_api:create_api"
+        assert is_factory is True
+
+    captured = capsys.readouterr()
+    assert "Using path package/mod/factory_api.py" in captured.out
+    assert "Resolved absolute path" in captured.out
+    assert "tests/assets/package/mod/factory_api.py" in captured.out
+    assert (
+        "Searching for package file structure from directories with __init__.py files"
+        in captured.out
+    )
+    assert "Importing from" in captured.out
+    assert "tests/assets" in captured.out
+    assert "╭─ Python package file structure ─╮" in captured.out
+    assert "│  📁 package" in captured.out
+    assert "│  ├── 🐍 __init__.py" in captured.out
+    assert "│  └── 📁 mod" in captured.out
+    assert "│      ├── 🐍 __init__.py " in captured.out
+    assert "│      └── 🐍 factory_api.py" in captured.out
+    assert "Importing module package.mod.factory_api" in captured.out
+    assert "Found importable FastAPI app" in captured.out
+    assert "Importable FastAPI app" in captured.out
+    assert "from package.mod.factory_api import create_api" in captured.out
+    assert "Using import string package.mod.factory_api:create_api" in captured.out
+
+
+def test_package_factory_other_root(capsys: CaptureFixture[str]) -> None:
+    with changing_dir(assets_path):
+        import_string, is_factory = get_import_string(
+            path=Path("package/mod/factory_other.py"), app_name="build_app"
+        )
+        assert import_string == "package.mod.factory_other:build_app"
+        assert is_factory is True
+
+    captured = capsys.readouterr()
+    assert "Using path package/mod/factory_other.py" in captured.out
+    assert "Resolved absolute path" in captured.out
+    assert "tests/assets/package/mod/factory_other.py" in captured.out
+    assert (
+        "Searching for package file structure from directories with __init__.py files"
+        in captured.out
+    )
+    assert "Importing from" in captured.out
+    assert "tests/assets" in captured.out
+    assert "╭─ Python package file structure ─╮" in captured.out
+    assert "│  📁 package" in captured.out
+    assert "│  ├── 🐍 __init__.py" in captured.out
+    assert "│  └── 📁 mod" in captured.out
+    assert "│      ├── 🐍 __init__.py " in captured.out
+    assert "│      └── 🐍 factory_other.py" in captured.out
+    assert "Importing module package.mod.factory_other" in captured.out
+    assert "Found importable FastAPI app" in captured.out
+    assert "Importable FastAPI app" in captured.out
+    assert "from package.mod.factory_other import build_app" in captured.out
+    assert "Using import string package.mod.factory_other:build_app" in captured.out
+
+
+def test_package_factory_inherit_root(capsys: CaptureFixture[str]) -> None:
+    with changing_dir(assets_path):
+        import_string, is_factory = get_import_string(
+            path=Path("package/mod/factory_inherit.py")
+        )
+        assert import_string == "package.mod.factory_inherit:create_app"
+        assert is_factory is True
+
+    captured = capsys.readouterr()
+    assert "Using path package/mod/factory_inherit.py" in captured.out
+    assert "Resolved absolute path" in captured.out
+    assert "tests/assets/package/mod/factory_inherit.py" in captured.out
+    assert (
+        "Searching for package file structure from directories with __init__.py files"
+        in captured.out
+    )
+    assert "Importing from" in captured.out
+    assert "tests/assets" in captured.out
+    assert "╭─ Python package file structure ─╮" in captured.out
+    assert "│  📁 package" in captured.out
+    assert "│  ├── 🐍 __init__.py" in captured.out
+    assert "│  └── 📁 mod" in captured.out
+    assert "│      ├── 🐍 __init__.py " in captured.out
+    assert "│      └── 🐍 factory_inherit.py" in captured.out
+    assert "Importing module package.mod.factory_inherit" in captured.out
+    assert "Found importable FastAPI app" in captured.out
+    assert "Importable FastAPI app" in captured.out
+    assert "from package.mod.factory_inherit import create_app" in captured.out
+    assert "Using import string package.mod.factory_inherit:create_app" in captured.out
+
+
 def test_package_app_mod(capsys: CaptureFixture[str]) -> None:
     with changing_dir(assets_path / "package/mod"):
-        import_string = get_import_string(path=Path("app.py"))
+        import_string, is_factory = get_import_string(path=Path("app.py"))
         assert import_string == "package.mod.app:app"
+        assert is_factory is False
 
     captured = capsys.readouterr()
     assert "Using path app.py" in captured.out
@@ -124,8 +252,9 @@ def test_package_app_mod(capsys: CaptureFixture[str]) -> None:
 
 def test_package_api_mod(capsys: CaptureFixture[str]) -> None:
     with changing_dir(assets_path / "package/mod"):
-        import_string = get_import_string(path=Path("api.py"))
+        import_string, is_factory = get_import_string(path=Path("api.py"))
         assert import_string == "package.mod.api:api"
+        assert is_factory is False
 
     captured = capsys.readouterr()
     assert "Using path api.py" in captured.out
@@ -152,8 +281,9 @@ def test_package_api_mod(capsys: CaptureFixture[str]) -> None:
 
 def test_package_other_mod(capsys: CaptureFixture[str]) -> None:
     with changing_dir(assets_path / "package/mod"):
-        import_string = get_import_string(path=Path("other.py"))
+        import_string, is_factory = get_import_string(path=Path("other.py"))
         assert import_string == "package.mod.other:first_other"
+        assert is_factory is False
 
     captured = capsys.readouterr()
     assert "Using path other.py" in captured.out
@@ -178,10 +308,131 @@ def test_package_other_mod(capsys: CaptureFixture[str]) -> None:
     assert "Using import string package.mod.other:first_other" in captured.out
 
 
-def test_package_app_above(capsys: CaptureFixture[str]) -> None:
+def test_package_factory_app_mod(capsys: CaptureFixture[str]) -> None:
+    with changing_dir(assets_path / "package/mod"):
+        import_string, is_factory = get_import_string(path=Path("factory_app.py"))
+        assert import_string == "package.mod.factory_app:create_app"
+        assert is_factory is True
+
+    captured = capsys.readouterr()
+    assert "Using path factory_app.py" in captured.out
+    assert "Resolved absolute path" in captured.out
+    assert "tests/assets/package/mod/factory_app.py" in captured.out
+    assert (
+        "Searching for package file structure from directories with __init__.py files"
+        in captured.out
+    )
+    assert "Importing from" in captured.out
+    assert "tests/assets" in captured.out
+    assert "╭─ Python package file structure ─╮" in captured.out
+    assert "│  📁 package" in captured.out
+    assert "│  ├── 🐍 __init__.py" in captured.out
+    assert "│  └── 📁 mod" in captured.out
+    assert "│      ├── 🐍 __init__.py " in captured.out
+    assert "│      └── 🐍 factory_app.py" in captured.out
+    assert "Importing module package.mod.factory_app" in captured.out
+    assert "Found importable FastAPI app" in captured.out
+    assert "Importable FastAPI app" in captured.out
+    assert "from package.mod.factory_app import create_app" in captured.out
+    assert "Using import string package.mod.factory_app:create_app" in captured.out
+
+
+def test_package_factory_api_mod(capsys: CaptureFixture[str]) -> None:
+    with changing_dir(assets_path / "package/mod"):
+        import_string, is_factory = get_import_string(path=Path("factory_api.py"))
+        assert import_string == "package.mod.factory_api:create_api"
+        assert is_factory is True
+
+    captured = capsys.readouterr()
+    assert "Using path factory_api.py" in captured.out
+    assert "Resolved absolute path" in captured.out
+    assert "tests/assets/package/mod/factory_api.py" in captured.out
+    assert (
+        "Searching for package file structure from directories with __init__.py files"
+        in captured.out
+    )
+    assert "Importing from" in captured.out
+    assert "tests/assets" in captured.out
+    assert "╭─ Python package file structure ─╮" in captured.out
+    assert "│  📁 package" in captured.out
+    assert "│  ├── 🐍 __init__.py" in captured.out
+    assert "│  └── 📁 mod" in captured.out
+    assert "│      ├── 🐍 __init__.py " in captured.out
+    assert "│      └── 🐍 factory_api.py" in captured.out
+    assert "Importing module package.mod.factory_api" in captured.out
+    assert "Found importable FastAPI app" in captured.out
+    assert "Importable FastAPI app" in captured.out
+    assert "from package.mod.factory_api import create_api" in captured.out
+    assert "Using import string package.mod.factory_api:create_api" in captured.out
+
+
+def test_package_factory_other_mod(capsys: CaptureFixture[str]) -> None:
+    with changing_dir(assets_path / "package/mod"):
+        import_string, is_factory = get_import_string(
+            path=Path("factory_other.py"), app_name="build_app"
+        )
+        assert import_string == "package.mod.factory_other:build_app"
+        assert is_factory is True
+
+    captured = capsys.readouterr()
+    assert "Using path factory_other.py" in captured.out
+    assert "Resolved absolute path" in captured.out
+    assert "tests/assets/package/mod/factory_other.py" in captured.out
+    assert (
+        "Searching for package file structure from directories with __init__.py files"
+        in captured.out
+    )
+    assert "Importing from" in captured.out
+    assert "tests/assets" in captured.out
+    assert "╭─ Python package file structure ─╮" in captured.out
+    assert "│  📁 package" in captured.out
+    assert "│  ├── 🐍 __init__.py" in captured.out
+    assert "│  └── 📁 mod" in captured.out
+    assert "│      ├── 🐍 __init__.py " in captured.out
+    assert "│      └── 🐍 factory_other.py" in captured.out
+    assert "Importing module package.mod.factory_other" in captured.out
+    assert "Found importable FastAPI app" in captured.out
+    assert "Importable FastAPI app" in captured.out
+    assert "from package.mod.factory_other import build_app" in captured.out
+    assert "Using import string package.mod.factory_other:build_app" in captured.out
+
+
+def test_package_factory_inherit_mod(capsys: CaptureFixture[str]) -> None:
+    with changing_dir(assets_path / "package/mod"):
+        import_string, is_factory = get_import_string(path=Path("factory_inherit.py"))
+        assert import_string == "package.mod.factory_inherit:create_app"
+        assert is_factory is True
+
+    captured = capsys.readouterr()
+    assert "Using path factory_inherit.py" in captured.out
+    assert "Resolved absolute path" in captured.out
+    assert "tests/assets/package/mod/factory_inherit.py" in captured.out
+    assert (
+        "Searching for package file structure from directories with __init__.py files"
+        in captured.out
+    )
+    assert "Importing from" in captured.out
+    assert "tests/assets" in captured.out
+    assert "╭─ Python package file structure ─╮" in captured.out
+    assert "│  📁 package" in captured.out
+    assert "│  ├── 🐍 __init__.py" in captured.out
+    assert "│  └── 📁 mod" in captured.out
+    assert "│      ├── 🐍 __init__.py " in captured.out
+    assert "│      └── 🐍 factory_inherit.py" in captured.out
+    assert "Importing module package.mod.factory_inherit" in captured.out
+    assert "Found importable FastAPI app" in captured.out
+    assert "Importable FastAPI app" in captured.out
+    assert "from package.mod.factory_inherit import create_app" in captured.out
+    assert "Using import string package.mod.factory_inherit:create_app" in captured.out
+
+
+def test_package_app_parent(capsys: CaptureFixture[str]) -> None:
     with changing_dir(assets_path.parent):
-        import_string = get_import_string(path=Path("assets/package/mod/app.py"))
+        import_string, is_factory = get_import_string(
+            path=Path("assets/package/mod/app.py")
+        )
         assert import_string == "package.mod.app:app"
+        assert is_factory is False
 
     captured = capsys.readouterr()
     assert "Using path assets/package/mod/app.py" in captured.out
@@ -208,8 +459,11 @@ def test_package_app_above(capsys: CaptureFixture[str]) -> None:
 
 def test_package_api_parent(capsys: CaptureFixture[str]) -> None:
     with changing_dir(assets_path.parent):
-        import_string = get_import_string(path=Path("assets/package/mod/api.py"))
+        import_string, is_factory = get_import_string(
+            path=Path("assets/package/mod/api.py")
+        )
         assert import_string == "package.mod.api:api"
+        assert is_factory is False
 
     captured = capsys.readouterr()
     assert "Using path assets/package/mod/api.py" in captured.out
@@ -236,8 +490,11 @@ def test_package_api_parent(capsys: CaptureFixture[str]) -> None:
 
 def test_package_other_parent(capsys: CaptureFixture[str]) -> None:
     with changing_dir(assets_path.parent):
-        import_string = get_import_string(path=Path("assets/package/mod/other.py"))
+        import_string, is_factory = get_import_string(
+            path=Path("assets/package/mod/other.py")
+        )
         assert import_string == "package.mod.other:first_other"
+        assert is_factory is False
 
     captured = capsys.readouterr()
     assert "Using path assets/package/mod/other.py" in captured.out
@@ -262,10 +519,136 @@ def test_package_other_parent(capsys: CaptureFixture[str]) -> None:
     assert "Using import string package.mod.other:first_other" in captured.out
 
 
+def test_package_factory_app_parent(capsys: CaptureFixture[str]) -> None:
+    with changing_dir(assets_path.parent):
+        import_string, is_factory = get_import_string(
+            path=Path("assets/package/mod/factory_app.py")
+        )
+        assert import_string == "package.mod.factory_app:create_app"
+        assert is_factory is True
+
+    captured = capsys.readouterr()
+    assert "Using path assets/package/mod/factory_app.py" in captured.out
+    assert "Resolved absolute path" in captured.out
+    assert "tests/assets/package/mod/factory_app.py" in captured.out
+    assert (
+        "Searching for package file structure from directories with __init__.py files"
+        in captured.out
+    )
+    assert "Importing from" in captured.out
+    assert "tests/assets" in captured.out
+    assert "╭─ Python package file structure ─╮" in captured.out
+    assert "│  📁 package" in captured.out
+    assert "│  ├── 🐍 __init__.py" in captured.out
+    assert "│  └── 📁 mod" in captured.out
+    assert "│      ├── 🐍 __init__.py " in captured.out
+    assert "│      └── 🐍 factory_app.py" in captured.out
+    assert "Importing module package.mod.factory_app" in captured.out
+    assert "Found importable FastAPI app" in captured.out
+    assert "Importable FastAPI app" in captured.out
+    assert "from package.mod.factory_app import create_app" in captured.out
+    assert "Using import string package.mod.factory_app:create_app" in captured.out
+
+
+def test_package_factory_api_parent(capsys: CaptureFixture[str]) -> None:
+    with changing_dir(assets_path.parent):
+        import_string, is_factory = get_import_string(
+            path=Path("assets/package/mod/factory_api.py")
+        )
+        assert import_string == "package.mod.factory_api:create_api"
+        assert is_factory is True
+
+    captured = capsys.readouterr()
+    assert "Using path assets/package/mod/factory_api.py" in captured.out
+    assert "Resolved absolute path" in captured.out
+    assert "tests/assets/package/mod/factory_api.py" in captured.out
+    assert (
+        "Searching for package file structure from directories with __init__.py files"
+        in captured.out
+    )
+    assert "Importing from" in captured.out
+    assert "tests/assets" in captured.out
+    assert "╭─ Python package file structure ─╮" in captured.out
+    assert "│  📁 package" in captured.out
+    assert "│  ├── 🐍 __init__.py" in captured.out
+    assert "│  └── 📁 mod" in captured.out
+    assert "│      ├── 🐍 __init__.py " in captured.out
+    assert "│      └── 🐍 factory_api.py" in captured.out
+    assert "Importing module package.mod.factory_api" in captured.out
+    assert "Found importable FastAPI app" in captured.out
+    assert "Importable FastAPI app" in captured.out
+    assert "from package.mod.factory_api import create_api" in captured.out
+    assert "Using import string package.mod.factory_api:create_api" in captured.out
+
+
+def test_package_factory_other_parent(capsys: CaptureFixture[str]) -> None:
+    with changing_dir(assets_path.parent):
+        import_string, is_factory = get_import_string(
+            path=Path("assets/package/mod/factory_other.py"),
+            app_name="build_app",
+        )
+        assert import_string == "package.mod.factory_other:build_app"
+        assert is_factory is True
+
+    captured = capsys.readouterr()
+    assert "Using path assets/package/mod/factory_other.py" in captured.out
+    assert "Resolved absolute path" in captured.out
+    assert "tests/assets/package/mod/factory_other.py" in captured.out
+    assert (
+        "Searching for package file structure from directories with __init__.py files"
+        in captured.out
+    )
+    assert "Importing from" in captured.out
+    assert "tests/assets" in captured.out
+    assert "╭─ Python package file structure ─╮" in captured.out
+    assert "│  📁 package" in captured.out
+    assert "│  ├── 🐍 __init__.py" in captured.out
+    assert "│  └── 📁 mod" in captured.out
+    assert "│      ├── 🐍 __init__.py " in captured.out
+    assert "│      └── 🐍 factory_other.py" in captured.out
+    assert "Importing module package.mod.factory_other" in captured.out
+    assert "Found importable FastAPI app" in captured.out
+    assert "Importable FastAPI app" in captured.out
+    assert "from package.mod.factory_other import build_app" in captured.out
+    assert "Using import string package.mod.factory_other:build_app" in captured.out
+
+
+def test_package_factory_inherit_parent(capsys: CaptureFixture[str]) -> None:
+    with changing_dir(assets_path.parent):
+        import_string, is_factory = get_import_string(
+            path=Path("assets/package/mod/factory_inherit.py")
+        )
+        assert import_string == "package.mod.factory_inherit:create_app"
+        assert is_factory is True
+
+    captured = capsys.readouterr()
+    assert "Using path assets/package/mod/factory_inherit.py" in captured.out
+    assert "Resolved absolute path" in captured.out
+    assert "tests/assets/package/mod/factory_inherit.py" in captured.out
+    assert (
+        "Searching for package file structure from directories with __init__.py files"
+        in captured.out
+    )
+    assert "Importing from" in captured.out
+    assert "tests/assets" in captured.out
+    assert "╭─ Python package file structure ─╮" in captured.out
+    assert "│  📁 package" in captured.out
+    assert "│  ├── 🐍 __init__.py" in captured.out
+    assert "│  └── 📁 mod" in captured.out
+    assert "│      ├── 🐍 __init__.py " in captured.out
+    assert "│      └── 🐍 factory_inherit.py" in captured.out
+    assert "Importing module package.mod.factory_inherit" in captured.out
+    assert "Found importable FastAPI app" in captured.out
+    assert "Importable FastAPI app" in captured.out
+    assert "from package.mod.factory_inherit import create_app" in captured.out
+    assert "Using import string package.mod.factory_inherit:create_app" in captured.out
+
+
 def test_package_mod_init_inside(capsys: CaptureFixture[str]) -> None:
     with changing_dir(assets_path / "package/mod"):
-        import_string = get_import_string(path=Path("__init__.py"))
+        import_string, is_factory = get_import_string(path=Path("__init__.py"))
         assert import_string == "package.mod:app"
+        assert is_factory is False
 
     captured = capsys.readouterr()
     assert "Using path __init__.py" in captured.out
@@ -291,8 +674,9 @@ def test_package_mod_init_inside(capsys: CaptureFixture[str]) -> None:
 
 def test_package_mod_dir(capsys: CaptureFixture[str]) -> None:
     with changing_dir(assets_path):
-        import_string = get_import_string(path=Path("package/mod"))
+        import_string, is_factory = get_import_string(path=Path("package/mod"))
         assert import_string == "package.mod:app"
+        assert is_factory is False
 
     captured = capsys.readouterr()
     assert "Using path package/mod" in captured.out
@@ -318,8 +702,9 @@ def test_package_mod_dir(capsys: CaptureFixture[str]) -> None:
 
 def test_package_init_inside(capsys: CaptureFixture[str]) -> None:
     with changing_dir(assets_path / "package"):
-        import_string = get_import_string(path=Path("__init__.py"))
+        import_string, is_factory = get_import_string(path=Path("__init__.py"))
         assert import_string == "package:app"
+        assert is_factory is False
 
     captured = capsys.readouterr()
     assert "Using path __init__.py" in captured.out
@@ -343,8 +728,9 @@ def test_package_init_inside(capsys: CaptureFixture[str]) -> None:
 
 def test_package_dir_inside_package(capsys: CaptureFixture[str]) -> None:
     with changing_dir(assets_path / "package/mod"):
-        import_string = get_import_string(path=Path("../"))
+        import_string, is_factory = get_import_string(path=Path("../"))
         assert import_string == "package:app"
+        assert is_factory is False
 
     captured = capsys.readouterr()
     assert "Using path .." in captured.out
@@ -368,8 +754,9 @@ def test_package_dir_inside_package(capsys: CaptureFixture[str]) -> None:
 
 def test_package_dir_above_package(capsys: CaptureFixture[str]) -> None:
     with changing_dir(assets_path.parent):
-        import_string = get_import_string(path=Path("assets/package"))
+        import_string, is_factory = get_import_string(path=Path("assets/package"))
         assert import_string == "package:app"
+        assert is_factory is False
 
     captured = capsys.readouterr()
     assert "Using path assets/package" in captured.out
@@ -393,8 +780,11 @@ def test_package_dir_above_package(capsys: CaptureFixture[str]) -> None:
 
 def test_package_dir_explicit_app(capsys: CaptureFixture[str]) -> None:
     with changing_dir(assets_path):
-        import_string = get_import_string(path=Path("package"), app_name="api")
+        import_string, is_factory = get_import_string(
+            path=Path("package"), app_name="api"
+        )
         assert import_string == "package:api"
+        assert is_factory is False
 
     captured = capsys.readouterr()
     assert "Using path package" in captured.out
diff --git a/tests/test_utils_single_file.py b/tests/test_utils_single_file.py
index 6395b32..a8fc768 100644
--- a/tests/test_utils_single_file.py
+++ b/tests/test_utils_single_file.py
@@ -12,8 +12,9 @@
 
 def test_single_file_app(capsys: CaptureFixture[str]) -> None:
     with changing_dir(assets_path):
-        import_string = get_import_string(path=Path("single_file_app.py"))
+        import_string, is_factory = get_import_string(path=Path("single_file_app.py"))
         assert import_string == "single_file_app:app"
+        assert is_factory is False
 
     captured = capsys.readouterr()
     assert "Using path single_file_app.py" in captured.out
@@ -36,8 +37,9 @@ def test_single_file_app(capsys: CaptureFixture[str]) -> None:
 
 def test_single_file_api(capsys: CaptureFixture[str]) -> None:
     with changing_dir(assets_path):
-        import_string = get_import_string(path=Path("single_file_api.py"))
+        import_string, is_factory = get_import_string(path=Path("single_file_api.py"))
         assert import_string == "single_file_api:api"
+        assert is_factory is False
 
     captured = capsys.readouterr()
     assert "Using path single_file_api.py" in captured.out
@@ -60,8 +62,9 @@ def test_single_file_api(capsys: CaptureFixture[str]) -> None:
 
 def test_single_file_other(capsys: CaptureFixture[str]) -> None:
     with changing_dir(assets_path):
-        import_string = get_import_string(path=Path("single_file_other.py"))
+        import_string, is_factory = get_import_string(path=Path("single_file_other.py"))
         assert import_string == "single_file_other:first_other"
+        assert is_factory is False
 
     captured = capsys.readouterr()
     assert "Using path single_file_other.py" in captured.out
@@ -84,10 +87,11 @@ def test_single_file_other(capsys: CaptureFixture[str]) -> None:
 
 def test_single_file_explicit_object(capsys: CaptureFixture[str]) -> None:
     with changing_dir(assets_path):
-        import_string = get_import_string(
+        import_string, is_factory = get_import_string(
             path=Path("single_file_app.py"), app_name="second_other"
         )
         assert import_string == "single_file_app:second_other"
+        assert is_factory is False
 
     captured = capsys.readouterr()
     assert "Using path single_file_app.py" in captured.out
@@ -108,6 +112,116 @@ def test_single_file_explicit_object(capsys: CaptureFixture[str]) -> None:
     assert "Using import string single_file_app:second_other" in captured.out
 
 
+def test_single_file_create_app_factory_function(capsys: CaptureFixture[str]) -> None:
+    with changing_dir(assets_path):
+        import_string, is_factory = get_import_string(
+            path=Path("factory_create_app.py")
+        )
+        assert import_string == "factory_create_app:create_app"
+        assert is_factory is True
+
+    captured = capsys.readouterr()
+    assert "Using path factory_create_app.py" in captured.out
+    assert "Resolved absolute path" in captured.out
+    assert "tests/assets/factory_create_app.py" in captured.out
+    assert (
+        "Searching for package file structure from directories with __init__.py files"
+        in captured.out
+    )
+    assert "Importing from" in captured.out
+    assert "tests/assets" in captured.out
+    assert "╭──── Python module file ────╮" in captured.out
+    assert "│  🐍 factory_create_app.py" in captured.out
+    assert "Importing module factory_create_app" in captured.out
+    assert "Found importable FastAPI app" in captured.out
+    assert "Importable FastAPI app" in captured.out
+    assert "from factory_create_app import create_app" in captured.out
+    assert "Using import string factory_create_app:create_app" in captured.out
+
+
+def test_single_file_create_api_factory_function(capsys: CaptureFixture[str]) -> None:
+    with changing_dir(assets_path):
+        import_string, is_factory = get_import_string(
+            path=Path("factory_create_api.py")
+        )
+        assert import_string == "factory_create_api:create_api"
+        assert is_factory is True
+
+    captured = capsys.readouterr()
+    assert "Using path factory_create_api.py" in captured.out
+    assert "Resolved absolute path" in captured.out
+    assert "tests/assets/factory_create_api.py" in captured.out
+    assert (
+        "Searching for package file structure from directories with __init__.py files"
+        in captured.out
+    )
+    assert "Importing from" in captured.out
+    assert "tests/assets" in captured.out
+    assert "╭──── Python module file ────╮" in captured.out
+    assert "│  🐍 factory_create_api.py" in captured.out
+    assert "Importing module factory_create_api" in captured.out
+    assert "Found importable FastAPI app" in captured.out
+    assert "Importable FastAPI app" in captured.out
+    assert "from factory_create_api import create_api" in captured.out
+    assert "Using import string factory_create_api:create_api" in captured.out
+
+
+def test_single_file_explicit_factory_function(capsys: CaptureFixture[str]) -> None:
+    with changing_dir(assets_path):
+        import_string, is_factory = get_import_string(
+            path=Path("factory_create_app.py"), app_name="create_app"
+        )
+        assert import_string == "factory_create_app:create_app"
+        assert is_factory is True
+
+    captured = capsys.readouterr()
+    assert "Using path factory_create_app.py" in captured.out
+    assert "Resolved absolute path" in captured.out
+    assert "tests/assets/factory_create_app.py" in captured.out
+    assert (
+        "Searching for package file structure from directories with __init__.py files"
+        in captured.out
+    )
+    assert "Importing from" in captured.out
+    assert "tests/assets" in captured.out
+    assert "╭──── Python module file ────╮" in captured.out
+    assert "│  🐍 factory_create_app.py" in captured.out
+    assert "Importing module factory_create_app" in captured.out
+    assert "Found importable FastAPI app" in captured.out
+    assert "Importable FastAPI app" in captured.out
+    assert "from factory_create_app import create_app" in captured.out
+    assert "Using import string factory_create_app:create_app" in captured.out
+
+
+def test_single_file_explicit_factory_function_other(
+    capsys: CaptureFixture[str],
+) -> None:
+    with changing_dir(assets_path):
+        import_string, is_factory = get_import_string(
+            path=Path("factory_create_app.py"), app_name="create_app_other"
+        )
+        assert import_string == "factory_create_app:create_app_other"
+        assert is_factory is True
+
+    captured = capsys.readouterr()
+    assert "Using path factory_create_app.py" in captured.out
+    assert "Resolved absolute path" in captured.out
+    assert "tests/assets/factory_create_app.py" in captured.out
+    assert (
+        "Searching for package file structure from directories with __init__.py files"
+        in captured.out
+    )
+    assert "Importing from" in captured.out
+    assert "tests/assets" in captured.out
+    assert "╭──── Python module file ────╮" in captured.out
+    assert "│  🐍 factory_create_app.py" in captured.out
+    assert "Importing module factory_create_app" in captured.out
+    assert "Found importable FastAPI app" in captured.out
+    assert "Importable FastAPI app" in captured.out
+    assert "from factory_create_app import create_app_other" in captured.out
+    assert "Using import string factory_create_app:create_app_other" in captured.out
+
+
 def test_single_non_existing_file() -> None:
     with changing_dir(assets_path):
         with pytest.raises(FastAPICLIException) as e: