Research Brief
A one-page, citation-grounded brief about a company, person, or market. The whole brief is a single markdown string with section headings. Use it for pre-meeting prep, account research, or competitive deep-dives.
Demo
Type a subject and hit Generate. Exa runs a deep search across the web and streams a structured markdown brief back with inline citations.
Try one of these:
How it works
One call to exa.search with type: "deep" produces a long markdown document grounded in web sources. Depth beats latency for a brief, so expect 15-30 seconds end-to-end.
Section structure comes from outputSchema.description, which lists the required headings (Overview, Product, Market, People, Recent News, Talking Points) and the formatting rules. Citation markers like [1] appear at the ends of sentences and map 1-to-1 to entries flattened from output.grounding.
The client renders the markdown with a small purpose-built parser: headings, paragraphs, bullets, and inline favicon citation chips deduped by domain.
Source code
"use client";
import { useState } from "react";
import type { Citation } from "@/lib/citations";
import { MarkdownBrief } from "@/lib/markdown";
type BriefState =
| { status: "idle" }
| { status: "generating" }
| { status: "done"; content: string; citations: Citation[] }
| { status: "error"; error: string };
const SUGGESTIONS = [
"Stripe",
"Patrick Collison",
"AI coding agents market",
"Anthropic Series G round",
];
export function BriefExample() {
const [subject, setSubject] = useState("");
const [brief, setBrief] = useState<BriefState>({ status: "idle" });
async function generate(input: string) {
const s = input.trim();
if (!s || brief.status === "generating") return;
setBrief({ status: "generating" });
try {
const res = await fetch("/api/brief", {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({ subject: s }),
});
const json = await res.json();
if (!res.ok || json.error) {
setBrief({
status: "error",
error: json.error || `Request failed (${res.status})`,
});
return;
}
setBrief({
status: "done",
content: json.content ?? "",
citations: json.citations ?? [],
});
} catch (err) {
setBrief({
status: "error",
error: err instanceof Error ? err.message : String(err),
});
}
}
const isGenerating = brief.status === "generating";
return (
<div className="space-y-4">
<form
onSubmit={(e) => {
e.preventDefault();
void generate(subject);
}}
className="border border-[#e5e5e5] bg-white overflow-hidden focus-within:border-[var(--color-brand)]"
>
<div className="flex items-center gap-2 px-3 py-2">
<input
type="text"
value={subject}
onChange={(e) => setSubject(e.target.value)}
placeholder="Company, person, or market"
className="flex-1 text-[13px] text-[#111827] focus:outline-none"
/>
<button
type="submit"
disabled={isGenerating || !subject.trim()}
className="bg-[var(--color-brand)] text-white text-[13px] font-medium px-3.5 py-1.5 disabled:opacity-50"
>
{isGenerating ? "Researching..." : "Generate"}
</button>
</div>
</form>
{brief.status === "idle" && (
<div className="text-[13px] text-[#6b7280]">
<p className="mb-2">Try one of these:</p>
<div className="flex flex-wrap gap-2">
{SUGGESTIONS.map((s) => (
<button
key={s}
type="button"
onClick={() => {
setSubject(s);
void generate(s);
}}
className="rounded-full border border-[#e5e5e5] bg-white px-3 py-1 text-[12px] text-[#374151] hover:border-[var(--color-brand)] hover:text-[#111827]"
>
{s}
</button>
))}
</div>
</div>
)}
{brief.status === "error" && (
<div className="text-[13px] text-[#b91c1c]">{brief.error}</div>
)}
{brief.status === "generating" && (
<div className="border border-[#e5e5e5] bg-white px-6 py-5">
<ResearchingState />
</div>
)}
{brief.status === "done" && (
<div className="border border-[#e5e5e5] bg-white px-6 py-5">
<MarkdownBrief
markdown={brief.content}
citations={brief.citations}
streaming={false}
/>
</div>
)}
</div>
);
}
function ResearchingState() {
return (
<div className="space-y-2">
<div className="h-4 w-1/3 rounded bg-[#e5e7eb] animate-pulse" />
<div className="h-3 w-full rounded bg-[#e5e7eb] animate-pulse" />
<div className="h-3 w-5/6 rounded bg-[#e5e7eb] animate-pulse" />
<div className="h-3 w-3/4 rounded bg-[#e5e7eb] animate-pulse" />
</div>
);
}