# Google reviews

List Google Business Profile reviews, reply and remove replies, read reviews across many locations, and get an event when a review arrives or changes.

Reviews are the most visible part of a Google listing, and answering them quickly helps both ratings and local ranking. Pozy gives you two ways to work with them: the shared [reviews inbox](https://pozy.ai/docs/inbox/reviews), which mixes Google and Facebook reviews from every account, and Google-specific endpoints with extras such as the average rating and multi-location batches. Both write to the same reviews on Google.

Google only returns reviews for verified locations matched to a Maps place. If a listing shows reviews on Google but Pozy returns none, check [verification](https://pozy.ai/docs/google-business/verifications).

## Find reviews that need an answer

The reviews inbox is usually the easiest start. [`GET /v1/inbox/reviews`](https://pozy.ai/docs/api/listInboxReviews) filters by `platform`, `accountId`, `profileId`, rating range and `hasReply`, sorts by `date` or `rating`, and pages with `cursor` (up to 50 per page).

```bash cURL
curl "https://api.pozy.ai/v1/inbox/reviews?platform=googlebusiness&accountId=6713b0e5d1f4c69a2b8e3f75&hasReply=false&maxRating=3" \
  -H "Authorization: Bearer $POZY_API_KEY"
```
```ts Node
const res = await fetch("https://api.pozy.ai/v1/inbox/reviews?platform=googlebusiness&accountId=6713b0e5d1f4c69a2b8e3f75&hasReply=false&maxRating=3", {
  headers: { Authorization: `Bearer ${process.env.POZY_API_KEY}` },
});
const data = await res.json();
if (!res.ok) throw new Error(data.error);

for (const r of data.data) console.log(r.rating, r.reviewer.name, r.text);
```
```python Python
import os
import requests

H = {"Authorization": f"Bearer {os.environ['POZY_API_KEY']}"}
resp = requests.get(
    "https://api.pozy.ai/v1/inbox/reviews",
    headers=H,
    params={"platform": "googlebusiness", "accountId": "6713b0e5d1f4c69a2b8e3f75",
            "hasReply": "false", "maxRating": 3},
    timeout=30,
)
for r in resp.json()["data"]:
    print(r["rating"], r["reviewer"]["name"], r["text"])
```

```json
{
  "id": "accounts/118273645509/locations/4821930576104832190/reviews/AbFvOqn3k2Xe",
  "platform": "googlebusiness",
  "accountId": "6713b0e5d1f4c69a2b8e3f75",
  "locationId": "4821930576104832190",
  "locationName": "Padaria Rosa Chiado",
  "reviewer": { "id": null, "name": "Tomás Ferreira", "profileImage": "https://lh3.googleusercontent.com/a/example" },
  "rating": 3,
  "text": "Great bread, but the queue on Saturday was 20 minutes.",
  "created": "2026-10-26T10:12:00Z",
  "hasReply": false,
  "reply": null
}
```

The review `id` is Google's full resource name, so it also tells you which location the review belongs to.

## Reply to a review

Reply through the inbox with [`POST /v1/inbox/reviews/{reviewId}/reply`](https://pozy.ai/docs/api/replyToInboxReview). The `reviewId` contains slashes, so URL-encode it in the path.

```bash cURL
REVIEW_ID="accounts%2F118273645509%2Flocations%2F4821930576104832190%2Freviews%2FAbFvOqn3k2Xe"

curl -X POST "https://api.pozy.ai/v1/inbox/reviews/$REVIEW_ID/reply" \
  -H "Authorization: Bearer $POZY_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: reply-AbFvOqn3k2Xe" \
  -d '{
    "accountId": "6713b0e5d1f4c69a2b8e3f75",
    "message": "Thanks, Tomás. We have added a second till on Saturdays, so the wait should be much shorter now."
  }'
```
```ts Node
const reviewId = "accounts/118273645509/locations/4821930576104832190/reviews/AbFvOqn3k2Xe";

const res = await fetch(`https://api.pozy.ai/v1/inbox/reviews/${reviewId}/reply`, {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.POZY_API_KEY}`,
    "Idempotency-Key": "reply-AbFvOqn3k2Xe",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    accountId: "6713b0e5d1f4c69a2b8e3f75",
    message: "Thanks, Tomás. We have added a second till on Saturdays, so the wait should be much shorter now.",
  }),
});
if (!res.ok) throw new Error((await res.json()).error);
```
```python Python
from urllib.parse import quote

