diff --git a/src/remote/activitypub/models/note.ts b/src/remote/activitypub/models/note.ts
index 9fc03c777d..a45946a050 100644
--- a/src/remote/activitypub/models/note.ts
+++ b/src/remote/activitypub/models/note.ts
@@ -10,7 +10,7 @@ import { resolvePerson, updatePerson } from './person';
 import { resolveImage } from './image';
 import { IRemoteUser, IUser } from '../../../models/user';
 import htmlToMFM from '../../../mfm/html-to-mfm';
-import Emoji from '../../../models/emoji';
+import Emoji, { IEmoji } from '../../../models/emoji';
 import { ITag } from './tag';
 import { toUnicode } from 'punycode';
 import { unique, concat, difference } from '../../../prelude/array';
@@ -84,6 +84,8 @@ export async function createNote(value: any, resolver?: Resolver, silent = false
 
 	const apMentions = await extractMentionedUsers(actor, note.to, note.cc, resolver);
 
+	const apHashtags = await extractHashtags(note.tag);
+
 	// 添付ファイル
 	// TODO: attachmentは必ずしもImageではない
 	// TODO: attachmentは必ずしも配列ではない
@@ -108,10 +110,13 @@ export async function createNote(value: any, resolver?: Resolver, silent = false
 	// テキストのパース
 	const text = note._misskey_content ? note._misskey_content : htmlToMFM(note.content);
 
-	await extractEmojis(note.tag, actor.host).catch(e => {
+	const emojis = await extractEmojis(note.tag, actor.host).catch(e => {
 		console.log(`extractEmojis: ${e}`);
+		return [] as IEmoji[];
 	});
 
+	const apEmojis = emojis.map(emoji => emoji.name);
+
 	// ユーザーの情報が古かったらついでに更新しておく
 	if (actor.lastFetchedAt == null || Date.now() - actor.lastFetchedAt.getTime() > 1000 * 60 * 60 * 24) {
 		updatePerson(note.attributedTo);
@@ -130,6 +135,8 @@ export async function createNote(value: any, resolver?: Resolver, silent = false
 		visibility,
 		visibleUsers,
 		apMentions,
+		apHashtags,
+		apEmojis,
 		uri: note.id
 	}, silent);
 }
@@ -199,3 +206,14 @@ async function extractMentionedUsers(actor: IRemoteUser, to: string[], cc: strin
 
 	return users.filter(x => x != null);
 }
+
+function extractHashtags(tags: ITag[]) {
+	if (!tags) return [];
+
+	const hashtags = tags.filter(tag => tag.type === 'Hashtag' && typeof tag.name == 'string');
+
+	return hashtags.map(tag => {
+		const m = tag.name.match(/^#(.+)/);
+		return m ? m[1] : null;
+	}).filter(x => x != null);
+}
diff --git a/src/services/note/create.ts b/src/services/note/create.ts
index a35e4fe18d..9fee12d6ad 100644
--- a/src/services/note/create.ts
+++ b/src/services/note/create.ts
@@ -98,6 +98,8 @@ type Option = {
 	visibility?: string;
 	visibleUsers?: IUser[];
 	apMentions?: IUser[];
+	apHashtags?: string[];
+	apEmojis?: string[];
 	uri?: string;
 	app?: IApp;
 };
@@ -153,16 +155,22 @@ export default async (user: IUser, data: Option, silent = false) => new Promise<
 		data.text = data.text.trim();
 	}
 
-	// Parse MFM
-	const tokens = data.text ? parse(data.text) : [];
-	const cwTokens = data.cw ? parse(data.cw) : [];
-	const combinedTokens = tokens.concat(cwTokens);
+	let tags = data.apHashtags;
+	let emojis = data.apEmojis;
+	let mentionedUsers = data.apMentions;
 
-	const tags = extractHashtags(combinedTokens);
+	// Parse MFM if needed
+	if (!tags || !emojis || !mentionedUsers) {
+		const tokens = data.text ? parse(data.text) : [];
+		const cwTokens = data.cw ? parse(data.cw) : [];
+		const combinedTokens = tokens.concat(cwTokens);
 
-	const emojis = extractEmojis(combinedTokens);
+		tags = data.apHashtags || extractHashtags(combinedTokens);
 
-	const mentionedUsers = data.apMentions || await extractMentionedUsers(user, combinedTokens);
+		emojis = data.apEmojis || extractEmojis(combinedTokens);
+
+		mentionedUsers = data.apMentions || await extractMentionedUsers(user, combinedTokens);
+	}
 
 	if (data.reply && !user._id.equals(data.reply.userId) && !mentionedUsers.some(u => u._id.equals(data.reply.userId))) {
 		mentionedUsers.push(await User.findOne({ _id: data.reply.userId }));