diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index 814b08827b..c91548d78a 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -1255,6 +1255,14 @@ admin/views/users.vue:
       createdAtDesc: "登録日時が新しい順"
       updatedAtAsc: "更新日時が古い順"
       updatedAtDesc: "更新日時が新しい順"
+    state:
+      title: "状態"
+      all: "すべて"
+      admin: "管理者"
+      moderator: "モデレーター"
+      adminOrModerator: "管理者+モデレーター"
+      verified: "公式アカウント"
+      suspended: "凍結済み"
     origin:
       title: "オリジン"
       combined: "ローカル+リモート"
diff --git a/src/client/app/admin/views/users.vue b/src/client/app/admin/views/users.vue
index 21d41efba8..dcacdd5bad 100644
--- a/src/client/app/admin/views/users.vue
+++ b/src/client/app/admin/views/users.vue
@@ -31,6 +31,14 @@
 					<option value="-updatedAt">{{ $t('users.sort.updatedAtAsc') }}</option>
 					<option value="+updatedAt">{{ $t('users.sort.updatedAtDesc') }}</option>
 				</ui-select>
+				<ui-select v-model="state">
+					<span slot="label">{{ $t('users.state.title') }}</span>
+					<option value="all">{{ $t('users.state.all') }}</option>
+					<option value="admin">{{ $t('users.state.admin') }}</option>
+					<option value="moderator">{{ $t('users.state.moderator') }}</option>
+					<option value="verified">{{ $t('users.state.verified') }}</option>
+					<option value="suspended">{{ $t('users.state.suspended') }}</option>
+				</ui-select>
 				<ui-select v-model="origin">
 					<span slot="label">{{ $t('users.origin.title') }}</span>
 					<option value="combined">{{ $t('users.origin.combined') }}</option>
@@ -39,7 +47,7 @@
 				</ui-select>
 			</ui-horizon-group>
 			<sequential-entrance animation="entranceFromTop" delay="25">
-				<div class="kofvwchc" v-for="user in users">
+				<div class="kofvwchc" v-for="user in users" :key="user.id">
 					<div>
 						<a :href="user | userPage(null, true)">
 							<mk-avatar class="avatar" :user="user" :disable-link="true"/>
@@ -49,6 +57,10 @@
 						<header>
 							<b><mk-user-name :user="user"/></b>
 							<span class="username">@{{ user | acct }}</span>
+							<span class="is-admin" v-if="user.isAdmin">admin</span>
+							<span class="is-moderator" v-if="user.isModerator">moderator</span>
+							<span class="is-verified" v-if="user.isVerified" :title="$t('@.verified-user')"><fa icon="star"/></span>
+							<span class="is-suspended" v-if="user.isSuspended" :title="$t('@.suspended-user')"><fa :icon="faSnowflake"/></span>
 						</header>
 						<div>
 							<span>{{ $t('users.updatedAt') }}: <mk-time :time="user.updatedAt" mode="detail"/></span>
@@ -84,6 +96,7 @@ export default Vue.extend({
 			suspending: false,
 			unsuspending: false,
 			sort: '+createdAt',
+			state: 'all',
 			origin: 'combined',
 			limit: 10,
 			offset: 0,
@@ -100,6 +113,12 @@ export default Vue.extend({
 			this.fetchUsers();
 		},
 
+		state() {
+			this.users = [];
+			this.offset = 0;
+			this.fetchUsers();
+		},
+
 		origin() {
 			this.users = [];
 			this.offset = 0;
@@ -236,7 +255,8 @@ export default Vue.extend({
 		},
 
 		fetchUsers() {
-			this.$root.api('users', {
+			this.$root.api('admin/show-users', {
+				state: this.state,
 				origin: this.origin,
 				sort: this.sort,
 				offset: this.offset,
@@ -284,4 +304,19 @@ export default Vue.extend({
 					margin-left 8px
 					opacity 0.7
 
+				> .is-admin
+				> .is-moderator
+					flex-shrink 0
+					align-self center
+					margin 0 0 0 .5em
+					padding 1px 6px
+					font-size 80%
+					border-radius 3px
+					background var(--noteHeaderAdminBg)
+					color var(--noteHeaderAdminFg)
+
+				> .is-verified
+				> .is-suspended
+					margin 0 0 0 .5em
+					color #4dabf7
 </style>
diff --git a/src/server/api/endpoints/admin/show-users.ts b/src/server/api/endpoints/admin/show-users.ts
new file mode 100644
index 0000000000..20ccfbd7f3
--- /dev/null
+++ b/src/server/api/endpoints/admin/show-users.ts
@@ -0,0 +1,123 @@
+import $ from 'cafy';
+import User, { pack } from '../../../../models/user';
+import define from '../../define';
+
+export const meta = {
+	requireCredential: true,
+	requireModerator: true,
+
+	params: {
+		limit: {
+			validator: $.num.optional.range(1, 100),
+			default: 10
+		},
+
+		offset: {
+			validator: $.num.optional.min(0),
+			default: 0
+		},
+
+		sort: {
+			validator: $.str.optional.or([
+				'+follower',
+				'-follower',
+				'+createdAt',
+				'-createdAt',
+				'+updatedAt',
+				'-updatedAt',
+			]),
+		},
+
+		state: {
+			validator: $.str.optional.or([
+				'all',
+				'admin',
+				'moderator',
+				'adminOrModerator',
+				'verified',
+				'suspended',
+			]),
+			default: 'all'
+		},
+
+		origin: {
+			validator: $.str.optional.or([
+				'combined',
+				'local',
+				'remote',
+			]),
+			default: 'local'
+		}
+	}
+};
+
+export default define(meta, (ps, me) => new Promise(async (res, rej) => {
+	let _sort;
+	if (ps.sort) {
+		if (ps.sort == '+follower') {
+			_sort = {
+				followersCount: -1
+			};
+		} else if (ps.sort == '-follower') {
+			_sort = {
+				followersCount: 1
+			};
+		} else if (ps.sort == '+createdAt') {
+			_sort = {
+				createdAt: -1
+			};
+		} else if (ps.sort == '+updatedAt') {
+			_sort = {
+				updatedAt: -1
+			};
+		} else if (ps.sort == '-createdAt') {
+			_sort = {
+				createdAt: 1
+			};
+		} else if (ps.sort == '-updatedAt') {
+			_sort = {
+				updatedAt: 1
+			};
+		}
+	} else {
+		_sort = {
+			_id: -1
+		};
+	}
+
+	const q = {
+		$and: []
+	} as any;
+
+	// state
+	q.$and.push(
+		ps.state == 'admin' ? { isAdmin: true } :
+		ps.state == 'moderator' ? { isModerator: true } :
+		ps.state == 'adminOrModerator' ? {
+			$or: [{
+				isAdmin: true
+			}, {
+				isModerator: true
+			}]
+		} :
+		ps.state == 'verified' ? { isVerified: true } :
+		ps.state == 'suspended' ? { isSuspended: true } :
+		{}
+	);
+
+	// origin
+	q.$and.push(
+		ps.origin == 'local' ? { host: null } :
+		ps.origin == 'remote' ? { host: { $ne: null } } :
+		{}
+	);
+
+	const users = await User
+		.find(q, {
+			limit: ps.limit,
+			sort: _sort,
+			skip: ps.offset
+		});
+
+	res(await Promise.all(users.map(user => pack(user, me, { detail: true }))));
+}));