diff --git a/sd_utils/plugins/plugin_base.py b/sd_utils/plugins/plugin_base.py
index 21ff90a..3578331 100644
--- a/sd_utils/plugins/plugin_base.py
+++ b/sd_utils/plugins/plugin_base.py
@@ -41,3 +41,13 @@ class PluginBase:
                 plugin from the plugin manager
         """
         raise NotImplementedError
+
+    def on_iterate(self, data: Any = None):
+        """
+        runs when the manager specifically calls iterate_all
+
+        Args:
+            data (Any): any parameters passed to this
+                plugin from the plugin manager
+        """
+        raise NotImplementedError
diff --git a/sd_utils/plugins/plugin_manager.py b/sd_utils/plugins/plugin_manager.py
index deb41ff..e6322a0 100644
--- a/sd_utils/plugins/plugin_manager.py
+++ b/sd_utils/plugins/plugin_manager.py
@@ -30,9 +30,13 @@ class PluginManager:
             w = Wrapper(plugin(**kwargs))
 
             self._plugins[name] = w
-            w.plugin.on_register(
-                self.get_on_register_params(name, **on_register_params)
-            )
+            try:
+                w.plugin.on_register(
+                    self.get_on_register_params(name, **on_register_params)
+                )
+            except NotImplementedError:
+                # dont need to implement this hook
+                pass
 
             return w
 
@@ -45,7 +49,12 @@ class PluginManager:
         """
         runs a given plugin.
 
-        Note: this function
+        Note: this function iterates over all plugins and will call the
+        on_search for each plugin before executing the on_find hook
+        for the plugin being searched for.
+
+        This function should not be used just to iterate over every plugin.
+        instead the iterate_all function should be used
 
         Args:
             name (str): the name of the plugin to run
@@ -78,6 +87,25 @@ class PluginManager:
             self.get_on_find_params(name, **on_find_params)
         )
 
+    @final
+    def iterate_all(self, on_iterate_params: dict = {}):
+        """
+        Iterates over all the plugins without directly calling
+        one of them. Only hook used is on_iterate
+
+        Args:
+            on_iterate_params (dict): a list of parameters to pass
+                to the on_iterate hook
+        """
+        for name, wrapper in self._plugins.items():
+            try:
+                wrapper.plugin.on_iterate(
+                    self.get_on_iterate_params(name, **on_iterate_params)
+                )
+            except NotImplementedError:
+                # just skip if its not implemented
+                pass
+
     def get_on_search_params(self, name: str, **kwargs) -> Any:
         """
         function that generates parameters for the on
@@ -126,3 +154,20 @@ class PluginManager:
                 on_register function
         """
         return kwargs
+
+    def get_on_iterate_params(self, name: str, **kwargs) -> Any:
+        """
+        function that generates parameters for the on
+        iterate_all function of a plugin given its name
+
+        Args:
+            name (str): the name of the command to
+                call iterate_all for
+            **kwargs: any arguments sent from the
+                iterate_all function
+
+        Returns:
+            Any: the arguments to be sent to the
+                iterate_all function
+        """
+        return kwargs
diff --git a/setup.py b/setup.py
index 340de1c..c9d2f12 100644
--- a/setup.py
+++ b/setup.py
@@ -7,7 +7,7 @@ with open("README.md", "r") as file:
 
 setup(
     name="sd_utils",
-    version="0.2.0",
+    version="0.2.1",
     description="A python module with basic utils I tend to use in my projects",
     long_description=readme,
     long_description_content_type="text/markdown",

diff --git a/tests/plugins/my_other_plugin.py b/tests/plugins/my_other_plugin.py
index 2f2542a..06a3c89 100644
--- a/tests/plugins/my_other_plugin.py
+++ b/tests/plugins/my_other_plugin.py
@@ -13,13 +13,6 @@ class MyOtherPlugin(PluginBase):
         """
         self.operations = []
 
-    def on_register(self, data: Any = None):
-        """
-        runs when this plugin is registered with
-        the plugin manager
-        """
-        self.operations.append("registered")
-
     def on_search(self, data: Any = None):
         """
         runs when the manager begins a query for
diff --git a/tests/plugins/my_plugin.py b/tests/plugins/my_plugin.py
index cc75323..93454a7 100644
--- a/tests/plugins/my_plugin.py
+++ b/tests/plugins/my_plugin.py
@@ -36,3 +36,7 @@ class MyPlugin(PluginBase):
         self.operations.append("found")
         self.data.append(data)
         return data
+
+    def on_iterate(self, data: Any):
+        self.operations.append("iterate")
+        self.data.append(data)
diff --git a/tests/plugins/test_plugins.py b/tests/plugins/test_plugins.py
index 4f75aa5..c468df4 100644
--- a/tests/plugins/test_plugins.py
+++ b/tests/plugins/test_plugins.py
@@ -91,3 +91,17 @@ def test_on_find_return_value():
     assert response == mydata
 
     mypluginmanager._plugins["myplugin"].plugin.data.clear()
+
+
+def test_iterate_all():
+    """
+    tests that the iterate all function works correctly
+    """
+    mydata = {"data": "value"}
+    mypluginmanager.iterate_all(mydata)
+
+    assert "iterate" in mypluginmanager._plugins["myplugin"].plugin.operations
+    assert mydata in mypluginmanager._plugins["myplugin"].plugin.data
+
+    mypluginmanager._plugins["myplugin"].plugin.data.clear()
+    mypluginmanager._plugins["myplugin"].plugin.operations.clear()

on_register hook required
currently the `on_register` hook in a plugin base must be implemented. This should be an optional hook 
tests/plugins/test_plugins.py::test_registered tests/plugins/test_plugins.py::test_search_and_find tests/plugins/test_plugins.py::test_plugin_doesnt_exist tests/plugins/test_plugins.py::test_search_only tests/plugins/test_plugins.py::test_get_on_search_params tests/plugins/test_plugins.py::test_get_on_find_params tests/plugins/test_plugins.py::test_on_find_return_value tests/plugins/test_plugins.py::test_iterate_all