diff --git a/include/opendht/callbacks.h b/include/opendht/callbacks.h
index 91ebd84df8a738c9b31d02e38e2e98211d4315b1..604a164014908e47ef186b38917448eb2fa72079 100644
--- a/include/opendht/callbacks.h
+++ b/include/opendht/callbacks.h
@@ -51,6 +51,7 @@ struct OPENDHT_PUBLIC NodeStats {
              incoming_nodes {0};
     unsigned table_depth {0};
     unsigned getKnownNodes() const { return good_nodes + dubious_nodes; }
+    unsigned long getNetworkSizeEstimation() const { return 8 * std::exp2(table_depth); }
     std::string toString() const;
 
 #ifdef OPENDHT_JSONCPP
diff --git a/include/opendht/dht_proxy_server.h b/include/opendht/dht_proxy_server.h
index e240aa4e5dcdef541729d2a090f1c50b4c5069b2..56f0e7fc256c8ab57dc619ef8e7a4c7eefefe7b7 100644
--- a/include/opendht/dht_proxy_server.h
+++ b/include/opendht/dht_proxy_server.h
@@ -34,6 +34,10 @@
 #include <mutex>
 #include <restbed>
 
+#ifdef OPENDHT_JSONCPP
+#include <json/json.h>
+#endif
+
 namespace Json {
     class Value;
 }
@@ -73,15 +77,44 @@ public:
         size_t pushListenersCount;
         /** Average requests per second */
         double requestRate;
+        /** Node Info **/
+        NodeInfo nodeInfo;
 
         std::string toString() const {
             std::ostringstream ss;
             ss << "Listens: " << listenCount << " Puts: " << putCount << " PushListeners: " << pushListenersCount << std::endl;
             ss << "Requests: " << requestRate << " per second." << std::endl;
+            auto& ni = nodeInfo;
+            auto& ipv4 = ni.ipv4;
+            if (ipv4.table_depth > 1) {
+                ss << "IPv4 Network estimation: " << ipv4.getNetworkSizeEstimation() << std::endl;;
+            }
+            auto& ipv6 = ni.ipv6;
+            if (ipv6.table_depth > 1) {
+                ss << "IPv6 Network estimation: " << ipv6.getNetworkSizeEstimation() << std::endl;;
+            }
             return ss.str();
         }
+
+#ifdef OPENDHT_JSONCPP
+        /**
+         * Build a json object from a NodeStats
+         */
+        Json::Value toJson() const {
+            Json::Value result;
+            result["listenCount"] = static_cast<Json::UInt64>(listenCount);
+            result["putCount"] = static_cast<Json::UInt64>(putCount);
+            result["pushListenersCount"] = static_cast<Json::UInt64>(pushListenersCount);
+            result["requestRate"] = requestRate;
+            result["nodeInfo"] = nodeInfo.toJson();
+            return result;
+        }
+#endif
     };
-    ServerStats getStats() const;
+
+    ServerStats stats() const { return stats_; }
+
+    void updateStats() const;
 
     std::shared_ptr<DhtRunner> getNode() const { return dht_; }
 
@@ -100,6 +133,14 @@ private:
      */
     void getNodeInfo(const std::shared_ptr<restbed::Session>& session) const;
 
