API docs
Review Radar for developers and AI agents
One endpoint family, three ways to authenticate: an Ounie session, a Bearer key, or a USDC payment with no account at all. Every rail returns the same report.
Quickstart
Mint a key at /dashboard/api-keys, then submit a brand. Submitting is asynchronous: you get a report id back immediately and poll it for free until it is done.
# 1. submit
curl -X POST https://reviews.ounie.com/api/reports \
-H "Authorization: Bearer rvw_live_..." \
-H "Content-Type: application/json" \
-d '{"brand": "northwind.example", "maxReviews": 200}'
# → 202
# { "report": { "id": "8f3c...", "status": "running",
# "credits_reserved": 25, "poll_url": "https://..." } }
# 2. poll (free)
curl https://reviews.ounie.com/api/reports/8f3c... \
-H "Authorization: Bearer rvw_live_..."Authentication
Every REST route accepts the shared .ounie.com session cookie first, then a Bearer key. Two key kinds work:
rvw_live_…— this app’s own key, minted in the dashboard. Up to five active per account.ounie_live_…— your ounie.com developer key, which works across the whole Ounie app fleet once you enable “Use across Ounie apps” in ounie.com settings.
A key can also ride the URL as ?api_key=…, for MCP hosts that cannot attach an Authorization header to an HTTP server entry — the Ounie AI Team is one of them.
Keys spend their owner’s Ounie credits and nothing else. A call that would overdraw is refused with 402 insufficient_credits before any work starts, so a key cannot run up a balance.
REST API
POST /api/reports
Submit a report. Reserves credits, starts the pull, returns 202.
{
"brand": "northwind.example", // required — name, domain, or review URL
"sources": ["trustpilot"], // optional — see Sources
"maxReviews": 200, // optional — 10–600, default 600
"recencyDays": 90, // optional — 1–3650
"language": "en" // optional — ISO 639-1
}maxReviews sets the reserve: one block per 200 reviews. You are billed on the blocks actually filled.
GET /api/reports/[id]
Read one report. Free — it is a read of work already paid for. Poll it until status is succeeded or failed.
GET /api/reports
The caller’s report history, newest first. Free.
GET /api/credits
The caller’s spendable Ounie credits. Free.
GET /api/pricing
Public. Block price, limits, the real source list, and the x402 terms.
Report shape
A finished report carries totals computed by arithmetic, sentiment taken from star ratings, and themes whose quotes are read verbatim out of the reviews.
{
"report": {
"id": "8f3c...", "status": "succeeded",
"reviews_count": 214,
"credits_reserved": 50, "credits_spent": 50,
"blocks_reserved": 2, "blocks_billed": 2,
"report": {
"brand": "northwind.example",
"business": { "name": "Northwind", "domain": "northwind.example",
"url": "https://...", "trustScore": 4.1,
"totalReviewsOnSite": 1840 },
"sources": ["trustpilot"],
"window": { "recencyDays": 90, "earliest": "...", "latest": "..." },
"totals": { "count": 214, "avgRating": 4.14,
"byStar": { "1": 22, "2": 9, "3": 14, "4": 41, "5": 128 } },
"sentiment": { "pos": 169, "neu": 14, "neg": 31 },
"themes": [
{ "label": "Delivery times", "count": 24, "stance": "negative",
"quotes": [
{ "text": "Ordered on the 3rd, tracking never moved for eleven days.",
"rating": 1, "date": "2026-07-14T09:12:00Z",
"url": "https://www.trustpilot.com/reviews/...",
"reviewer": "M. Alvarez" }
] }
],
"representative": [ /* a spread of real reviews, negative first */ ],
"notes": []
}
}
}Guarantees worth relying on
- Quote text is verbatim. It is the review’s own body, only ever shortened on a word boundary. No model writes it.
- Every theme has ≥1 quote. A theme with nothing resolvable behind it is dropped, so
quotesis never empty. themescan legitimately be empty. When clustering fails or the reviews are too sparse,notessays so rather than the report inventing filler.- Sentiment is not modelled. 1–2★ is negative, 3★ neutral, 4–5★ positive.
Sources
sources accepts exactly trustpilot. That is the complete list of review sites Review Radar can read, and it is derived from the implementations themselves — a source cannot be advertised here without an adapter behind it.
| Source | Site | Accepts |
|---|---|---|
| trustpilot | trustpilot.com | brand name, domain, or a trustpilot.com/review/ URL |
G2, Capterra and Amazon are not implemented. Passing them returns 400 invalid_request rather than an empty result — a source that silently returns nothing is worse than one that refuses.
MCP server
Streamable HTTP at https://reviews.ounie.com/api/mcp (legacy SSE at /api/sse). Works with Claude, Cursor, ChatGPT, the AI SDK, and the Ounie AI Team.
{
"mcpServers": {
"review-radar": {
"type": "http",
"url": "https://reviews.ounie.com/api/mcp",
"headers": { "Authorization": "Bearer rvw_live_..." }
}
}
}
# Hosts that can't set headers (the Ounie AI Team's manual HTTP entry)
# put the key in the URL instead:
# https://reviews.ounie.com/api/mcp?api_key=rvw_live_...| Tool | Cost | What it does |
|---|---|---|
| run_review_report | 25 cr / block | Pull a brand's reviews and build the report |
| get_review_report | free | Poll a report by id; advances a running pull |
| list_review_reports | free | The caller's report history |
| list_review_sources | free · public | The sources that are actually implemented |
| get_credit_balance | free | Spendable Ounie credits |
| get_pricing | free · public | Block price and x402 terms |
| whoami | free | The authenticated key's owner |
Credit safety is server-side: run_review_report reserves before it runs and returns an insufficient_credits error instead of overdrawing. It waits in-tool for a fast pull and otherwise hands back an id to poll.
x402 pay-per-call
For agents with no Ounie account. POST to /api/x402/report; an unpaid request returns 402 with real terms. Sign and retry with an X-Payment header.
curl -X POST https://reviews.ounie.com/api/x402/report \
-H "Content-Type: application/json" \
-d '{"brand":"northwind.example"}'
# → 402 { "x402Version": 1, "accepts": [{
# "scheme": "exact", "network": "base",
# "maxAmountRequired": "300000",
# "asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
# "payTo": "0x...", "extra": { "name": "USD Coin", "version": "2" } }] }
# retry with the signed authorization
curl -X POST https://reviews.ounie.com/api/x402/report \
-H "X-Payment: <base64 payload>" \
-H "Content-Type: application/json" \
-d '{"brand":"northwind.example"}'
# → 202 { "report_id": "...", "status": "running",
# "poll_url": "https://reviews.ounie.com/api/reports/..." }What the flat price buys
One call buys one block — up to 200 reviews — for $0.30 in USDC on Base. The cap is deliberate. An on-chain settlement cannot be refunded, so the rail must never quote for blocks it might not fill; asking for 600 reviews and receiving 40 would have no way to return the difference. The credits rail is the one that scales past a block, because it can refund.
Ordering, and when you are not charged
The request is validated before a price is quoted, so a malformed brand or an unimplemented source returns 400 rather than making you sign an authorization for a request that can never succeed. After payment is verified we start the pull and wait for the first real review to land; only then is the payment settled. If the brand turns out to have no reviews, the run is aborted and the payment is never settled — you get 422 no_reviews_found with charged: false.
Because a large pull cannot be held open inside one HTTP response, the 202 returns a report id and a poll URL. Polling is free.
Errors
| Status | Code | Meaning |
|---|---|---|
| 400 | invalid_request | Bad brand, unknown source, out-of-range maxReviews. Refused before any price is quoted. |
| 401 | unauthorized | Missing or invalid credential. Never a redirect — these routes stay outside the browser proxy. |
| 402 | insufficient_credits | Wallet cannot cover the reserve. Carries required_credits, balance_credits and buy_credits_url. |
| 402 | payment_failed | x402 only: the settlement was refused by the facilitator. |
| 404 | not_found | No such report, or it belongs to another account. |
| 422 | no_reviews_found | x402 only: the brand produced nothing. Not charged. |
| 429 | too_many_running | More than 5 reports already running. |
| 503 | upstream_unavailable | The review source is unavailable. Nothing charged, no payment quoted — retry after the Retry-After header. |
A report that finishes with no reviews is not an HTTP error on the credits rail: it completes with status: "failed", error: "no_reviews_found" and credits_spent: 0.
When the review source is down
Review Radar tracks the outcome of real runs. If the source starts refusing them, the next request is refused immediately with 503 upstream_unavailable and a Retry-After header — before any price is quoted and before any credits are reserved. On the x402 rail that response deliberately carries no accepts and no price, so you are never asked to sign a payment authorization for work that cannot succeed; on the credits rail your wallet is not touched at all.
It recovers on its own. After a short cooldown one request is let through as a probe; if it succeeds, normal service resumes with no action from you. Treat a 503 as “come back shortly”, not as a failed job — nothing was created and nothing was billed.
Credits
25 credits per block of up to 200 reviews, drawn from the shared Ounie wallet — the same balance every Ounie app uses. One credit is one cent. There is no subscription and no per-app plan.
A report reserves ceil(maxReviews / 200) blocks and settles on ceil(reviewsFound / 200), never above the reserve. The difference is refunded to the exact pools it was drawn from. Buy credits at ounie.com.