List reviews
const url = 'https://api.proofql.dev/v1/reviews?limit=20&source=google&min_rating=4&hidden=true&since=2025-01-01&indexed=true';const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}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.
Authorizations
Section titled “Authorizations”Parameters
Section titled “Parameters”Query Parameters
Section titled “Query Parameters”next_cursor from the previous page. Opaque; invalid → 422.
Page size.
Example
20Where the review came from. custom is the escape hatch for anything not listed.
rating >= min_rating. Unrated reviews are excluded when set.
Example
4true only hidden, false only visible, all (default) both.
true only indexed (queryable) reviews, false only those still indexing.
Responses
Section titled “Responses”One page.
object
A stored review, as the management routes return it.
object
Where the review came from. custom is the escape hatch for anything not listed.
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
Hidden reviews never appear in query results.
indexing until the pipeline has embedded the review (seconds), then indexed and queryable.
Pass back as ?cursor= for the next page; null on the last page.
Examples
{ "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"}Headers
Section titled “Headers”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.
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.
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).
object
object
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.
For humans; may change between releases. Switch on code.
https://docs.proofql.dev/errors#<code>.
Same value as the x-request-id header.
Present for validation_failed only.
object
Dotted path to the offending field ("0.rating", "filters.since"); "" or "(body)" for the whole body.
object
Examples
{ "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" }}Headers
Section titled “Headers”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.
object
object
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.
For humans; may change between releases. Switch on code.
https://docs.proofql.dev/errors#<code>.
Same value as the x-request-id header.
Present for validation_failed only.
object
Dotted path to the offending field ("0.rating", "filters.since"); "" or "(body)" for the whole body.
object
Example
{ "error": { "code": "forbidden" }}Headers
Section titled “Headers”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).
object
object
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.
For humans; may change between releases. Switch on code.
https://docs.proofql.dev/errors#<code>.
Same value as the x-request-id header.
Present for validation_failed only.
object
Dotted path to the offending field ("0.rating", "filters.since"); "" or "(body)" for the whole body.
object
Examples
{ "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" } ] }}Headers
Section titled “Headers”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.
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.
The request limit per window for this key’s kind.
rate_limited: over this key’s per-minute limit. Wait Retry-After seconds.
object
object
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.
For humans; may change between releases. Switch on code.
https://docs.proofql.dev/errors#<code>.
Same value as the x-request-id header.
Present for validation_failed only.
object
Dotted path to the offending field ("0.rating", "filters.since"); "" or "(body)" for the whole body.
object
Example
{ "error": { "code": "rate_limited" }}Headers
Section titled “Headers”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.
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.
The request limit per window for this key’s kind.
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.
object
object
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.
For humans; may change between releases. Switch on code.
https://docs.proofql.dev/errors#<code>.
Same value as the x-request-id header.
Present for validation_failed only.
object
Dotted path to the offending field ("0.rating", "filters.since"); "" or "(body)" for the whole body.
object
Example
{ "error": { "code": "internal" }}Headers
Section titled “Headers”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.
object
object
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.
For humans; may change between releases. Switch on code.
https://docs.proofql.dev/errors#<code>.
Same value as the x-request-id header.
Present for validation_failed only.
object
Dotted path to the offending field ("0.rating", "filters.since"); "" or "(body)" for the whole body.
object
Example
{ "error": { "code": "service_unavailable" }}Headers
Section titled “Headers”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.
Whole seconds to wait. For rate_limited, until the current window
ends; for query_quota_exceeded, until the next UTC month begins.