+    /**
+     * Return ServerStats in JSON format
+     * Method: STATS "/"
+     * Result: HTTP 200, body: Node infos in JSON format
+     * @param session
+     */
+    void getStats(const std::shared_ptr<restbed::Session>& session) const;
+
     /**
      * Return Values of an infoHash
      * Method: GET "/{InfoHash: .*}"
@@ -262,6 +303,8 @@ private:
 
     const std::string pushServer_;
 
+    mutable ServerStats stats_;
+
 #if OPENDHT_PUSH_NOTIFICATIONS
     struct Listener;
     struct PushListener;
diff --git a/src/callbacks.cpp b/src/callbacks.cpp
index ec57d2b13295e54128ce3bebb9acc3c0d78efabb..be6bec9a3dd39c0739be486c54f1b499052e28c5 100644
--- a/src/callbacks.cpp
+++ b/src/callbacks.cpp
@@ -62,8 +62,7 @@ NodeStats::toString() const
     ss << "Known nodes: " << good_nodes << " good, " << dubious_nodes << " dubious, " << incoming_nodes << " incoming." << std::endl;
     if (table_depth > 1) {
         ss << "Routing table depth: " << table_depth << std::endl;
-        unsigned long tot_nodes = 8 * std::exp2(table_depth);
-        ss << "Network size estimation: " << tot_nodes << " nodes" << std::endl;
+        ss << "Network size estimation: " << getNetworkSizeEstimation() << " nodes" << std::endl;
     }
     return ss.str();
 }
@@ -81,8 +80,7 @@ NodeStats::toJson() const
     val["incoming"] = static_cast<Json::LargestUInt>(incoming_nodes);
     if (table_depth > 1) {
         val["table_depth"] = static_cast<Json::LargestUInt>(table_depth);
-        unsigned long tot_nodes = 8 * std::exp2(table_depth);
-        val["network_size_estimation"] = static_cast<Json::LargestUInt>(tot_nodes);
+        val["network_size_estimation"] = static_cast<Json::LargestUInt>(getNetworkSizeEstimation());
     }
     return val;
 }
diff --git a/src/dht_proxy_server.cpp b/src/dht_proxy_server.cpp
index 6506a40e775d504305bdd46ca83d6112da2bd4e6..81272a2defa8b954455e1ffbb2cc1e5a6719f92f 100644
--- a/src/dht_proxy_server.cpp
+++ b/src/dht_proxy_server.cpp
@@ -70,6 +70,7 @@ DhtProxyServer::DhtProxyServer(std::shared_ptr<DhtRunner> dht, in_port_t port ,
         auto resource = std::make_shared<restbed::Resource>();
         resource->set_path("/");
         resource->set_method_handler("GET", std::bind(&DhtProxyServer::getNodeInfo, this, _1));
+        resource->set_method_handler("STATS", std::bind(&DhtProxyServer::getStats, this, _1));
         service_->publish(resource);
         resource = std::make_shared<restbed::Resource>();
         resource->set_path("/{hash: .*}");
@@ -137,7 +138,7 @@ DhtProxyServer::DhtProxyServer(std::shared_ptr<DhtRunner> dht, in_port_t port ,
     printStatsJob_ = scheduler_.add(scheduler_.time() + PRINT_STATS_PERIOD, [this] {
         if (stopListeners) return;
         if (service_->is_up())
-            std::cout << getStats().toString() << std::endl;
+            updateStats();
         // Refresh stats cache
         auto newInfo = dht_->getNodeInfo();
         {
@@ -178,21 +179,20 @@ DhtProxyServer::stop()
         server_thread.join();
 }
 
-DhtProxyServer::ServerStats
-DhtProxyServer::getStats() const
+void
+DhtProxyServer::updateStats() const
 {
-    ServerStats ret {};
     auto now = clock::now();
     auto last = lastStatsReset_.exchange(now);
     auto count = requestNum_.exchange(0);
     auto dt = std::chrono::duration<double>(now - last);
-    ret.requestRate = count / dt.count();
+    stats_.requestRate = count / dt.count();
 #if OPENDHT_PUSH_NOTIFICATIONS
-    ret.pushListenersCount = pushListeners_.size();
+    stats_.pushListenersCount = pushListeners_.size();
 #endif
-    ret.putCount = puts_.size();
-    ret.listenCount = currentListeners_.size();
-    return ret;
+    stats_.putCount = puts_.size();
+    stats_.listenCount = currentListeners_.size();
+    stats_.nodeInfo = nodeInfo_;
 }
 
 void
@@ -234,6 +234,36 @@ DhtProxyServer::getNodeInfo(const std::shared_ptr<restbed::Session>& session) co
     );
 }
 
+void
+DhtProxyServer::getStats(const std::shared_ptr<restbed::Session>& session) const
+{
+    requestNum_++;
+    const auto request = session->get_request();
+    int content_length = std::stoi(request->get_header("Content-Length", "0"));
+    session->fetch(content_length,
+        [this](const std::shared_ptr<restbed::Session> s, const restbed::Bytes& /*b*/) mutable
+        {
+            try {
+                if (dht_) {
+#ifdef OPENDHT_JSONCPP
+                    Json::StreamWriterBuilder wbuilder;
+                    wbuilder["commentStyle"] = "None";
+                    wbuilder["indentation"] = "";
+                    auto output = Json::writeString(wbuilder, stats_.toJson()) + "\n";
+                    s->close(restbed::OK, output);
+#else
+                    s->close(restbed::NotFound, "{\"err\":\"JSON not enabled on this instance\"}");
+#endif
+                }
+                else
+                    s->close(restbed::SERVICE_UNAVAILABLE, "{\"err\":\"Incorrect DhtRunner\"}");
+            } catch (...) {
+                s->close(restbed::INTERNAL_SERVER_ERROR, "{\"err\":\"Internal server error\"}");
+            }
+        }
+    );
+}
+
 void
 DhtProxyServer::get(const std::shared_ptr<restbed::Session>& session) const
 {
diff --git a/tools/dhtnode.cpp b/tools/dhtnode.cpp
index 1117a6d8253ac2c4d6a26c5eac48206fa7e435eb..98ae5e6ab93ddd00e4aa0571da22e995f994c1fc 100644
--- a/tools/dhtnode.cpp
+++ b/tools/dhtnode.cpp
@@ -144,7 +144,7 @@ void cmd_loop(std::shared_ptr<DhtRunner>& dht, dht_params& params
 #if OPENDHT_PROXY_SERVER
             for (const auto& proxy : proxies) {
                 std::cout << "Stats for proxy on port " << proxy.first << std::endl;
-                std::cout << "  " << proxy.second->getStats().toString() << std::endl;
+                std::cout << "  " << proxy.second->stats().toString() << std::endl;
             }
 #endif
             continue;