Reviews API
Pull and Monitor Reviews From 100+ Platforms
Collect and monitor reviews from Google, Yelp, Tripadvisor and 100+ platforms in one standardized format, with every change pushed to you.
Reviews API
Every review of a listing, in one standardized format#
Send a platform and a listing; Zembra collects all of its reviews and returns them in one consistent format, whatever the platform. Pull them once, or keep monitoring the listing and get every change, new reviews, edits, replies, deletions, pushed to you.
At a glance
- One
POSTto start, results in seconds - Listing details returned instantly with every response, at no extra cost
- Four monitoring levels, from a one-off pull to full tracking
- Webhooks to your server, Slack or email
- Reading results with
GETis free
Why teams choose Zembra for reviews#
Performance
First results in seconds, listing details returned instantly, and complete results served from ready-made caches: every review of a listing in a single GET.
Completeness
131 platforms, every review including hidden ones, the full history of edits, replies, deletions and restorations.
Quality
One schema for every platform: ratings normalized to 1–5, UTC timestamps, stable review IDs, and one listing behind every variant of its URL.
Rich data
Author, photos, replies, language, translations, rating breakdown, and every extra field the platform shows in rawData.
No noise
Webhooks only fire when something changed, with the level of detail you choose: counts, changes only, or everything.
Fair pricing
Pay per use with credits that get cheaper as you grow. No subscription, no commitment, no minimum.
How it works
From one request to a continuous stream of updates#
You create a job for a listing. Zembra answers right away with the listing's details, collects the reviews, then delivers them, either pushed to your webhook or read whenever you ask.
- Create a job with
POST /reviews: the platform, the listing's slug or URL, and a monitoring level. - Get an instant answer: the job and the listing's details, so you know right away the listing exists, with no polling.
- Zembra pulls every review of the listing, usually within seconds.
- Your webhook receives the reviews, then, with monitoring, only what changes after each scheduled check.
- Create a job with
POST /reviews. - Get an instant answer with the job and the listing's details.
- Read the reviews with
GET /reviews. While the first pull is running you get206 Partial Contentwith what's collected so far; once it's done,200with every review. - Read again whenever you like: GET calls are free and can filter by date, rating, replies and more.
The life of a job#
Jobs last 30 days #
A monitoring job runs for 30 days. With autoRenew, a new 30-day job starts where the old one ends; it is charged about a day before expiry, and if your balance is too low the renewal fails and you are notified.
One active monitoring job per listing #
Creating a monitoring job for a listing you already monitor returns the existing job with 409 Conflict, free of charge. Slug variants count as the same listing.
Switching monitoring levels #
The level of a job can't be changed. Delete the job and create a new one at the new level; the new job is charged as a new job.
Stopping: delete or turn off auto-renew #
To stop at the end of the period, set autoRenew to false with PATCH /reviews: everything keeps working until the job expires. DELETE /reviews stops monitoring and webhooks immediately, the job can no longer be read (410 Gone), and nothing is refunded.
Monitoring levels
One-off pull or continuous monitoring#
Every level starts by pulling all the reviews at once. What differs is what happens next.
none |
basic |
regular |
full |
|
|---|---|---|---|---|
| What it is | A one-off job, no monitoring | Monthly updates | Daily updates | Complete, continuous tracking |
| First pull | All reviews, or up to your sizeLimit / dateLimit | All reviews, at once, when the job is created | ||
| Checks for new reviews | — | Every 30 days | Every day | Near real time: at most every 8 hours, plus a complete re-check every week |
| Keeps older reviews up to date (edits, replies, deletions) | — | Recent reviews only | Recent reviews only | All reviews |
| Keeps listing details up to date | — | No (returned on the first request) | No (returned on the first request) | Yes: review count and rating at most every 8 hours, with the reviews; all other details every week |
| Limit by size or date | Yes, the only level that allows it | No: monitoring always covers every review | ||
| Auto-renew every 30 days | — | Yes, with autoRenew | ||
| Data available for | 5 days | The 30-day life of the job | ||
| Price | 50 credits + 1 per review | 75 credits + 3 per review renewal 50 + 3 per new review |
125 credits + 3 per review renewal 75 + 3 per new review |
150 credits + 1 per review renewal 100 + 1 per review |
Basic and Regular close the gap between the last review they pulled and the newest ones. They don't promise to keep older reviews or the listing's details up to date. Full is the only level that keeps every review and the listing current.
Which level for which use#
One-off audits and research
Competitor benchmarks, market studies, AI training data, importing a new client's history. Set a size or date limit to pay only for what you need.
Light, periodic tracking
Monthly reports and sales prospecting, where a few weeks of delay is fine.
Daily dashboards on a budget
Reputation dashboards and alerts that need new reviews every day, without tracking every edit.
Live reputation management
Reply workflows, review inboxes and analytics that must reflect every edit, reply and deletion, plus accurate listing details.
Pricing
Pay per use, no commitment#
No subscription, no minimum use, no contract. You pay credits for the reviews you collect, and reading them with GET is free. Credits get cheaper as your volume grows: how credit pricing works.
How a job is priced#
none50 + 1 × reviews pulledbasic75 + 3 × reviews, then 3 × each new reviewregular125 + 3 × reviews, then 3 × each new reviewfull150 + 1 × reviews, then 1 × each new review
Renewals. With autoRenew, each new 30-day period costs a renewal fee:
Basic 50 and Regular 75 credits plus 3 per new review;
Full 100 credits plus 1 per review on the listing, since Full keeps every review up to date.
Charges follow the work: the base fee when the job starts, review fees as reviews are collected. Optional permalink (permanent copies of photos) adds 25 credits. A 409 for a listing you already monitor costs nothing.
What your credits get you#
For a typical listing with 300 reviews: a one-off pull costs 350 credits, the first month of Regular monitoring 1,025, the first month of Full monitoring 450.
| Credits | Price | One-off pulls | Regular monitoring, first month | Full monitoring, first month |
|---|---|---|---|---|
| 10,000 | Free on sign-up | ≈ 8,400 reviews (28 listings) | ≈ 9 listings | ≈ 22 listings |
| 100,000 | $92 | ≈ 85,500 reviews (285 listings) | ≈ 97 listings | ≈ 222 listings |
| 1,000,000 | $762 | ≈ 857,100 reviews (2,857 listings) | ≈ 975 listings | ≈ 2,222 listings |
| 10,000,000 | $4,862 | ≈ 8,571,300 reviews (28,571 listings) | ≈ 9,756 listings | ≈ 22,222 listings |
API reference
Requests and parameters#
Base URL https://api.zembra.io, authenticated with Authorization: Bearer <token>. A sandbox at https://sandbox.zembra.io returns realistic dummy data without using credits.
curl --location 'https://api.zembra.io/reviews' \
--header 'Authorization: Bearer {token}' \
--header 'Content-Type: application/json' \
--data '{
"network": "google",
"slug": "my-listing-slug",
"monitoring": "full",
"autoRenew": true,
"internalId": "location-1042",
"includeRawData": false
}'
var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Post, "https://api.zembra.io/reviews");
request.Headers.Add("Authorization", "Bearer {token}");
var content = new StringContent("{
\"network\": \"google\",
\"slug\": \"my-listing-slug\",
\"monitoring\": \"full\",
\"autoRenew\": true,
\"internalId\": \"location-1042\",
\"includeRawData\": false
}", null, "application/json");
request.Content = content;
var response = await client.SendAsync(request);
response.EnsureSuccessStatusCode();
Console.WriteLine(await response.Content.ReadAsStringAsync());
package main
import (
"fmt"
"strings"
"net/http"
"io/ioutil"
)
func main() {
url := "https://api.zembra.io/reviews"
method := "POST"
payload := strings.NewReader(`{
"network": "google",
"slug": "my-listing-slug",
"monitoring": "full",
"autoRenew": true,
"internalId": "location-1042",
"includeRawData": false
}`)
client := &http.Client {
}
req, err := http.NewRequest(method, url, payload)
if err != nil {
fmt.Println(err)
return
}
req.Header.Add("Authorization", "Bearer {token}")
req.Header.Add("Content-Type", "application/json")
res, err := client.Do(req)
if err != nil {
fmt.Println(err)
return
}
defer res.Body.Close()
body, err := ioutil.ReadAll(res.Body)
if err != nil {
fmt.Println(err)
return
}
fmt.Println(string(body))
}
OkHttpClient client = new OkHttpClient().newBuilder()
.build();
MediaType mediaType = MediaType.parse("application/json");
RequestBody body = RequestBody.create(mediaType, "{
\"network\": \"google\",
\"slug\": \"my-listing-slug\",
\"monitoring\": \"full\",
\"autoRenew\": true,
\"internalId\": \"location-1042\",
\"includeRawData\": false
}");
Request request = new Request.Builder()
.url("https://api.zembra.io/reviews")
.method("POST", body)
.addHeader("Authorization", "Bearer {token}")
.addHeader("Content-Type", "application/json")
.build();
Response response = client.newCall(request).execute();
var myHeaders = new Headers();
myHeaders.append("Authorization", "Bearer {token}");
myHeaders.append("Content-Type", "application/json");
var raw = JSON.stringify({
"network": "google",
"slug": "my-listing-slug",
"monitoring": "full",
"autoRenew": true,
"internalId": "location-1042",
"includeRawData": false
});
var requestOptions = {
method: 'POST',
headers: myHeaders,
body: raw,
redirect: 'follow'
};
fetch("https://api.zembra.io/reviews", requestOptions)
.then(response => response.text())
.then(result => console.log(result))
.catch(error => console.log('error', error));
<?php
$client = new Client();
$headers = [
'Authorization' => 'Bearer {token}',
'Content-Type' => 'application/json'
];
$body = '{
"network": "google",
"slug": "my-listing-slug",
"monitoring": "full",
"autoRenew": true,
"internalId": "location-1042",
"includeRawData": false
}';
$request = new Request('POST', 'https://api.zembra.io/reviews', $headers, $body);
$res = $client->sendAsync($request)->wait();
echo $res->getBody();
import requests
import json
url = "https://api.zembra.io/reviews"
payload = json.dumps({
"network": "google",
"slug": "my-listing-slug",
"monitoring": "full",
"autoRenew": True,
"internalId": "location-1042",
"includeRawData": False
})
headers = {
'Authorization': 'Bearer {token}',
'Content-Type': 'application/json'
}
response = requests.request("POST", url, headers=headers, data=payload)
print(response.text)
require "uri"
require "json"
require "net/http"
url = URI("https://api.zembra.io/reviews")
https = Net::HTTP.new(url.host, url.port)
https.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = "Bearer {token}"
request["Content-Type"] = "application/json"
request.body = JSON.dump({
"network": "google",
"slug": "my-listing-slug",
"monitoring": "full",
"autoRenew": true,
"internalId": "location-1042",
"includeRawData": false
})
response = https.request(request)
puts response.read_body
Response
{
"message": "Reviews fetching order received for Google: my-listing. Please allow a few minutes before requesting reviews.",
"data": {
"job": {
"network": "google",
"slug": "my-listing-slug",
"jobId": "123e4567-e89b-12d3-a456-426614174000",
"uid": "OTBmNTBlYzViMjM2NTU4MGVmNTUxMmQwMmY0YzZlNmU",
"monitoring": "full",
"internalId": "",
"requestedAt": "2023-01-01T00:00:00Z",
"expiresAt": "2023-01-01T00:00:00Z",
"includeRawData": false,
"cost": 165,
"active": true,
"sizeLimit": 100,
"dateLimit": 1734140315,
"autoRenew": true,
"renewed": false,
"url": ""
},
"target": {
"address": {
"street": "5621 Main St",
"city": "Mallorca",
"region": "QC",
"postalCode": "78128",
"country": "US"
},
"aliases": [
"my-listing-1",
"my-first-listing"
],
"categories": [
"Restaurant",
"Fast Food"
],
"formattedAddress": "5621 Main St, Mallorca, QC 78128, US",
"globalRating": 4.5,
"id": "ChIJV9OJxsOq3oARnyGzSXY0ym8",
"lastCrawled": "2023-01-01T00:00:00Z",
"lastUpdated": "2023-01-01T00:00:00Z",
"link": "https://my.zembratech.com/dashboard/review-page/insights/MWJmZTc5MDZjMzg5OGI3OGMyNjJiODNjOGJjMWI3Y2Q",
"name": "My Google Business",
"network": "google",
"phone": "(210) 456-0987",
"photos": [
"https://media2.trover.com/T/5a5eae0184b8636d6b0385f0/fixedw-1.jpg",
"https://media2.trover.com/T/5a5eae0184b8636d6b0385f0/fixedw-2.jpg"
],
"priceRange": "$$$",
"profileImage": "https://media2.trover.com/T/5a5eae0184b8636d6b0385f0/fixedw.jpg",
"ratingBreakdown": {
"overall": 8.4,
"perCriteria": [
{
"criteria": "Overall",
"rating": 8.4
},
{
"criteria": "Cleanliness",
"rating": 8.9
},
{
"criteria": "Facilities",
"rating": 8.3
},
{
"criteria": "Location",
"rating": 8.7
},
{
"criteria": "Service",
"rating": 8.7
},
{
"criteria": "Value for money",
"rating": 8.2
}
],
"ratingDistribution": [
{
"rating": "9+ Exceptional",
"count": 135
},
{
"rating": "8-9 Excellent",
"count": 66
},
{
"rating": "7-8 Very Good",
"count": 32
},
{
"rating": "6-7 Good",
"count": 24
},
{
"rating": "<6 Below Expectation",
"count": 11
}
]
},
"rawData": [],
"reviewCount": {
"native": {
"total": 6320,
"active": 5986,
"hidden": 334
},
"available": {
"total": 6320,
"active": 5986,
"hidden": 334
}
},
"slug": "my-listing-slug",
"url": "https://maps.google.com/?cid=16256511492469774330",
"website": "https://my-business.com"
}
}
}
{
"message": "Invalid slug [not-a-real-listing-slug]"
}
{
"message": "Invalid bearer token"
}
{
"message": "Cannot process request: balance too low!"
}
{
"message": "Requested network [Facebook] is not enabled for your account"
}
{
"message": "Listing not found. Maybe not a valid listing."
}
POST /reviews: create a job#
network | string, required The platform, e.g. google, yelp. See platforms. |
|---|---|
slug | string, required The listing: its identifier, its URL, or part of it. |
monitoring | none · basic · regular · fullDefault none. See monitoring levels. |
sizeLimit | integer ≥ 25, none onlyThe most recent reviews to collect, and to pay for. |
dateLimit | Unix seconds, none onlyOnly collect reviews posted after this date. |
autoRenew | boolean, monitoring only Renew the job every 30 days. Default: your account setting. |
internalId | string, 3–255 Your own reference. See internalId. |
includeRawData | boolean Include rawData in webhook deliveries. |
skipListeners | boolean Don't send this monitoring job's changes to your webhooks. |
permalink | boolean Host permanent copies of the photos (+25 credits). |
GET /reviews: read the reviews (free)#
jobId / uid / network + slug | identify the job A job ID, the listing's uid, or the platform and slug. |
|---|---|
limit, offset | integer, limit 0–1000 Paginate the response. Without filters, every review comes back in one response. |
sortBy, sortDirection | timestamp, rating, … · ASC/DESCDefault: newest first. |
postedAfter, postedBefore | Unix seconds Reviews posted in a date range. |
minRating, maxRating | number Filter on the normalized 1–5 rating. |
hasReplies, hasEdits, hasPhotos, hasTranslation, isHidden, isRecommended | boolean Filter on review properties. |
includeDeleted, isDeleted | boolean Include or select deleted reviews. |
fields[], language | array · 2–3 letters Pick fields (e.g. rawData) or a language. |
One-off (none) jobs only support limit and offset.
sizeLimit and dateLimit on POST decide how many reviews are collected and charged, and only for one-off jobs. limit on GET only paginates what you read back: it never changes what is collected or what you pay.
#
Other calls#
GET /reviews/jobs | List your jobs; filter by internalId, slug, platform or level. Free. |
|---|---|
PATCH /reviews | Change internalId, autoRenew, includeRawData or skipListeners. Free. |
PUT /reviews | Force an extra check now: partial (15 credits) or full (50 credits). |
DELETE /reviews | Delete a job. See stopping a job. |
Status codes#
200 | Job created, or reviews returned |
|---|---|
206 | First pull still running: partial results |
400 | Invalid parameter |
402 | Balance too low |
403 | Platform not enabled on your account |
404 | The listing doesn't exist on the platform |
406 | Not a reviewable business |
409 | Listing already monitored: existing job returned, free |
410 | Job expired or deleted |
429 | Rate limit reached |
503 | Platform temporarily unavailable |
Data
What you get back#
Every response has the same layout: the job, the listing (target), the reviews, and how many were returned.
A review#
id, url | The review's ID on the platform and its link. |
|---|---|
text, language, translation | The text, its language, and the platform's translation when it offers one. |
rating, recommendation | Rating on a 1–5 scale; recommendation (1, 0, -1) on platforms that use it. |
timestamp, editedAt | When it was posted and last edited, in UTC. |
author, photos | Name, photo, profile link and location of the author; review photos. |
replies | The business's and others' replies. |
edits | Previous versions of the review. |
isHidden, isDeleted | See hidden, edited and deleted reviews. |
rawData | See rawData. |
Example GET /reviews response
{
"status": "SUCCESS",
"message": "Reviews for Google: my-listing-slug",
"data": {
"job": {
"network": "google",
"slug": "my-listing-slug",
"jobId": "0d6c7d22-1c7e-4bde-9a3c-5b1c3b2f1e10",
"uid": "MWJmZTc5MDZjMzg5OGI3OGMyNjJiODNjOGJjMWI3Y2Q",
"monitoring": "full",
"skipListeners": false,
"internalId": "location-1042",
"requestedAt": "2026-01-01 08:00:00",
"expiresAt": "2026-01-31 08:00:00",
"includeRawData": false,
"permalink": false,
"cost": 470,
"active": true,
"autoRenew": true,
"renewed": false
},
"target": {
"address": {
"street": "5621 Main St",
"city": "San Diego",
"region": "CA",
"postalCode": "92101",
"country": "US"
},
"aliases": [
"old-listing-slug"
],
"categories": [
"Restaurant"
],
"formattedAddress": "5621 Main St, San Diego, CA 92101, US",
"globalRating": 4.4,
"id": "ChIJV9OJxsOq3oARnyGzSXY0ym8",
"lastCrawled": "2026-01-01 08:00:00",
"lastUpdated": "2026-01-01 08:00:00",
"name": "My Restaurant",
"network": "google",
"phone": "(619) 555-0134",
"priceRange": "$$",
"reviewCount": {
"native": {
"total": 320,
"active": 312,
"hidden": 8
},
"available": {
"total": 320,
"active": 312,
"hidden": 8
}
},
"slug": "my-listing-slug",
"url": "https://maps.google.com/?cid=16256511492469774330",
"website": "https://my-restaurant.com",
"rawData": {
"…": "everything else the platform shows"
}
},
"reviews": [
{
"id": "ChdDSUhNMG9nS0VJQ0FnSUR4",
"url": "https://www.google.com/maps/reviews/...",
"text": "Great food and friendly staff. Will come back!",
"language": "en",
"timestamp": "2026-01-02 19:41:00",
"editedAt": "2026-01-05 10:12:00",
"rating": 5,
"recommendation": null,
"isHidden": false,
"isDeleted": false,
"author": {
"id": "1084...",
"name": "Jane D.",
"photo": "https://...",
"url": "https://..."
},
"photos": [
"https://..."
],
"edits": [
{
"id": "ChdDSUhNMG9nS0VJQ0FnSUR4",
"text": "Good food.",
"timestamp": "2026-01-02 19:41:00",
"rating": 4
}
],
"replies": [
{
"id": "r-1",
"text": "Thank you Jane, see you soon!",
"timestamp": "2026-01-03 09:00:00"
}
]
}
],
"returned": 1
}
}
One rating scale: 1 to 5#
Platforms rate on different scales. Zembra maps every rating linearly onto 1–5, rounded to one decimal: on a 1–10 scale, 10 becomes 5, 8 becomes 4.1, 5 becomes 2.8 and 1 stays 1. The original value is still in rawData.
Hidden, edited, deleted#
- Hidden reviews are on the platform but not shown by default, like Yelp's "not recommended" reviews. They're returned with
isHidden: true. - Edited reviews keep their ID; earlier versions are listed in
editsandeditedAttells you when. - Deleted reviews are never erased: they're flagged
isDeleted: trueand left out unless you ask withincludeDeleted. If one reappears, it's restored.
rawData#
The standard fields cover what every platform has in common. rawData carries everything else a platform shows, exactly as it shows it: extra ratings, badges, platform-specific fields. Ask for it with fields[]=rawData on GET, or includeRawData for webhooks. It's free, but it can make payloads large.
Listing details, included #
Every response includes the listing (target): name, address, phone, website, categories, rating, review counts and more, at no extra cost. On POST it comes back immediately, so you know at once whether the listing exists (404) or can be reviewed (406), without waiting for a job.
Slugs can change #
Platforms sometimes change a listing's identifier or URL. Zembra keeps the old ones as aliases and resolves every variant to the same listing and the same job. The listing's uid never changes.
Use internalId #
Set internalId to your own reference, like your location ID. It's returned in every response and every webhook, carried over on renewals, and searchable with GET /reviews/jobs, so you can match data to your records without extra lookups.
Webhooks
Get changes pushed to you#
Set up your destinations once in your dashboard's API settings: your server, Slack, or email. Every monitoring job then delivers there, unless you set skipListeners.
Settings#
| Events | New reviews, edited reviews, new replies, deleted reviews, restored reviews |
|---|---|
| Payload | summary (counts), changes (only what changed) or all (every review) |
| Frequency | Right away, or as a daily digest at your chosen time (UTC) |
| Delivery | JSON POST with the X-Zembra-Token header, 100 reviews per call |
| Retries | 5 attempts over about 1.5 hours; after that the webhook is paused and you're notified |
One-off jobs deliver their reviews once, when the pull is done. Use reference and page to detect a delivery you already processed.
Payloads#
summary · Counts only: how many new, edited, replied, deleted and restored reviews. Light, ideal to trigger your own GET.
{
"reference": "761154dc-...",
"type": "reviews",
"internalId": "location-1042",
"format": "summary",
"data": {
"summary": {
"countNewReviews": 3,
"countEditedReviews": 1,
"countNewReplies": 2,
"countDeletedReviews": 0,
"countRestoredReviews": 0
},
"job": {
"network": "google",
"slug": "my-listing-slug",
"jobId": "0d6c7d22-1c7e-4bde-9a3c-5b1c3b2f1e10",
"uid": "MWJmZTc5MDZjMzg5OGI3OGMyNjJiODNjOGJjMWI3Y2Q",
"monitoring": "full",
"skipListeners": false,
"internalId": "location-1042",
"requestedAt": "2026-01-01 08:00:00",
"expiresAt": "2026-01-31 08:00:00",
"includeRawData": false,
"permalink": false,
"cost": 470,
"active": true,
"autoRenew": true,
"renewed": false
}
}
}
changes · Only the reviews that changed, grouped by event, with the listing details.
{
"reference": "761154dc-...",
"type": "reviews",
"internalId": "location-1042",
"format": "changes",
"data": {
"job": {
"network": "google",
"slug": "my-listing-slug",
"jobId": "0d6c7d22-1c7e-4bde-9a3c-5b1c3b2f1e10",
"uid": "MWJmZTc5MDZjMzg5OGI3OGMyNjJiODNjOGJjMWI3Y2Q",
"monitoring": "full",
"skipListeners": false,
"internalId": "location-1042",
"requestedAt": "2026-01-01 08:00:00",
"expiresAt": "2026-01-31 08:00:00",
"includeRawData": false,
"permalink": false,
"cost": 470,
"active": true,
"autoRenew": true,
"renewed": false
},
"target": {
"name": "My Restaurant",
"…": "listing info"
},
"updates": {
"newReviews": [
{
"id": "ChdDSUhNMG9nS0VJQ0FnSUR4",
"url": "https://www.google.com/maps/reviews/...",
"text": "Great food and friendly staff. Will come back!",
"language": "en",
"timestamp": "2026-01-02 19:41:00",
"editedAt": "2026-01-05 10:12:00",
"rating": 5,
"recommendation": null,
"isHidden": false,
"isDeleted": false,
"author": {
"id": "1084...",
"name": "Jane D.",
"photo": "https://...",
"url": "https://..."
},
"photos": [
"https://..."
],
"edits": [
{
"id": "ChdDSUhNMG9nS0VJQ0FnSUR4",
"text": "Good food.",
"timestamp": "2026-01-02 19:41:00",
"rating": 4
}
],
"replies": [
{
"id": "r-1",
"text": "Thank you Jane, see you soon!",
"timestamp": "2026-01-03 09:00:00"
}
]
}
],
"editedReviews": [],
"newReplies": [],
"deletedReviews": [],
"restoredReviews": []
}
}
}
all · Every review of the listing after each check, split into pages of 100.
{
"reference": "761154dc-...",
"type": "reviews",
"internalId": "location-1042",
"format": "all",
"pages": 4,
"page": 1,
"data": {
"job": {
"network": "google",
"slug": "my-listing-slug",
"jobId": "0d6c7d22-1c7e-4bde-9a3c-5b1c3b2f1e10",
"uid": "MWJmZTc5MDZjMzg5OGI3OGMyNjJiODNjOGJjMWI3Y2Q",
"monitoring": "full",
"skipListeners": false,
"internalId": "location-1042",
"requestedAt": "2026-01-01 08:00:00",
"expiresAt": "2026-01-31 08:00:00",
"includeRawData": false,
"permalink": false,
"cost": 470,
"active": true,
"autoRenew": true,
"renewed": false
},
"target": {
"name": "My Restaurant",
"…": "listing info"
},
"reviews": [
{
"id": "ChdDSUhNMG9nS0VJQ0FnSUR4",
"url": "https://www.google.com/maps/reviews/...",
"text": "Great food and friendly staff. Will come back!",
"language": "en",
"timestamp": "2026-01-02 19:41:00",
"editedAt": "2026-01-05 10:12:00",
"rating": 5,
"recommendation": null,
"isHidden": false,
"isDeleted": false,
"author": {
"id": "1084...",
"name": "Jane D.",
"photo": "https://...",
"url": "https://..."
},
"photos": [
"https://..."
],
"edits": [
{
"id": "ChdDSUhNMG9nS0VJQ0FnSUR4",
"text": "Good food.",
"timestamp": "2026-01-02 19:41:00",
"rating": 4
}
],
"replies": [
{
"id": "r-1",
"text": "Thank you Jane, see you soon!",
"timestamp": "2026-01-03 09:00:00"
}
]
},
"… up to 100 reviews per call"
],
"returned": 100
}
}
Performance
Fast, with no priority tiers#
What to expect #
The job and the listing come back instantly. Reviews usually follow within seconds; very large listings, with thousands of reviews, can take longer. Meanwhile, GET answers 206 with what's collected so far.
One pipeline for everyone #
There are no paid priority tiers and no queue per platform: every job, on every platform and level, runs through the same pipeline. The first pull of each new job goes ahead of routine checks, so new jobs start fast.
What can cause delays #
A platform slowing down or blocking automated access, a platform outage (see status.zembra.io; POST answers 503 meanwhile), very large listings, or platforms that limit how many reviews they show. A failed check is retried within an hour.
Rate limits #
100 job requests and 1,000 reads per minute, per account. Batches of up to 1,000 requests are available for large imports.
Supported platforms
131 platforms, one format#
-
Google
-
Yelp
-
Facebook
-
Tripadvisor
-
Trustpilot
-
Booking
-
Expedia
-
Airbnb
-
Glassdoor
-
Indeed
-
G2
-
Capterra
-
App Store
-
Google Play
-
Open Table
-
Healthgrades
-
Zillow
-
Better Business Bureau
-
A Place for Mom
-
Abritel
-
Agoda
-
AliExpress
-
AlternativeTo
-
Angi
-
Apartment Guide
-
ApartmentRatings
-
Apartments.com
-
Autotrader
-
Avvo
-
Best Buy
-
Bilbayt
-
Bol
-
Bookabach
- Car Dealer Reviews
-
Care.com
-
Carfax
-
CarGurus
-
Caring
-
Cars
-
Carvana
-
City Search
-
Civitatis
-
ClassPass
-
Clutch
-
Community Health Network
-
ConsumerAffairs
-
CreditKarma
-
Customer Lobby
-
DealerRater
-
Deliveroo
-
DesignMyNight
-
Doctor
-
DoorDash
-
eBay
-
Edmunds
-
Etsy
-
FertilityIQ
-
FeWo-Direkt
-
FindLaw
-
Flipkart
-
Foursquare
-
Gartner
-
GetYourGuide
-
Goibibo
-
Goodreads
-
Google Local Services Ads
-
GreatSchools
-
Grubhub
-
HolidayCheck
-
HomeAdvisor
-
HomeStars
-
Hotels
-
Houzz
-
Influenster
-
InsiderPages
-
Just Eat
-
JustDial
-
Kayak
-
Kelley Blue Book
-
Lawyers
-
LendingTree
-
Lieferando
-
MakeMyTrip
-
Martindale
-
Mindbody
-
Motability
-
Nextdoor
-
Niche
-
Orbitz
-
PagesJaunes
- PeerSpot
-
Postmates
-
Practo
-
Priceline
-
Product Hunt
-
Product Review
-
RateMDs
-
RealSelf
-
Rent
-
ResellerRatings
-
Reviews.io
-
Sitejabber
-
Smyths toys
-
Software Advice
-
Solv
-
Stayz
-
Steam
-
SuperMoney
-
Takeaway
-
Talabat
-
Target
-
The Knot
-
TheFork
-
Thuisbezorgd
-
Thumbtack
-
Travelocity
-
Treatwell
-
Trip
-
Trusted Shops
-
TrustRadius
-
UberEats
-
Vitals
-
Vrbo
-
Walmart
-
WebMD
-
WeddingWire
-
Yell
-
Yellow Pages
-
ZocDoc
-
Zomato
-
Zoover
- A Place for Mom
- Abritel
- Agoda
- Airbnb
- AliExpress
- AlternativeTo
- Angi
- Apartment Guide
- ApartmentRatings
- Apartments.com
- App Store
- Autotrader
- Avvo
- Best Buy
- Better Business Bureau
- Bilbayt
- Bol
- Bookabach
- Booking
- Capterra
- Car Dealer Reviews
- Care.com
- Carfax
- CarGurus
- Caring
- Cars
- Carvana
- City Search
- Civitatis
- ClassPass
- Clutch
- Community Health Network
- ConsumerAffairs
- CreditKarma
- Customer Lobby
- DealerRater
- Deliveroo
- DesignMyNight
- Doctor
- DoorDash
- eBay
- Edmunds
- Etsy
- Expedia
- FertilityIQ
- FeWo-Direkt
- FindLaw
- Flipkart
- Foursquare
- G2
- Gartner
- GetYourGuide
- Glassdoor
- Goibibo
- Goodreads
- Google Local Services Ads
- Google Play
- GreatSchools
- Grubhub
- Healthgrades
- HolidayCheck
- HomeAdvisor
- HomeStars
- Hotels
- Houzz
- Indeed
- Influenster
- InsiderPages
- Just Eat
- JustDial
- Kayak
- Kelley Blue Book
- Lawyers
- LendingTree
- Lieferando
- MakeMyTrip
- Martindale
- Mindbody
- Motability
- Nextdoor
- Niche
- Open Table
- Orbitz
- PagesJaunes
- PeerSpot
- Postmates
- Practo
- Priceline
- Product Hunt
- Product Review
- RateMDs
- RealSelf
- Rent
- ResellerRatings
- Reviews.io
- Sitejabber
- Smyths toys
- Software Advice
- Solv
- Stayz
- Steam
- SuperMoney
- Takeaway
- Talabat
- Target
- The Knot
- TheFork
- Thuisbezorgd
- Thumbtack
- Travelocity
- Treatwell
- Trip
- Tripadvisor
- Trusted Shops
- Trustpilot
- TrustRadius
- UberEats
- Vitals
- Vrbo
- Walmart
- WebMD
- WeddingWire
- Yell
- Yellow Pages
- Yelp
- Zillow
- ZocDoc
- Zomato
- Zoover
FAQ
Common questions#
Do I need the business's login?
No. The Reviews API reads public reviews; no credentials or platform approval are needed. #
Do GET requests cost credits?
No. Reading jobs and reviews is free; you only pay to collect reviews. #
Is there a minimum or a commitment?
No. Pay per use, with no subscription, no minimum and no contract. New accounts get 10,000 free credits. #
Can I change a job's monitoring level?
Not on an existing job: delete it and create a new one at the level you want. #
How do I stop monitoring a listing?
Turn off autoRenew to stop at the end of the 30 days, or delete the job to stop immediately (no refund). #
Can I test without spending credits?
Yes. The sandbox at sandbox.zembra.io returns realistic dummy data and never touches your balance. #
Start today for free
Create an account and get 10,000 free credits: enough to pull about 8,400 reviews. No commitment, no minimum.