CRM Enrichment
Keep accounts and contacts fresh with real-time company & people data
Live preview
Checking API key
How it works
Many GTM tools require row-by-row data enrichment: CRM views, prospect lists, account dashboards, monitor inboxes.
Each account or contact row is enriched with one /search request using type: "deep"and a JSON schema. The schema fields become the CRM properties you want kept fresh, and each field's description is the prompt that fills it. For example, annual_revenuewith the description "estimated annual revenue in dollars" or job_titlewith "current public job title or role at the company" tells Exa what to return.
Use type: "deep-reasoning" when accuracy matters more than latency (12-40s vs 4-20s per row).
For large async enrichment requests, try the Agent API (in beta).
Source code
npm install exa-js
import Exa from "exa-js";
const exa = new Exa("YOUR_API_KEY");
const rows = ["Stripe", "Notion", "Vercel", "Linear"];
// One Exa search request per row. Run in parallel for speed.
// Note account QPS limits and consider adding retry logic when running parallel requests at greater scale.
const results = await Promise.all(
rows.map(async (company) => {
const result = await exa.search(`Research ${company}`, {
type: "deep",
outputSchema: {
type: "object",
required: ["company_name"],
properties: {
company_name: {
type: "string",
description: "Most commonly-used company name",
},
annual_revenue: {
type: "integer",
description: "Estimated annual revenue in dollars",
},
headquarters: {
type: "string",
description: "Single headquarters city only. Return one city, without state or country.",
},
headcount: {
type: "integer",
description: "Estimated number of employees",
},
},
},
});
return result.output;
})
);
const enriched = results.map((result) => result.content);
const grounding = results.map((result) => result.grounding);
console.log(enriched);
console.log(grounding);