Skip to content

List reviews

GET
/v1/reviews
curl --request GET \
--url 'https://api.proofql.dev/v1/reviews?limit=20&source=google&min_rating=4&hidden=true&since=2025-01-01&indexed=true' \
--header 'Authorization: Bearer <token>'

The key’s project and environment, newest first, with the filters the dashboard’s review browser needs. Hidden reviews are included by default (hidden=all) — this is a management call.

Pagination is keyset, not offset: rows are ordered by (occurred_at DESC NULLS LAST, id DESC) and each page carries a next_cursor for the position after its last row. Pass it back as ?cursor= (with the same filters) for the next page; null means the last page. The cursor is opaque — its encoding may change — and a tampered or truncated one is a 422 validation_failed, never a server error. A page is stable while rows are inserted or deleted ahead of it.

cursor
string
>= 1 characters /^[A-Za-z0-9_-]+$/

next_cursor from the previous page. Opaque; invalid → 422.

limit
integer
default: 20 >= 1 <= 100

Page size.

Example
20
source

Where the review came from. custom is the escape hatch for anything not listed.

string
Allowed values: google yelp facebook trustpilot custom
min_rating
integer
>= 1 <= 5

rating >= min_rating. Unrated reviews are excluded when set.

Example
4
hidden
string
default: all
Allowed values: true false all

true only hidden, false only visible, all (default) both.

since
Any of:

YYYY-MM-DD (midnight UTC) or a full ISO 8601 timestamp with Z or offset.

string format: date

occurred_at >= since; a full ISO 8601 datetime or a bare YYYY-MM-DD.

Example
2025-01-01
indexed
string
Allowed values: true false

true only indexed (queryable) reviews, false only those still indexing.

One page.

Media typeapplication/json
object
reviews
required
Array<object>

A stored review, as the management routes return it.

object
id
required
string format: uuid
external_id
required
string
source
required

Where the review came from. custom is the escape hatch for anything not listed.

string
Allowed values: google yelp facebook trustpilot custom
rating
required
integer | null
>= 1 <= 5
text
required
string
author_name
required
string | null
author_avatar_url
required
string | null
occurred_at
required
string | null format: date-time
url
required
string | null
language
required
string | null
metadata
required

Flat string-to-string map the customer can filter on at query time. At most 32 entries; keys 1–64 characters, values up to 512. Nested objects, arrays, numbers, and booleans are rejected, not coerced.

object
<= 32 properties
key
additional properties
string
<= 512 characters
sentiment
required
One of:
string
Allowed values: positive neutral negative
sentiment_source
required
One of:

rating when derived from the star rating (5–4 positive, 3 neutral, 2–1 negative), model when classified for an unrated review.

string
Allowed values: rating model
hidden
required

Hidden reviews never appear in query results.

boolean
status
required

indexing until the pipeline has embedded the review (seconds), then indexed and queryable.

string
Allowed values: indexing indexed
created_at
required
string format: date-time
updated_at
required
string format: date-time
next_cursor
required

Pass back as ?cursor= for the next page; null on the last page.

string | null
Examples
Examplepage
{
"reviews": [
{
"id": "2f1c9e5a-3b7d-4c1e-9f0a-6d2b8e4a1c3f",
"external_id": "accounts/1/locations/2/reviews/abc",
"source": "google",
"rating": 5,
"text": "Dr. Patel did my implant and I honestly forgot it wasn't my own tooth within a week.",
"author_name": "Marcus T.",
"author_avatar_url": null,
"occurred_at": "2026-03-14T18:20:00.000Z",
"url": "https://maps.google.com/?cid=123",
"language": "en",
"metadata": {
"location": "north"
},
"sentiment": "positive",
"sentiment_source": "rating",
"hidden": false,
"status": "indexed",
"created_at": "2026-03-14T18:21:03.412Z",
"updated_at": "2026-03-14T18:21:03.412Z"
}
],
"next_cursor": "eyJvIjoiMjAyNi0wMy0xNFQxODoyMDowMC4wMDBaIiwiaSI6IjJmMWM5ZTVhLTNiN2QtNGMxZS05ZjBhLTZkMmI4ZTRhMWMzZiJ9"
}
x-request-id
required
string
>= 1 characters <= 128 characters

This request’s id, on every response. Reuses the caller’s x-request-id when sent (up to 128 chars), else Cloudflare’s ray id, else a fresh UUID. Also inside every error envelope.

RateLimit-Policy
required
string
/^[0-9]+;w=[0-9]+$/

The key’s rate-limit policy as <limit>;w=<window seconds> (IETF draft ratelimit headers), e.g. 300;w=60 for a secret key, 120;w=60 for a publishable key. Present once the key is known.

RateLimit-Limit
required
integer
>= 1

The request limit per window for this key’s kind.

unauthorized: no Authorization header (and no ?key= where that is allowed), a header that is not Bearer <key>, a key that does not look like pq_(sk|pk)_(live|test)_…, a secret key in the URL, a ?key= on POST /v1/query, or an unknown or revoked key. Rate-limit headers are absent (the key was not resolved).