review_id = "accounts/118273645509/locations/4821930576104832190/reviews/AbFvOqn3k2Xe"
requests.post(
    f"https://api.pozy.ai/v1/inbox/reviews/{quote(review_id, safe='')}/reply",
    headers={**H, "Idempotency-Key": "reply-AbFvOqn3k2Xe"},
    json={
        "accountId": "6713b0e5d1f4c69a2b8e3f75",
        "message": "Thanks, Tomás. We have added a second till on Saturdays, so the wait should be much shorter now.",
    },
    timeout=30,
).raise_for_status()
```

> **One reply per review:**
> 
> Google keeps a single owner reply on each review. Replying again replaces the earlier reply, including one somebody typed by hand on Google. Read `hasReply` before you answer, and use an `Idempotency-Key` so a retry after a timeout does not post twice.

To remove a reply and leave the review in place, call [`DELETE /v1/inbox/reviews/{reviewId}/reply`](https://pozy.ai/docs/api/deleteInboxReviewReply) with the `accountId`.

## Google-specific review endpoints

These work on one Google account and take Google's own field names.

| Call | Use it for |
| --- | --- |
| [`GET /v1/accounts/{accountId}/gmb-reviews`](https://pozy.ai/docs/api/getGoogleBusinessReviews) | One location's reviews plus `averageRating` and `totalReviewCount`. Pages with `pageToken`, up to 50 per page. Add `locationId` for another location. |
| [`GET .../gmb-reviews/{reviewId}`](https://pozy.ai/docs/api/getGoogleBusinessReview) | One review, with `starRating` (`ONE` to `FIVE`), `comment` and `reviewReply` |
| [`POST .../gmb-reviews/{reviewId}/reply`](https://pozy.ai/docs/api/replyToGoogleBusinessReview) | Reply with a `comment` |
| [`DELETE .../gmb-reviews/{reviewId}/reply`](https://pozy.ai/docs/api/deleteGoogleBusinessReviewReply) | Remove the reply |
| [`POST .../gmb-reviews/batch`](https://pozy.ai/docs/api/batchGetGoogleBusinessReviews) | Reviews from up to 50 locations in one call |

### Reviews across many locations

For a chain, the batch call saves one request per location. Send `locationNames` in the form `accounts/{googleAccountId}/locations/{locationId}`, built from the [locations list](https://pozy.ai/docs/google-business#work-with-many-locations).

```bash cURL
curl -X POST https://api.pozy.ai/v1/accounts/6713b0e5d1f4c69a2b8e3f75/gmb-reviews/batch \
  -H "Authorization: Bearer $POZY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "locationNames": [
      "accounts/118273645509/locations/4821930576104832190",
      "accounts/118273645509/locations/5930184726391058277"
    ],
    "pageSize": 50,
    "orderBy": "updateTime desc"
  }'
```

The response is a flat `locationReviews` list, each item with the location `name` and the `review`. Results come newest first by default (`orderBy` can also be `rating` or `rating desc`), so you can stop paging once you pass the dates you care about. Send `nextPageToken` back as `pageToken` in the next body; no token means the last page. The batch does not return averages or totals; use the single-location call for those.

## Get told about new reviews

Subscribe a [webhook](https://pozy.ai/docs/webhooks) to the review events instead of polling.

| Event | Fires when |
| --- | --- |
| `review.new` | A new review is posted |
| `review.updated` | The reviewer edits the text or the rating |
| `review.deleted` | Google stops returning the review, for example after its spam filter removed it. This usually arrives 6 to 12 hours after the review disappears. |
| `review.restored` | A review reported as deleted comes back |

A review Google hides and later restores produces both `review.deleted` and `review.restored`. Pozy only checks for deletions on locations covered by a webhook subscribed to `review.deleted` or `review.restored`, so subscribe to them if you need that signal.

A common setup: on `review.new` with a rating of 4 or 5, send an automatic thank-you; on 3 or below, notify a person to answer.

## Endpoints

| Endpoint | What it does |
| --- | --- |
| [`GET /v1/inbox/reviews`](https://pozy.ai/docs/api/listInboxReviews) | List reviews |
| [`POST /v1/inbox/reviews/{reviewId}/reply`](https://pozy.ai/docs/api/replyToInboxReview) | Reply to review |
| [`DELETE /v1/inbox/reviews/{reviewId}/reply`](https://pozy.ai/docs/api/deleteInboxReviewReply) | Delete review reply |
| [`GET /v1/accounts/{accountId}/gmb-reviews`](https://pozy.ai/docs/api/getGoogleBusinessReviews) | Get reviews |
| [`POST /v1/accounts/{accountId}/gmb-reviews/batch`](https://pozy.ai/docs/api/batchGetGoogleBusinessReviews) | Batch get reviews |
| [`GET /v1/accounts/{accountId}/gmb-reviews/{reviewId}`](https://pozy.ai/docs/api/getGoogleBusinessReview) | Get a review |
| [`POST /v1/accounts/{accountId}/gmb-reviews/{reviewId}/reply`](https://pozy.ai/docs/api/replyToGoogleBusinessReview) | Reply to a review |
| [`DELETE /v1/accounts/{accountId}/gmb-reviews/{reviewId}/reply`](https://pozy.ai/docs/api/deleteGoogleBusinessReviewReply) | Delete a review reply |

## Webhook events

| Event | When it fires |
| --- | --- |
| [`review.new`](https://pozy.ai/docs/webhooks/events#review-new) | Review new. |
| [`review.updated`](https://pozy.ai/docs/webhooks/events#review-updated) | Review updated. |
| [`review.deleted`](https://pozy.ai/docs/webhooks/events#review-deleted) | Review deleted. |
| [`review.restored`](https://pozy.ai/docs/webhooks/events#review-restored) | Review restored. |
