diff --git a/lib/partner-listener/routers/product-tree.js b/lib/partner-listener/routers/product-tree.js
index f1e7b81..30ed9c2 100644
--- a/lib/partner-listener/routers/product-tree.js
+++ b/lib/partner-listener/routers/product-tree.js
@@ -1,8 +1,13 @@
 const MODULE_NAME = 'PARTNER-LISTENER.ROUTERS.PRODUCT-TREE';
 
+const RATE_LIMIT_MESSAGE = 'Rate limited. Cobalah satu menit lagi!\n';
+const RATE_LIMIT_MAX = 2;
+const RATE_LIMIT_WINDOW_MS = 60 * 1000;
+
 const axios = require('axios').default;
 const express = require('express');
 const urlJoin = require('join-path');
+const expressRateLimit = require('express-rate-limit');
 
 const coreUrl = require('komodo-sdk/core-url');
 const logger = require('tektrans-logger');
@@ -15,11 +20,26 @@ const CORE_ENDPOINT = urlJoin(coreUrl, '/product-tree');
 const router = express.Router();
 module.exports = router;
 
+const rateLimit = expressRateLimit({
+    windowMs: RATE_LIMIT_WINDOW_MS,
+    max: RATE_LIMIT_MAX,
+    message: RATE_LIMIT_MESSAGE,
+    keyGenerator: (req, res) => res.locals && res.locals.terminalName,
+    // handler: (req, res, next, opts) => {
+    //     onRateLimited(req, res, 'ip', opts);
+    // },
+});
+
+const extractTerminalName = (req, res, next) => {
+    const terminalNameWithoutIp = (getFromBodyQsParams(req, 'terminal_name') || '').toString().trim();
+    res.locals.terminalName = `${terminalNameWithoutIp}@${ipv6ToIpv4(req.ip)}`;
+    next();
+};
+
 const mainHandler = async (req, res) => {
     const { xid } = res.locals;
 
-    const terminalNameWithoutIp = (getFromBodyQsParams(req, 'terminal_name') || '').toString().trim();
-    const terminalName = `${terminalNameWithoutIp}@${ipv6ToIpv4(req.ip)}`;
+    const { terminalName } = res.locals;
     const password = getFromBodyQsParams(req, 'password');
 
     try {
@@ -52,4 +72,4 @@ const mainHandler = async (req, res) => {
     }
 };
 
-router.get('/', mainHandler);
+router.get('/', extractTerminalName, rateLimit, mainHandler);