diff --git a/Framework/API/inc/MantidAPI/Algorithm.h b/Framework/API/inc/MantidAPI/Algorithm.h
index faae3348a666d201aaa39ab2c05482d7c246c5a9..6fd4d6441f3f30c7bb9a83c09c61b99be72380c5 100644
--- a/Framework/API/inc/MantidAPI/Algorithm.h
+++ b/Framework/API/inc/MantidAPI/Algorithm.h
@@ -200,6 +200,11 @@ public:
   /// is provided
   const std::string alias() const override { return ""; }
 
+  /// function to return URL for algorithm documentation; A default
+  /// implementation is provided.
+  /// Override if the algorithm is not part of the Mantid distribution.
+  const std::string helpURL() const override { return ""; }
+
   const std::string workspaceMethodName() const override;
   const std::vector<std::string> workspaceMethodOn() const override;
   const std::string workspaceMethodInputProperty() const override;
diff --git a/Framework/API/inc/MantidAPI/AlgorithmProxy.h b/Framework/API/inc/MantidAPI/AlgorithmProxy.h
index 827d16e14545becd538ade8fc773eb9575565c9a..23e2d3350ac1b497a1596c46c5370e9e7e61162c 100644
--- a/Framework/API/inc/MantidAPI/AlgorithmProxy.h
+++ b/Framework/API/inc/MantidAPI/AlgorithmProxy.h
@@ -86,6 +86,8 @@ public:
   }
   /// Aliases to the algorithm
   const std::string alias() const override { return m_alias; }
+  /// Optional documentation URL for the real algorithm
+  const std::string helpURL() const override { return m_helpURL; }
   /// function returns a summary message that will be displayed in the default
   /// GUI, and in the help.
   const std::string summary() const override { return m_summary; }
@@ -176,6 +178,7 @@ private:
   const std::string
       m_categorySeparator;     ///< category seperator of the real algorithm
   const std::string m_alias;   ///< alias to the algorithm
+  const std::string m_helpURL; ///< Optional documentation URL
   const std::string m_summary; ///<Message to display in GUI and help.
   const int m_version;         ///< version of the real algorithm
 
diff --git a/Framework/API/inc/MantidAPI/IAlgorithm.h b/Framework/API/inc/MantidAPI/IAlgorithm.h
index 071e13f90f7377e98841c7a2011049e3ec43b29a..d22b7e91ac615efd7f397139af69d74c32d07803 100644
--- a/Framework/API/inc/MantidAPI/IAlgorithm.h
+++ b/Framework/API/inc/MantidAPI/IAlgorithm.h
@@ -80,6 +80,10 @@ public:
   /// function to return any aliases of the algorithm.
   virtual const std::string alias() const = 0;
 
+  /// function to return an optional URL for documentation.
+  /// Override if the algorithm is not part of the Mantid distribution
+  virtual const std::string helpURL() const = 0;
+
   /** @name Algorithms As Methods */
   ///@{
   /// Returns a name that will be used when attached as a workspace method.
diff --git a/Framework/PythonInterface/inc/MantidPythonInterface/api/PythonAlgorithm/AlgorithmAdapter.h b/Framework/PythonInterface/inc/MantidPythonInterface/api/PythonAlgorithm/AlgorithmAdapter.h
index 32c0f58fa80185cb77f9ab509bded35360cac58c..54c2f6efa879e5d089691408fd1706a4e6e5d92c 100644
--- a/Framework/PythonInterface/inc/MantidPythonInterface/api/PythonAlgorithm/AlgorithmAdapter.h
+++ b/Framework/PythonInterface/inc/MantidPythonInterface/api/PythonAlgorithm/AlgorithmAdapter.h
@@ -59,6 +59,8 @@ public:
   const std::string summary() const override;
   /// Returns a category of the algorithm.
   const std::string category() const override;
+  /// Returns optional documentation URL of the algorithm
+  const std::string helpURL() const override;
   /// Allow the isRunning method to be overridden
   bool isRunning() const override;
   /// Allow the cancel method to be overridden
diff --git a/Framework/PythonInterface/mantid/api/src/Exports/IAlgorithm.cpp b/Framework/PythonInterface/mantid/api/src/Exports/IAlgorithm.cpp
index cc92119a4c67aa89fc64093d332f1f88962e56f5..889e326b37407529948cd91e19f6da1a88b608af 100644
--- a/Framework/PythonInterface/mantid/api/src/Exports/IAlgorithm.cpp
+++ b/Framework/PythonInterface/mantid/api/src/Exports/IAlgorithm.cpp
@@ -346,6 +346,8 @@ void export_ialgorithm() {
            "Returns the list of categories this algorithm belongs to")
       .def("summary", &IAlgorithm::summary, arg("self"),
            "Returns a summary message describing the algorithm")
