What you need
- A subscription to XHS Data API.
- A server-side RapidAPI key.
- A current public note URL containing the
xsec_tokencopied with that note.
export X_RAPIDAPI_KEY="your_rapidapi_key"
export XHS_NOTE_URL="complete_current_public_note_url"
Validate the URL first
/note/parse_url performs local validation and extracts URL components without fetching upstream note data.
import os
import requests
base_url = "https://xhs-data-api.p.rapidapi.com"
headers = {
"X-RapidAPI-Key": os.environ["X_RAPIDAPI_KEY"],
"X-RapidAPI-Host": "xhs-data-api.p.rapidapi.com",
}
parsed = requests.get(
f"{base_url}/note/parse_url",
headers=headers,
params={"url": os.environ["XHS_NOTE_URL"], "raw": "false"},
timeout=30,
)
parsed.raise_for_status()
print(parsed.json()["data"]["note_id"])
Retrieve normalized detail
detail = requests.get(
f"{base_url}/note/detail_from_url",
headers=headers,
params={"url": os.environ["XHS_NOTE_URL"], "raw": "false"},
timeout=45,
)
detail.raise_for_status()
data = detail.json()["data"]
note = data["note"]
print("title:", note.get("title"))
print("author:", (note.get("author") or {}).get("nickname"))
print("images:", len(note.get("images") or []))
print("request_id:", data["request_id"])
The normalized contract can include author identity, description, engagement counts, tags, timestamps, location, images, video, music, and media dimensions when the public source supplies them.
null means the selected upstream page did not provide that field. It is not zero. Media URLs may be signed or temporary, so use them promptly.
Handle failures
Keep data.request_id from every error. A retryable 503 REQUEST_TIMEOUT may include Retry-After; wait before retrying the identical request. When a token expires, obtain a current complete public URL instead of inventing or transferring a token.