Research Desk for agents
Three rails, one behaviour: submit a question, get back a brief in which every claim carries the indices of sources that were actually fetched. Unsupported claims never ship, and a brief with nothing traceable is refunded rather than delivered.
REST
Every route accepts the shared .ounie.com session cookie or a bearer key — in the Authorization header, or as ?api_key=for clients that cannot set headers. Calls draw the key owner’s Ounie credits and can never overdraw: a short wallet gets 402 before anything upstream runs.
Run a brief
POST https://research.ounie.com/api/briefs
Authorization: Bearer rsd_live_…
Content-Type: application/json
{
"question": "What changed in EU AI Act enforcement in 2026?",
"depth": "standard", // quick | standard | deep (default standard)
"recencyDays": 180, // optional, 1–3650
"domains": ["europa.eu", "-reddit.com"], // '-' excludes; max 20
"format": "both" // markdown | json | both (default both)
}
→ 202 Accepted
{
"id": "…", "status": "queued",
"eta_seconds": 60, "credits_reserved": 35,
"poll_url": "https://research.ounie.com/api/briefs/…"
}A brief runs immediately in the background; a cron sweeps anything the inline runner never started, so a dropped connection costs latency, not money. Up to 5 briefs may run at once per account.
Poll or read one
GET https://research.ounie.com/api/briefs/{id} # free — a read of paid work
GET https://research.ounie.com/api/briefs # ?status=&limit=&offset=
DELETE https://research.ounie.com/api/briefs/{id} # abort (refunds) or deletePolling costs nothing and also nudges a queued brief into running, so a caller that polls is its own backstop.
MCP server
One endpoint serves Streamable HTTP and legacy SSE. Works with Claude, Cursor, ChatGPT, the AI SDK, and the Ounie AI Team, whose manual HTTP entries cannot set static headers — so the key rides in the URL instead.
https://research.ounie.com/api/mcp # Authorization: Bearer rsd_live_… https://research.ounie.com/api/mcp?api_key=rsd_live_… # header-less clients https://research.ounie.com/api/sse # legacy SSE
| Tool | What it does | Cost |
|---|---|---|
| research | Run a brief and wait for it. Returns the cited brief, or a brief_id to poll if a deep run outlasts the tool call. | by depth |
| get_brief | Fetch a brief by id: status, claims, sources, quotes, stats. | free |
| list_briefs | List your briefs, optionally filtered by status. | free |
| get_credit_balance | Spendable Ounie credits on the shared wallet. | free |
| get_pricing | The depth ladder and what each rung does upstream. No auth. | free |
| whoami | The authenticated key's owner and key id. | free |
A tool call that cannot be funded returns insufficient_creditswith the amount needed and a link to buy — it never runs a partial brief.
x402 — keyless, pay per call
For agents with no Ounie account. Pay in USDC on Base; no key, no wallet with us, no signup.
POST https://research.ounie.com/api/x402/research
{ "question": "…", "depth": "standard" }
→ 402 Payment Required { accepts: [{ scheme, network, payTo, asset, … }] }
# sign the terms, retry with the header
X-Payment: <base64 signed payload>
→ 202 Accepted
{
"brief_id": "…",
"poll_url": "https://research.ounie.com/api/x402/research/…", # FREE
"eta_seconds": 60,
"payment": { "tx_hash": "0x…", "paid_atomic_usdc": "420000" }
}400 no_sources_found with your funds untouched.| Depth | Price | What runs |
|---|---|---|
| Quick | $0.18 | 1 search · up to 6 sources |
| Standard | $0.42 | 3 searches · up to 14 sources |
| Deep | $0.90 | 6 searches · up to 24 sources |
Keyless briefs belong to no account and are reachable only by the id returned at purchase. Polling one is free and never charges twice.
The brief
sources[].index is 1-based and every index appearing in keyFindings[].sources or answer[].sources resolves to one of them. That invariant is enforced before delivery, not asserted here.
{
"status": "succeeded", // queued|running|succeeded|thin|failed|aborted
"credits_spent": 35,
"confidence": 0.78,
"brief": {
"question": "…",
"depth": "standard",
"keyFindings": [ { "text": "…", "sources": [1, 3] } ],
"answer": [ { "text": "…", "sources": [2] } ],
"sources": [{
"index": 1,
"title": "…",
"url": "https://…",
"publishedAt": "2026-07-02T00:00:00.000Z", // null when unknown — never guessed
"quote": "the sentence the claim rests on",
"domain": "example.com"
}],
"openQuestions": ["…"],
"stats": {
"queriesRun": 3,
"sourcesConsulted": 14, // pages fetched and read
"sourcesCited": 9, // pages something actually cited
"claimsDropped": 2, // claims that cited nothing real
"citationsRepaired": 1 // indices pointing at no fetched source
}
},
"markdown": "# …" // omitted when format="json"
}A source nobody cited is counted in sourcesConsulted but not shipped in sources— it consumed budget, not trust. Surviving sources are renumbered compactly, so there are never gaps in the indices you are asked to check.
Pricing
| Depth | Credits | Searches | Pages requested | Read / source |
|---|---|---|---|---|
| Quick | 15 | 1 | 6 | 1,200 |
| Standard | 35 | 3 | 24 | 3,000 |
| Deep | 75 | 6 | 120 | 6,000 |
A credit is one cent, drawn from the shared Ounie wallet. Reserved at submit, settled when the brief lands, refunded in full when it is thin, aborted or failed. Reads, exports and polls are free. Manage your wallet.
Errors
| Status | Meaning |
|---|---|
| 400 | The question or a filter was rejected. The message names the field and the fix. |
| 401 | No session cookie and no valid bearer key. |
| 402 | insufficient_credits — includes required_credits, balance_credits and buy_credits_url. Nothing upstream ran. |
| 404 | No such brief for this owner. |
| 429 | More than 5 briefs already running. |
| 503 | Search or synthesis is unavailable on this deployment. Nothing was charged. |
A finished brief can still carry status: "thin": the run completed and found nothing traceable. That is deliberately distinct from failed, and both refund in full.