JD.com Product Data API: Details, Prices and Reviews
Retrieve JD.com product details, images, current price records and customer reviews from a known product ID. Includes a Python example with review pagination and JSON export.
Start with a known JD.com product ID
Product research often combines product attributes, prices and customer feedback. Just One API exposes separate JD.com endpoints for these datasets, which you can join using the product ID supplied with each request.
This tutorial uses Product Details V4, Product Price V1 and Product Reviews V2. It saves one detail response, one price snapshot and up to two review pages. Start with a familiar product to check whether the available fields meet your needs before expanding to a catalog.
Prepare your product ID and token
Confirm the product ID on the JD.com detail page, including the specific product or variant you intend to query. Pass that ID as itemId; do not substitute a store ID, campaign identifier or complete product URL.
Register for Just One API, obtain your API token and try Product Details V4 in the interactive documentation. One token works across platform APIs, with pay-as-you-go usage. Check your account for each endpoint's permissions and free trial allowance.
All three GET endpoints require token and itemId. Check both the HTTP status and the response body's business code=0 before reading data.
Step 1: Retrieve product attributes and images
Call /api/jd/get-item-detail/v4 with the product ID. The current public response example contains data.sku, data.raw_data and data.graphext_data. The latter includes detail_images and properties.
Save the full response first, then map the available attributes and image links into your own model. Field availability can vary by product. Retain missing values rather than substituting attributes from another item. This example saves image URLs; it does not download the image files.
V4 is used here for a synchronous request workflow. The Product Details V2 documentation describes an asynchronous workflow through the dashboard's Task Management feature. Do not apply the synchronous example below to that workflow. See the API version guide for how to choose between versions.
Step 2: Preserve the raw current price
Product Price V1 uses /api/jd/get-item-price/v1. In the current public example, records appear in data.data, with good_id and price fields.
The public description does not establish the price unit for that example. The script therefore saves the complete records unchanged. It does not divide values by 100 or label raw values as yuan. Confirm the unit, associated product ID and price definition before comparing products.
The output includes the requested product ID and a UTC retrieval timestamp. This is a price snapshot. To study changes over time, schedule your own repeated collection and keep earlier snapshots; one request does not provide a historical price series. Check how discounts, membership conditions and promotions affect the specific price being returned before drawing comparisons.
Step 3: Paginate through customer reviews
Call Product Reviews V2 at /api/jd/get-item-comments/v2, starting with page=1.
The current public response example places reviews in data.items. Fields include recordId, text, rating, createdAt and variantName. data.totalPages can help determine when pagination should stop.
The script deduplicates by product ID and review recordId. It stops on an empty page, a page containing only previously seen records, or the reported final page, and caps collection at two pages. Summary counts and available review pages may use different scopes. Keep variant information and do not treat a small sample as the complete review corpus.
Python example: Export details, price records and reviews
Install the dependency and set the token and numeric product ID. Replace both placeholder values. These commands work in a macOS or Linux terminal.
python3 -m pip install requests
export JUSTONEAPI_TOKEN='your_token'
export JD_ITEM_ID='your_numeric_item_id'Save the code as jd_sample.py and run python3 jd_sample.py. It makes up to 4 API requests: one for details, one for price and at most two for reviews. There are no automatic retries. Successful responses already saved remain available if a later request fails.
import json
import os
from datetime import datetime, timezone
from pathlib import Path
import requests
BASE_URL = "https://api.justoneapi.com"
TOKEN = os.environ.get("JUSTONEAPI_TOKEN", "")
def call_api(path, **params):
try:
response = requests.get(
BASE_URL + path,
params={"token": TOKEN, **params},
timeout=120, allow_redirects=False,
)
except requests.RequestException:
raise RuntimeError("API network error or timeout") from None
if response.status_code != 200:
raise RuntimeError(f"API HTTP {response.status_code}")
try:
result = response.json()
except ValueError:
raise RuntimeError("API response is not JSON") from None
if not isinstance(result, dict) or result.get("code") != 0:
code = result.get("code") if isinstance(result, dict) else None
raise RuntimeError(f"API failed: code={code}; check the docs")
data = result.get("data")
if not isinstance(data, dict):
raise RuntimeError("Unexpected data shape; inspect the docs")
inner = data.get("base_resp")
if isinstance(inner, dict) and inner.get("status_code") not in (0, "0"):
raise RuntimeError("Platform response was unsuccessful")
return result
def save_json(folder, name, value):
(folder / name).write_text(
json.dumps(value, ensure_ascii=False, indent=2), encoding="utf-8"
)
def require_list(data, key):
value = data.get(key)
if not isinstance(value, list):
raise RuntimeError(f"Expected a list at {key}; inspect saved JSON")
return value
def output_folder(topic):
if not TOKEN or TOKEN == "your_token":
raise RuntimeError("Set JUSTONEAPI_TOKEN before running")
run_id = datetime.now(timezone.utc).strftime("%Y%m%dT%H%M%S%fZ")
folder = Path(topic + "-output") / run_id
folder.mkdir(parents=True, exist_ok=False)
return folder
ITEM_ID = os.environ.get("JD_ITEM_ID", "")
MAX_REVIEW_PAGES = 2
def main():
if not ITEM_ID or not ITEM_ID.isdigit():
raise RuntimeError("Set JD_ITEM_ID to a numeric product ID")
folder = output_folder("jd")
detail = call_api("/api/jd/get-item-detail/v4", itemId=ITEM_ID)
save_json(folder, "product-detail.json", detail)
price = call_api("/api/jd/get-item-price/v1", itemId=ITEM_ID)
save_json(folder, "product-price.json", price)
rows = require_list(price["data"], "data")
save_json(folder, "price-snapshot.json", {
"requestedItemId": ITEM_ID,
"retrievedAt": datetime.now(timezone.utc).isoformat(),
"rawPriceRecords": rows,
})
reviews, seen = [], set()
for page in range(1, MAX_REVIEW_PAGES + 1):
result = call_api(
"/api/jd/get-item-comments/v2", itemId=ITEM_ID, page=str(page)
)
save_json(folder, f"reviews-{page}.json", result)
data = result["data"]
items = require_list(data, "items")
if not items:
break
added = 0
for item in items:
if not isinstance(item, dict):
raise RuntimeError("Unexpected review item")
rid = item.get("recordId")
if rid is None:
raise RuntimeError("Missing review recordId; inspect saved JSON")
key = (ITEM_ID, str(rid))
if key in seen:
continue
seen.add(key)
added += 1
reviews.append({
"itemId": ITEM_ID, "recordId": str(rid),
"text": item.get("text"), "rating": item.get("rating"),
"createdAt": item.get("createdAt"),
"variantName": item.get("variantName"),
})
total_pages = data.get("totalPages")
if added == 0 or (
isinstance(total_pages, int) and page >= total_pages
):
break
save_json(folder, "reviews.json", reviews)
print(f"Saved product data and {len(reviews)} reviews to {folder}")
if __name__ == "__main__":
try:
main()
except RuntimeError as error:
raise SystemExit(str(error)) from NoneUse the output for product research
Each run creates a dated folder under jd-output. product-detail.json and product-price.json preserve full responses. price-snapshot.json adds retrieval time to the raw price records. reviews.json contains deduplicated reviews, with the original page responses saved separately.
When building a catalog, retain the requested ID, identifiers returned in the response, retrieval time and endpoint version. For review analysis, group by variant and date before examining repeated themes, and keep the original text for verification. A proportion calculated from sampled reviews does not establish satisfaction across all buyers.
Common questions
Can I use a price field from product details instead?
Details and price endpoints return different structures. This tutorial retrieves prices separately to make their source and retrieval time explicit. Whether a detail field is a suitable substitute depends on the actual response and the price definition your application needs.
How do I collect multiple products?
Replace the single JD_ITEM_ID with your own ID list, save separate results for each item and set a total request limit. Validate fields and price units on a small sample first. Consult the retry and concurrency guide before adding retries.
Why might a successful request contain no reviews?
An empty list in a successful response differs from a failed operation. Check the ID, page number and saved response before deciding whether pagination has ended. Do not repeatedly request the same page just to reach a target count.
Try a familiar product in the JD.com product documentation, then connect the returned fields to your research workflow.
Continue with Just One API
Log in to the Dashboard for your token, read the full API docs, or open the MCP GitHub project for the latest configuration.