+      .def("helpURL", &IAlgorithm::helpURL, arg("self"),
+           "Returns optional URL for algorithm documentation")
       .def("workspaceMethodName", &IAlgorithm::workspaceMethodName, arg("self"),
            "Returns a name that will be used when attached as a workspace "
            "method. Empty string indicates do not attach")
diff --git a/Framework/PythonInterface/mantid/api/src/PythonAlgorithm/AlgorithmAdapter.cpp b/Framework/PythonInterface/mantid/api/src/PythonAlgorithm/AlgorithmAdapter.cpp
index 8a110298ee50ea2d56958080a07ca3b397d204f1..515d5c2052a7a5bfdb6caad6bf8e508e99b6dffd 100644
--- a/Framework/PythonInterface/mantid/api/src/PythonAlgorithm/AlgorithmAdapter.cpp
+++ b/Framework/PythonInterface/mantid/api/src/PythonAlgorithm/AlgorithmAdapter.cpp
@@ -103,9 +103,21 @@ const std::string AlgorithmAdapter<BaseAlgorithm>::summary() const {
 }
 
 /**
- * @return True if the algorithm is considered to be running
+ * Optional documentation URL of the algorithm, empty string if not overridden.
  */
 template <typename BaseAlgorithm>
+const std::string AlgorithmAdapter<BaseAlgorithm>::helpURL() const {
+  try {
+    return callMethod<std::string>(getSelf(), "helpURL");
+  } catch (UndefinedAttributeError &) {
+    return std::string();
+  }
+}
+
+/**
+*@return True if the algorithm is considered to be running
+*/
+template <typename BaseAlgorithm>
 bool AlgorithmAdapter<BaseAlgorithm>::isRunning() const {
   if (!m_isRunningObj) {
     return SuperClass::isRunning();
diff --git a/Framework/PythonInterface/test/python/mantid/api/AlgorithmManagerTest.py b/Framework/PythonInterface/test/python/mantid/api/AlgorithmManagerTest.py
index 9eababd3b7bda1f98bd8a127e54a36ba6176475b..5f5823d63a87f405503cfaf61cb8dcc1049c64de 100644
--- a/Framework/PythonInterface/test/python/mantid/api/AlgorithmManagerTest.py
+++ b/Framework/PythonInterface/test/python/mantid/api/AlgorithmManagerTest.py
@@ -15,6 +15,7 @@ class AlgorithmManagerTest(unittest.TestCase):
         self.assertEquals(alg.name(), "ConvertUnits")
         self.assertEquals(alg.version(), 1)
         self.assertEquals(alg.category(), "Transforms\\Units")
+        self.assertEquals(alg.helpURL(), "")
 
     def test_create_unknown_alg_throws(self):
         self.assertRaises(RuntimeError, AlgorithmManager.create,"DoesNotExist")
diff --git a/Framework/PythonInterface/test/python/mantid/api/AlgorithmTest.py b/Framework/PythonInterface/test/python/mantid/api/AlgorithmTest.py
index 96e223076e53debf067f23b498f28cf25a164cb9..e93b83e27edb3d123e5d575c5fba3a25e8aad784 100644
--- a/Framework/PythonInterface/test/python/mantid/api/AlgorithmTest.py
+++ b/Framework/PythonInterface/test/python/mantid/api/AlgorithmTest.py
@@ -21,6 +21,7 @@ class AlgorithmTest(unittest.TestCase):
         self.assertEquals('DataHandling', self._load.category())
         self.assertEquals(1, len(self._load.categories()))
         self.assertEquals('DataHandling', self._load.categories()[0])
+        self.assertEquals('', self._load.helpURL())
 
     def test_get_unknown_property_raises_error(self):
         self.assertRaises(RuntimeError, self._load.getProperty, "NotAProperty")
diff --git a/Framework/PythonInterface/test/python/mantid/api/PythonAlgorithmTraitsTest.py b/Framework/PythonInterface/test/python/mantid/api/PythonAlgorithmTraitsTest.py
index 23d67225cf8cab8d0ccc0c4ded6eb0925232d7a5..bbbf4a8895ffffb87378dbc36a61a914428de1d9 100644
--- a/Framework/PythonInterface/test/python/mantid/api/PythonAlgorithmTraitsTest.py
+++ b/Framework/PythonInterface/test/python/mantid/api/PythonAlgorithmTraitsTest.py
@@ -28,6 +28,9 @@ class TestPyAlgOverriddenAttrs(PythonAlgorithm):
     def category(self):
         return "BestAlgorithms"
 
+    def helpURL(self):
+        return "Optional documentation URL"
+
     def isRunning(self):
         return True
 
@@ -106,6 +109,7 @@ class PythonAlgorithmTest(unittest.TestCase):
         self.assertEquals(alg.name(), "TestPyAlgOverriddenAttrs")
         self.assertEquals(alg.version(), 2)
         self.assertEquals(alg.category(), "BestAlgorithms")
+        self.assertEquals(alg.helpURL(), "Optional documentation URL")
 
     def test_alg_can_be_cancelled(self):
         alg = AlgorithmManager.createUnmanaged("CancellableAlg")
diff --git a/MantidQt/MantidWidgets/src/MantidHelpWindow.cpp b/MantidQt/MantidWidgets/src/MantidHelpWindow.cpp
index c8613668120d3695ad8d108a03516bd3427093d8..281babbed39e8b7b1ed5013750aa2a5a5760b7d4 100644
--- a/MantidQt/MantidWidgets/src/MantidHelpWindow.cpp
+++ b/MantidQt/MantidWidgets/src/MantidHelpWindow.cpp
@@ -1,3 +1,4 @@
+#include "MantidAPI/AlgorithmManager.h"
 #include "MantidQtMantidWidgets/MantidHelpWindow.h"
 #include "MantidQtMantidWidgets/pqHelpWindow.h"
 #include "MantidQtAPI/InterfaceManager.h"
@@ -183,23 +184,38 @@ void MantidHelpWindow::showWikiPage(const QString &page) {
  */
 void MantidHelpWindow::showAlgorithm(const string &name, const int version) {
   auto versionStr("-v" + boost::lexical_cast<string>(version));
-  if (version <= 0)
+  if (version <= 0) {
     versionStr = ""; // let the redirect do its thing
+  }
 
+  QString help_url("");
+  if (!name.empty()) {
+    auto alg = Mantid::API::AlgorithmManager::Instance().createUnmanaged(name);
+    help_url = QString::fromStdString(alg->helpURL());
+  }
   if (bool(g_helpWindow)) {
-    QString url(BASE_URL);
-    url += "algorithms/";
-    if (name.empty())
-      url += "index.html";
-    else
-      url += QString(name.c_str()) + QString(versionStr.c_str()) + ".html";
-    this->showHelp(url);
-  } else // qt-assistant disabled
-  {
-    if (name.empty())
-      this->showWikiPage(std::string("Category:Algorithms"));
-    else
-      this->showWikiPage(name);
+    if (help_url.isEmpty()) {
+      QString url(BASE_URL);
+      url += "algorithms/";
+      if (name.empty()) {
+        url += "index.html";
+      } else {
+        url += QString(name.c_str()) + QString(versionStr.c_str()) + ".html";
+      }
+      this->showHelp(url);
+    } else {
+      this->showHelp(help_url);
+    }
+  } else { // qt-assistant disabled
+    if (help_url.isEmpty()) {
+      if (name.empty()) {
+        this->showWikiPage(std::string("Category:Algorithms"));
+      } else {
+        this->showWikiPage(name);
+      }
+    } else {
+      this->openWebpage(help_url);
+    }
   }
 }
 
diff --git a/MantidQt/MantidWidgets/src/pqHelpWindow.cxx b/MantidQt/MantidWidgets/src/pqHelpWindow.cxx
index 8e0bd48e30d88dae1e6920d7e3db2dfcf1e5d042..c93d46055507054123f222ed6dfb6d8f924ed4be 100644
--- a/MantidQt/MantidWidgets/src/pqHelpWindow.cxx
+++ b/MantidQt/MantidWidgets/src/pqHelpWindow.cxx
@@ -53,7 +53,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <QUrl>
 #include <QWebHistory>
 #include <QWebView>
-#include <iostream>
 
 using MantidQt::API::MantidDesktopServices;
 
@@ -305,7 +304,7 @@ void pqHelpWindow::errorMissingPage(const QUrl& url)
 //-----------------------------------------------------------------------------
 void pqHelpWindow::showPage(const QString& url)
 {
-  this->showPage(QUrl(url));
+  this->showPage(QUrl::fromUserInput(url));
 }
 
 //-----------------------------------------------------------------------------
diff --git a/docs/source/release/v3.10.0/framework.rst b/docs/source/release/v3.10.0/framework.rst
index e42695c21e6252a4ccdc7c7b01492e2bc02d3cb3..14805eed1e49432087a3c5c9f96e2b4507c6878d 100644
--- a/docs/source/release/v3.10.0/framework.rst
+++ b/docs/source/release/v3.10.0/framework.rst
@@ -18,6 +18,8 @@ Algorithms
 ----------
 
 - Removed the optional flag ``LocationParameters`` from ``ClearInstrumentParameters``.
+- New method `IAlgorithm::helpURL` returns an optional documentation webpage. Useful when registering Python
+  algorithms at runtime that are not part of the Mantid distribution.
 
 New
 ###