Yotpo Headless Integration Guide (API-Based)
This guide is for developers showing Yotpo reviews and star ratings through the API instead of the default Yotpo widgets. It fits custom headless storefronts (for example React, Vue or Hydrogen).
Every read in this guide uses the V3 storefront API on api-cdn.yotpo.com. It's public: your store ID in the path identifies the store, and no token is needed. For the full reference, see the V3 Reviews pages, starting with Retrieve product reviews. For a deeper playbook, see Building custom reviews widgets.
⭐ Get a product's star rating
Call Retrieve product rating bottomline. It returns the average score and the review count, with no review bodies.
Endpoint:
GET https://api-cdn.yotpo.com/v3/storefront/store/{storeId}/product/{productId}/ratingscurl --request GET \
--url https://api-cdn.yotpo.com/v3/storefront/store/YOUR_STORE_ID/product/YOUR_PRODUCT_ID/ratings \
--header 'accept: application/json'Sample response:
{
"bottomline": {
"totalReviews": 757,
"averageScore": 4.8758254,
"totalOrganicReviews": 0,
"organicAverageScore": 0.0
}
}totalReviews and averageScore count every review, including syndicated ones. The organic pair counts only reviews your own store collected. Pick one pair and use it everywhere on your site.
React example:
import { useEffect, useState } from 'react';
type Bottomline = { totalReviews: number; averageScore: number };
const useYotpoStarRating = (storeId: string, productId: string) => {
const [bottomline, setBottomline] = useState<Bottomline>();
useEffect(() => {
fetch(`https://api-cdn.yotpo.com/v3/storefront/store/${storeId}/product/${productId}/ratings`)
.then((res) => res.json())
.then((data) => setBottomline(data.bottomline))
.catch(console.error);
}, [storeId, productId]);
return bottomline;
};Usage:
- Show the star rating on the product page, for example
4.9 stars (757 reviews). - For category pages, don't call this once per product card. Load ratings in bulk with Retrieve bottomline scores (see the category page recipe).
📝 Get reviews for a product
Call Retrieve product reviews. One request returns a page of reviews plus the rating summary and star distribution.
Endpoint:
GET https://api-cdn.yotpo.com/v3/storefront/store/{storeId}/product/{productId}/reviews?page=1&perPage=10&sort=smartcurl --request GET \
--url 'https://api-cdn.yotpo.com/v3/storefront/store/YOUR_STORE_ID/product/YOUR_PRODUCT_ID/reviews?page=1&perPage=10&sort=smart' \
--header 'accept: application/json'| Parameter | Values | Notes |
|---|---|---|
page | Integer, starting at 1 | |
perPage | Integer | Default 5, maximum 150 |
sort | smart, date, badge, images, rating | smart is AI-powered Smart Sorting (Pro and Premium plans) |
direction | asc, desc | |
lang | Language code | Returns translated review content |
Sample response (one review shown, products shortened):
{
"pagination": { "page": 1, "perPage": 10, "total": 757 },
"bottomline": {
"totalReview": 757,
"averageScore": 4.8758254,
"totalOrganicReviews": 0,
"organicAverageScore": 0.0,
"starDistribution": { "1": 2, "2": 3, "3": 12, "4": 53, "5": 687 },
"customFieldsBottomline": null
},
"products": [
{ "id": 94562870, "domainKey": "YOUR_PRODUCT_ID", "name": "Leather Backpack" }
],
"reviews": [
{
"id": 911166863,
"score": 5,
"votesUp": 0,
"votesDown": 0,
"content": "Fits a 15-inch laptop with room to spare.",
"title": "Great everyday bag",
"createdAt": "2026-10-24T09:32:19",
"verifiedBuyer": true,
"isIncentivized": false,
"incentiveType": null,
"customFields": null,
"imagesData": null,
"videosData": null,
"comment": {
"id": 41384957,
"content": "Thanks for your review!",
"createdAt": "2026-10-25T08:00:00",
"displayName": "Store name",
"commentsAvatar": null
},
"user": {
"userId": 155028634,
"displayName": "Dana L.",
"socialImage": null,
"userType": "User",
"isSocialConnected": 0
},
"sourceReviewId": null,
"language": "en",
"translatedLanguage": null
}
]
}Note the singular totalReview in this response. The ratings endpoint above says totalReviews.
React example:
import { useEffect, useState } from 'react';
const useYotpoReviews = (storeId: string, productId: string, perPage = 10, page = 1) => {
const [reviews, setReviews] = useState([]);
const [total, setTotal] = useState(0);
useEffect(() => {
const url = `https://api-cdn.yotpo.com/v3/storefront/store/${storeId}/product/${productId}/reviews?page=${page}&perPage=${perPage}&sort=smart`;
fetch(url)
.then((res) => res.json())
.then((data) => {
setReviews(data.reviews);
setTotal(data.pagination.total);
})
.catch(console.error);
}, [storeId, productId, perPage, page]);
return { reviews, total };
};Usage:
- Show a list of reviews per product, and page through them with
pageandperPage. - Use
bottomline.starDistributionfor a rating histogram. - To add search and star or photo filters, send a
POSTto the same path (Search product reviews).
🌍 Get site reviews
Site reviews are reviews about your store rather than a product. Yotpo keeps them under the reserved product ID yotpo_site_reviews, so you use the same two endpoints as for a product.
Site star rating and count:
GET https://api-cdn.yotpo.com/v3/storefront/store/{storeId}/product/yotpo_site_reviews/ratings{
"bottomline": {
"totalReviews": 226,
"averageScore": 4.9,
"totalOrganicReviews": 0,
"organicAverageScore": 0.0
}
}Site reviews list:
GET https://api-cdn.yotpo.com/v3/storefront/store/{storeId}/product/yotpo_site_reviews/reviews?page=1&perPage=10The response has the same shape as product reviews, so the useYotpoStarRating and useYotpoReviews hooks above work with productId set to yotpo_site_reviews.
Don't use Retrieve store reviews for this. It returns every review in your store across all products, not just site reviews.
✍️ Collect reviews
To submit reviews from your own form, call Create review (POST https://api.yotpo.com/v1/widget/reviews) from your server, not from the browser. Review submission is still a v1 endpoint. Your server is where you validate input, block bots, and set the product title and URL from your own catalog. Use sku: "yotpo_site_reviews" to create a site review. New reviews go through your moderation settings before they appear.
🛠 Developer notes
- Storefront endpoints are served from a CDN (
api-cdn.yotpo.com) and need no token. Data can lag by up to 3 hours, and responses are sent withCache-Control: no-store, so cache them on your side. user.displayNameis an empty string for anonymous reviewers. Show a fallback such as "Anonymous".imagesDataandvideosDatahold the shopper's photos and videos, ornull.verifiedBuyermarks reviews from confirmed customers.isIncentivizedmarks reviews written for an incentive; show a disclosure when it'strue.commentis the store's public reply, ornull.- Consider lazy-loading further pages for better UX.
- Already using the v1 storefront endpoints (
/v1/widget/...and/products/.../bottomline)? They keep working. V3 uses camelCase fields and adds Smart Sorting, translated content and search filters.
If you're using any of the following frameworks, call these APIs from your server for the first page load, so the rating and reviews are in the HTML that search engines see, and cache the responses:
- Next.js
- Shopify Hydrogen
- Vue Storefront
- Gatsby
Updated 5 days ago