Media typeapplication/json
object
error
required
object
code
required

Stable error codes. Status by code: unauthorized 401, forbidden 403, not_found 404, payload_too_large 413, unsupported_media_type 415, validation_failed 422, review_limit_reached 422, rate_limited 429, query_quota_exceeded 429, internal 500, embedding_unavailable 503, service_unavailable 503.

string
Allowed values: unauthorized forbidden validation_failed review_limit_reached not_found payload_too_large unsupported_media_type rate_limited embedding_unavailable query_quota_exceeded service_unavailable internal
message
required

For humans; may change between releases. Switch on code.

string
doc_url
required

https://docs.proofql.dev/errors#<code>.

string format: uri
request_id
required

Same value as the x-request-id header.

string
details

Present for validation_failed only.

Array<object>
object
path
required

Dotted path to the offending field ("0.rating", "filters.since"); "" or "(body)" for the whole body.

string
message
required
string
error
required
object
code
Allowed value: unauthorized
Examples
Examplemissing
{
"error": {
"code": "unauthorized",
"message": "Missing Authorization header. Send `Authorization: Bearer <api key>`.",
"doc_url": "https://docs.proofql.dev/errors#unauthorized",
"request_id": "1b7c3d9e-2f4a-4b6c-8d0e-1f2a3b4c5d6e"
}
}
x-request-id
required
string
>= 1 characters <= 128 characters

This request’s id, on every response. Reuses the caller’s x-request-id when sent (up to 128 chars), else Cloudflare’s ray id, else a fresh UUID. Also inside every error envelope.

forbidden: a publishable key (pq_pk_…) on a route that requires a secret key. Decided before the key is looked up, so rate-limit headers are absent.

Media typeapplication/json
object
error
required
object
code
required

Stable error codes. Status by code: unauthorized 401, forbidden 403, not_found 404, payload_too_large 413, unsupported_media_type 415, validation_failed 422, review_limit_reached 422, rate_limited 429, query_quota_exceeded 429, internal 500, embedding_unavailable 503, service_unavailable 503.

string
Allowed values: unauthorized forbidden validation_failed review_limit_reached not_found payload_too_large unsupported_media_type rate_limited embedding_unavailable query_quota_exceeded service_unavailable internal
message
required

For humans; may change between releases. Switch on code.

string
doc_url
required

https://docs.proofql.dev/errors#<code>.

string format: uri
request_id
required

Same value as the x-request-id header.

string
details

Present for validation_failed only.

Array<object>
object
path
required

Dotted path to the offending field ("0.rating", "filters.since"); "" or "(body)" for the whole body.

string
message
required
string
error
required
object
code
Allowed value: forbidden
Example
{
"error": {
"code": "forbidden"
}
}
x-request-id
required
string
>= 1 characters <= 128 characters

This request’s id, on every response. Reuses the caller’s x-request-id when sent (up to 128 chars), else Cloudflare’s ray id, else a fresh UUID. Also inside every error envelope.

validation_failed: the body or query string is not valid JSON, does not match the schema, or carries an unknown field. details lists every issue with a dotted path ("0.rating", "filters.limt"; "" or "(body)" for the body as a whole).

Media typeapplication/json
object
error
required
object
code
required

Stable error codes. Status by code: unauthorized 401, forbidden 403, not_found 404, payload_too_large 413, unsupported_media_type 415, validation_failed 422, review_limit_reached 422, rate_limited 429, query_quota_exceeded 429, internal 500, embedding_unavailable 503, service_unavailable 503.

string
Allowed values: unauthorized forbidden validation_failed review_limit_reached not_found payload_too_large unsupported_media_type rate_limited embedding_unavailable query_quota_exceeded service_unavailable internal
message
required

For humans; may change between releases. Switch on code.

string
doc_url
required

https://docs.proofql.dev/errors#<code>.

string format: uri
request_id
required

Same value as the x-request-id header.

string
details

Present for validation_failed only.

Array<object>
object
path
required

Dotted path to the offending field ("0.rating", "filters.since"); "" or "(body)" for the whole body.

string
message
required
string
error
required
object
code
Allowed value: validation_failed
details
required
array
Examples
ExampleunknownField
{
"error": {
"code": "validation_failed",
"message": "Invalid request: limt is not a recognized field.",
"doc_url": "https://docs.proofql.dev/errors#validation_failed",
"request_id": "1b7c3d9e-2f4a-4b6c-8d0e-1f2a3b4c5d6e",
"details": [
{
"path": "limt",
"message": "is not a recognized field"
}
]
}
}
x-request-id
required
string
>= 1 characters <= 128 characters

This request’s id, on every response. Reuses the caller’s x-request-id when sent (up to 128 chars), else Cloudflare’s ray id, else a fresh UUID. Also inside every error envelope.

RateLimit-Policy
required
string
/^[0-9]+;w=[0-9]+$/

The key’s rate-limit policy as <limit>;w=<window seconds> (IETF draft ratelimit headers), e.g. 300;w=60 for a secret key, 120;w=60 for a publishable key. Present once the key is known.

