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, soquotes is never empty.
  • themes can legitimately be empty. When clustering fails or the reviews are too sparse, notes says 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.

SourceSiteAccepts
trustpilottrustpilot.combrand 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_...
ToolCostWhat it does
run_review_report25 cr / blockPull a brand's reviews and build the report
get_review_reportfreePoll a report by id; advances a running pull
list_review_reportsfreeThe caller's report history
list_review_sourcesfree · publicThe sources that are actually implemented
get_credit_balancefreeSpendable Ounie credits
get_pricingfree · publicBlock price and x402 terms
whoamifreeThe 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

StatusCodeMeaning
400invalid_requestBad brand, unknown source, out-of-range maxReviews. Refused before any price is quoted.
401unauthorizedMissing or invalid credential. Never a redirect — these routes stay outside the browser proxy.
402insufficient_creditsWallet cannot cover the reserve. Carries required_credits, balance_credits and buy_credits_url.
402payment_failedx402 only: the settlement was refused by the facilitator.
404not_foundNo such report, or it belongs to another account.
422no_reviews_foundx402 only: the brand produced nothing. Not charged.
429too_many_runningMore than 5 reports already running.
503upstream_unavailableThe 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.