Docs
Dashboard
Examples / Chat and researchBeta

Company research

Research a company on one topic: search the web, read the top pages in a cloud browser, and get structured findings (an overview, the findings, key points and what is still open) where every key point names the page it comes from.

When to use it: You want findings on a company or topic from several pages, where every key point names its page.

Usessearch, extract over several pages with a schema
Needsweb search and a model on the API (no keys of your own)
Sitewhatever the search finds
Outputoutput/report.md (with links to the sources), output/result.json
LanguagesNode and Python

The search goes through the platform's search provider, so no search engine's pages are scraped. The query leaves your computer: keep personal data out of it.

The code

The example as it is in its folder. Both versions do the same job and write the same output/, so one result check covers both.

/** * Company research: research a company on one topic. Search the web, read the top pages in a sandboxed browser, and * return structured findings where every key point names the page it comes from. * *   npx tsx node/index.ts            (BOXLINE_API_KEY; COMPANY and TOPIC for your own research) * * Writes output/result.json and output/report.md. */import { mkdirSync, writeFileSync } from "node:fs";import { join } from "node:path";import { Boxline } from "@boxline/sdk";const out = process.env.OUTPUT_DIR ?? "output";const company = process.env.COMPANY ?? "Mozilla";const topic = process.env.TOPIC ?? "how it makes money";const bx = new Boxline();type Findings = {  companyName: string;  overview: string;  findings: string;  keyPoints: { point: string; source: string }[];  openQuestions: string[];};// 1. Search (the platform's search provider; no search engine pages are scraped). The query leaves your computer://    keep personal data out of it.const found = await bx.search({ query: `${company} ${topic}`, limit: 8 });const urls = found.results.map((r) => r.url).slice(0, 5);console.log(`Researching ${company}: ${topic}. Reading ${urls.length} of ${found.results.length} results:`);for (const u of urls) console.log(`  ${u}`);if (!urls.length) throw new Error("the search found nothing; try another company or topic");// 2. Read those pages in a real browser and fill a schema. Page text is data for the model, never instructions.const r = await bx.extract<Findings>({  urls,  prompt:    `Research ${company}, specifically: ${topic}. Use only these pages. overview: what the company is, in two sentences. ` +    `findings: what the pages say about the topic, in one paragraph. keyPoints: 3 to 6 facts about the topic, each with ` +    `the URL of the page it comes from (one of the pages given). openQuestions: what the pages leave unanswered.`,  schema: {    type: "object",    properties: {      companyName: { type: "string" },      overview: { type: "string" },      findings: { type: "string" },      keyPoints: {        type: "array",        items: { type: "object", properties: { point: { type: "string" }, source: { type: "string", description: "the URL of the page" } }, required: ["point", "source"] },      },      openQuestions: { type: "array", items: { type: "string" } },    },    required: ["companyName", "overview", "findings", "keyPoints", "openQuestions"],  },});const f = r.data;const read = r.pages.filter((p) => p.status !== null && p.status < 400);console.log(`\n${f.companyName}\n\n${f.overview}\n\n${f.findings}\n`);f.keyPoints.forEach((k, i) => console.log(`${i + 1}. ${k.point}\n   (${k.source})`));if (f.openQuestions.length) console.log(`\nStill open: ${f.openQuestions.join("; ")}`);console.log(`\nRead ${read.length}/${r.pages.length} pages with ${r.model} for $${r.usage.costUsd.toFixed(4)}.`);const report = [  `# ${f.companyName}: ${topic}`,  "",  f.overview,  "",  f.findings,  "",  "## Key points",  "",  ...f.keyPoints.map((k) => `- ${k.point} ([source](${k.source}))`),  "",  ...(f.openQuestions.length ? ["## Still open", "", ...f.openQuestions.map((q) => `- ${q}`), ""] : []),].join("\n");mkdirSync(out, { recursive: true });writeFileSync(join(out, "report.md"), report);writeFileSync(  join(out, "result.json"),  JSON.stringify({ company, topic, searched: found.results.map((x) => ({ title: x.title, url: x.url })), pages: r.pages, ...f, model: r.model, usage: { modelUsd: r.usage.costUsd, searches: found.cached ? 0 : 1 } }, null, 2),);

Inputs

Environment variables the example reads:

  • COMPANY: the company (default Mozilla).
  • TOPIC: what to find out about it (default how it makes money).

Run it

Get the examples with git clone https://github.com/Boxline-dev/examples.git and open this one: cd examples/company-research. Put your API key in BOXLINE_API_KEY (and BOXLINE_API_URL for another API), then run either version from the example’s folder. Both write to output/ and stop their sessions when they finish.

# Node 18 or newer, in the example's folderexport BOXLINE_API_KEY=…   # your API keynpm install @boxline/sdk tsxnpx tsx node/index.ts

Then check the result, from the same folder: npx tsx check.ts output. It exits with 0 only when the result is right.

What its check proves

check.ts reads what the example wrote and passes only when the result is right, not merely when the example ran.

The findings are about the company asked for, there are at least 3 key points, at least 2 pages were read, and every key point cites a page that was really read. For the defaults, the findings mention Mozilla's search deals.

Source

github.com/Boxline-dev/examples/company-research on GitHub, with its README.md, check.ts and .env.example. The examples are open source. See all examples.

These docs describe a beta API. Endpoints, fields and SDK methods may change before general availability; breaking changes will be listed in the changelog.