Is Xiaohongshu for Desktop safe?
Xiaohongshu for Desktop sends each post's ID and Xiaohongshu access token to a third-party server (api.old-panda.com) to fetch media.
When you download a Xiaohongshu post, the extension reads the post ID and the post's xsec_token (Xiaohongshu's own per-resource signed access token) from the page URL and forwards both to api.old-panda.com, which returns the post's image and video URLs, title and text. Separately, its translate buttons send the visible text of a post or a selected comment to Google Translate's public endpoint. Both happen only when you click download or translate, not in the background.
AI-generated. Findings may contain errors. Those marked Verified have been manually reviewed.
Publishers can request a review.
Findings
Download action sends Xiaohongshu token to old-panda API
Clicking the download button makes the content script read the post ID and xsec_token from the page URL and send them to the worker, which GETs api.old-panda.com for the media, title, and text.
A browsing-only run didn't trigger this.
You click the download button that the extension adds to a Xiaohongshu or Rednote post.
The extension reads the post ID and page token from the URL and sends them to api.old-panda.com.
| Field | Value | Why it matters | |
|---|---|---|---|
Post identifier | 65f1a2b3c4d5e6f7890abcde | This identifies the exact Xiaohongshu or Rednote post you chose to download. | |
Post access token | AT_eyJzaWQiOiJ4aHMiLCJub25jZSI6IjE3MTYyNjM0MDAifQ | This token is copied from the page URL and travels with the post lookup request. | |
Returned media URLs | https://sns-img-qc.xhscdn.com/1040g00830vtb9apg6g005n4bq6c0j6a2tq8h6f8 | The response can describe the images or videos that will be saved from that post. | |
Returned title and text | Weekend route notes Coffee shops and photo locations near the river | The response can include the title and written content of the post you selected. |
| Content-Type | application/json |
The shipped code parses the token and forwards it to the API
async downloadPostByHref(href) {
if (!href.includes(XHS_DOMAIN_ZH) && !href.includes(XHS_DOMAIN_EN)) {
throw new Error("Unsupported post URL.");
}
const url = new URL(href);
const postId = url.pathname
.split("/")
.filter((e) => e)
.pop();
const xsecToken = url.searchParams.get("xsec_token") || "";
const response = await browser.runtime.sendMessage({
action: ACTION_GET_DL_DATA,
postId,
xsecToken,
});
if (!response?.success) {
throw new Error(
getErrorMessage(response?.message, "Failed to fetch post data."),
);
}
const storageData = await getStorage([STORAGE_FILENAME_FORMAT]);
const fileNameFormat = storageData[STORAGE_FILENAME_FORMAT];
const { baseName, zipBlob } = await zipFile(
response.data,
postId,
fileNameFormat,
);
const fileName = `${baseName}${ZIP_FILE_NAME_SUFFIX}`;
this.saveBlob(zipBlob, fileName);
return fileName;
} async handleMessage(message, sendResponse, Filter) {
if (message.action === ACTION_GET_DL_DATA) {
try {
const response = await fetch(
`${DL_API_URL}${message.postId}&xsec_token=${message.xsecToken}`,
{
headers: { "Content-Type": "application/json" },
},
);
if (response.status == 200) {
const data = await response.json();
sendResponse({ success: true, data });
} else {
throw new Error(
"Failed to get video information. Please try again later or check your network connection.",
);
}
} catch (error) {
sendResponse({ success: false, message: error.message });
}
} else if (message.action === ACTION_GET_TRANSLATE_DATA) {
const currentLang = await getStorage([STORAGE_TRANSLATE_LANGUAGE]);
let lang =
currentLang[STORAGE_TRANSLATE_LANGUAGE] || DEFAULT_TRANSLATE_LANG;
if (lang === DEFAULT_TRANSLATE_LANG) {
lang = browser.i18n.getUILanguage();
}
try {
const data = await this.translateText(
message.text,
message.tagText,
lang,
);
sendResponse({ success: true, data });
} catch (error) {
sendResponse({ success: false, message: error.message });
}
} else if (message.action === ACTION_OPEN_SETTING) {
await browser.tabs.create({
url: browser.runtime.getURL(SETTING_PAGE),
});
sendResponse({ success: true });
} else if (message.action === ACTION_APPLY_SCHEDULE) {
const data = await getStorage([
STORAGE_SCHEDULE_ENABLED,
STORAGE_SCHEDULE_START,
STORAGE_SCHEDULE_END,
]);
await this.applySchedule(data);
sendResponse({ success: true });
} else if (message.action === ACTION_OPEN_XHS) {
const lang = message.lang;
const domain = lang === "zh" ? XHS_DOMAIN_ZH : XHS_DOMAIN_EN;
Filter.openAppWindow(`https://${domain}`);
sendResponse({ success: true });
}
}export const DL_API_URL = "https://api.old-panda.com/image/xiaohongshu/?post_id="; export const ACTION_GET_DL_DATA = "getDlData";
- api.old-panda.com
Receives the Xiaohongshu post ID and xsec_token in the query string and returns media metadata for the extension's download workflow.
- www.xiaohongshu.com
Source page where the extension content script reads the post URL when the download button is clicked.
- www.rednote.com
Alternate source page domain accepted by the same content-script download function.