diff --git a/README.md b/README.md
index d7400e8594..2d1f2c50bd 100644
--- a/README.md
+++ b/README.md
@@ -79,6 +79,10 @@ Note that `$(pwd)` is the working directory.
 ## Launch
 `sudo npm start`
 
+## Debug
+### Show debug messages
+Misskey uses [debug](https://github.com/visionmedia/debug) and namespace is 'misskey:*'.
+
 ## Contribute
 Do you have feature request or problem with Misskey?
 Please create issue to report it if it is about the Misskey implementation itself.
diff --git a/package.json b/package.json
index 3fd0ae1237..d8eb0f3b07 100644
--- a/package.json
+++ b/package.json
@@ -29,6 +29,7 @@
     "@types/chalk": "0.4.31",
     "@types/compression": "0.0.33",
     "@types/cors": "2.8.0",
+    "@types/debug": "0.0.29",
     "@types/elasticsearch": "5.0.12",
     "@types/escape-html": "0.0.19",
     "@types/event-stream": "3.3.30",
@@ -82,6 +83,7 @@
     "cors": "2.8.1",
     "cropperjs": "1.0.0-beta",
     "crypto": "0.0.3",
+    "debug": "2.6.0",
     "deepcopy": "0.6.3",
     "del": "2.2.2",
     "elasticsearch": "12.1.3",
diff --git a/src/api/limitter.ts b/src/api/limitter.ts
index 9cc25675d8..ea1ece6a70 100644
--- a/src/api/limitter.ts
+++ b/src/api/limitter.ts
@@ -1,8 +1,11 @@
 import * as Limiter from 'ratelimiter';
+import * as debug from 'debug';
 import limiterDB from '../db/redis';
 import { IEndpoint } from './endpoints';
 import { IAuthContext } from './authenticate';
 
+const log = debug('misskey:limitter');
+
 export default (endpoint: IEndpoint, ctx: IAuthContext) => new Promise((ok, reject) => {
 	const limitKey = endpoint.hasOwnProperty('limitKey')
 		? endpoint.limitKey
@@ -24,7 +27,7 @@ export default (endpoint: IEndpoint, ctx: IAuthContext) => new Promise((ok, reje
 	}
 
 	// Short-term limit
-	function min(): void {
+	function min() {
 		const minIntervalLimiter = new Limiter({
 			id: `${ctx.user._id}:${limitKey}:min`,
 			duration: endpoint.minInterval,
@@ -32,10 +35,14 @@ export default (endpoint: IEndpoint, ctx: IAuthContext) => new Promise((ok, reje
 			db: limiterDB
 		});
 
-		minIntervalLimiter.get((limitErr, limit) => {
-			if (limitErr) {
-				reject('ERR');
-			} else if (limit.remaining === 0) {
+		minIntervalLimiter.get((err, info) => {
+			if (err) {
+				return reject('ERR');
+			}
+
+			log(`min remaining: ${info.remaining}`);
+
+			if (info.remaining === 0) {
 				reject('BRIEF_REQUEST_INTERVAL');
 			} else {
 				if (hasRateLimit) {
@@ -48,7 +55,7 @@ export default (endpoint: IEndpoint, ctx: IAuthContext) => new Promise((ok, reje
 	}
 
 	// Long term limit
-	function max(): void {
+	function max() {
 		const limiter = new Limiter({
 			id: `${ctx.user._id}:${limitKey}`,
 			duration: endpoint.limitDuration,
@@ -56,10 +63,14 @@ export default (endpoint: IEndpoint, ctx: IAuthContext) => new Promise((ok, reje
 			db: limiterDB
 		});
 
-		limiter.get((limitErr, limit) => {
-			if (limitErr) {
-				reject('ERR');
-			} else if (limit.remaining === 0) {
+		limiter.get((err, info) => {
+			if (err) {
+				return reject('ERR');
+			}
+
+			log(`max remaining: ${info.remaining}`);
+
+			if (info.remaining === 0) {
 				reject('RATE_LIMIT_EXCEEDED');
 			} else {
 				ok();