diff --git a/lib/partner-listener/routers/product-price.js b/lib/partner-listener/routers/product-price.js
index 8168ac2..c63f146 100644
--- a/lib/partner-listener/routers/product-price.js
+++ b/lib/partner-listener/routers/product-price.js
@@ -1,8 +1,13 @@
 const MODULE_NAME = 'PARTNER-LISTENER.ROUTERS.PRODUCT-PRICE';
 
+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');
@@ -16,6 +21,16 @@ 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 traverse = (data, productType) => {
     const products = [];
 
@@ -47,11 +62,16 @@ const traverse = (data, productType) => {
     return products;
 };
 
+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 {
@@ -96,4 +116,4 @@ const mainHandler = async (req, res) => {
     }
 };
 
-router.get('/', mainHandler);
+router.get('/', extractTerminalName, rateLimit, mainHandler);
diff --git a/package-lock.json b/package-lock.json
index 236a393..11fde53 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -11,6 +11,7 @@
       "dependencies": {
         "axios": "^0.19.2",
         "express": "^4.17.1",
+        "express-rate-limit": "^6.6.0",
         "join-path": "^1.1.1",
         "komodo-sdk": "^1.45.6",
         "mkdirp": "^1.0.4",
@@ -1173,6 +1174,17 @@
         "node": ">= 0.10.0"
       }
     },
+    "node_modules/express-rate-limit": {
+      "version": "6.6.0",
+      "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-6.6.0.tgz",
+      "integrity": "sha512-HFN2+4ZGdkQOS8Qli4z6knmJFnw6lZed67o6b7RGplWeb1Z0s8VXaj3dUgPIdm9hrhZXTRpCTHXA0/2Eqex0vA==",
+      "engines": {
+        "node": ">= 12.9.0"
+      },
+      "peerDependencies": {
+        "express": "^4 || ^5"
+      }
+    },
     "node_modules/express-session": {
       "version": "1.17.2",
       "resolved": "https://registry.npmjs.org/express-session/-/express-session-1.17.2.tgz",
@@ -5600,6 +5612,12 @@
         }
       }
     },
+    "express-rate-limit": {
+      "version": "6.6.0",
+      "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-6.6.0.tgz",
+      "integrity": "sha512-HFN2+4ZGdkQOS8Qli4z6knmJFnw6lZed67o6b7RGplWeb1Z0s8VXaj3dUgPIdm9hrhZXTRpCTHXA0/2Eqex0vA==",
+      "requires": {}
+    },
     "express-session": {
       "version": "1.17.2",
       "resolved": "https://registry.npmjs.org/express-session/-/express-session-1.17.2.tgz",
diff --git a/package.json b/package.json
index 392374b..33096de 100644
--- a/package.json
+++ b/package.json
@@ -31,6 +31,7 @@
   "dependencies": {
     "axios": "^0.19.2",
     "express": "^4.17.1",
+    "express-rate-limit": "^6.6.0",
     "join-path": "^1.1.1",
     "komodo-sdk": "^1.45.6",
     "mkdirp": "^1.0.4",