RateLimit-Limit
required
integer
>= 1

The request limit per window for this key’s kind.

rate_limited: over this key’s per-minute limit. Wait Retry-After seconds.

Media typeapplication/json
object
error
required
object
code
required

Stable error codes. Status by code: unauthorized 401, forbidden 403, not_found 404, payload_too_large 413, unsupported_media_type 415, validation_failed 422, review_limit_reached 422, rate_limited 429, query_quota_exceeded 429, internal 500, embedding_unavailable 503, service_unavailable 503.

string
Allowed values: unauthorized forbidden validation_failed review_limit_reached not_found payload_too_large unsupported_media_type rate_limited embedding_unavailable query_quota_exceeded service_unavailable internal
message
required

For humans; may change between releases. Switch on code.

string
doc_url
required

https://docs.proofql.dev/errors#<code>.

string format: uri
request_id
required

Same value as the x-request-id header.

string
details

Present for validation_failed only.

Array<object>
object
path
required

Dotted path to the offending field ("0.rating", "filters.since"); "" or "(body)" for the whole body.

string
message
required
string
error
required
object
code
Allowed value: rate_limited
Example
{
"error": {
"code": "rate_limited"
}
}
x-request-id
required
string
>= 1 characters <= 128 characters

This request’s id, on every response. Reuses the caller’s x-request-id when sent (up to 128 chars), else Cloudflare’s ray id, else a fresh UUID. Also inside every error envelope.

RateLimit-Policy
required
string
/^[0-9]+;w=[0-9]+$/

The key’s rate-limit policy as <limit>;w=<window seconds> (IETF draft ratelimit headers), e.g. 300;w=60 for a secret key, 120;w=60 for a publishable key. Present once the key is known.

RateLimit-Limit
required
integer
>= 1

The request limit per window for this key’s kind.

Retry-After
required
integer
>= 1

Whole seconds to wait. For rate_limited, until the current window ends; for query_quota_exceeded, until the next UTC month begins.

internal: something unexpected. The message carries the request id and nothing about the cause.

Media typeapplication/json
object
error
required
object
code
required

Stable error codes. Status by code: unauthorized 401, forbidden 403, not_found 404, payload_too_large 413, unsupported_media_type 415, validation_failed 422, review_limit_reached 422, rate_limited 429, query_quota_exceeded 429, internal 500, embedding_unavailable 503, service_unavailable 503.

string
Allowed values: unauthorized forbidden validation_failed review_limit_reached not_found payload_too_large unsupported_media_type rate_limited embedding_unavailable query_quota_exceeded service_unavailable internal
message
required

For humans; may change between releases. Switch on code.

string
doc_url
required

https://docs.proofql.dev/errors#<code>.

string format: uri
request_id
required

Same value as the x-request-id header.

string
details

Present for validation_failed only.

Array<object>
object
path
required

Dotted path to the offending field ("0.rating", "filters.since"); "" or "(body)" for the whole body.

string
message
required
string
error
required
object
code
Allowed value: internal
Example
{
"error": {
"code": "internal"
}
}
x-request-id
required
string
>= 1 characters <= 128 characters

This request’s id, on every response. Reuses the caller’s x-request-id when sent (up to 128 chars), else Cloudflare’s ray id, else a fresh UUID. Also inside every error envelope.

service_unavailable: the database refused or timed out a connection (a connection storm, a paused compute; Retry-After: 1), or a daily platform allowance is spent (Retry-After is the seconds until it renews, at most a day). Nothing about the request was wrong; retry after Retry-After seconds. The message carries the request id and nothing about the cause.

Media typeapplication/json
object
error
required
object
code
required

Stable error codes. Status by code: unauthorized 401, forbidden 403, not_found 404, payload_too_large 413, unsupported_media_type 415, validation_failed 422, review_limit_reached 422, rate_limited 429, query_quota_exceeded 429, internal 500, embedding_unavailable 503, service_unavailable 503.

string
Allowed values: unauthorized forbidden validation_failed review_limit_reached not_found payload_too_large unsupported_media_type rate_limited embedding_unavailable query_quota_exceeded service_unavailable internal
message
required

For humans; may change between releases. Switch on code.

string
doc_url
required

https://docs.proofql.dev/errors#<code>.

string format: uri
request_id
required

Same value as the x-request-id header.

string
details

Present for validation_failed only.

Array<object>
object
path
required

Dotted path to the offending field ("0.rating", "filters.since"); "" or "(body)" for the whole body.

string
message
required
string
error
required
object
code
Allowed value: service_unavailable
Example
{
"error": {
"code": "service_unavailable"
}
}
x-request-id
required
string
>= 1 characters <= 128 characters

This request’s id, on every response. Reuses the caller’s x-request-id when sent (up to 128 chars), else Cloudflare’s ray id, else a fresh UUID. Also inside every error envelope.

Retry-After
required
integer
>= 1

Whole seconds to wait. For rate_limited, until the current window ends; for query_quota_exceeded, until the next UTC month begins.