← back to Ventura Claw

server/connectors/sec.js

59 lines

// SEC EDGAR — public-company filings & financial data. No key.
// https://www.sec.gov/edgar/sec-api-documentation
// SEC requires a descriptive User-Agent on every request.
const UA = "VenturaClaw/1.0 (steve@venturaclaw.com)";
async function call(url) {
  const r = await fetch(url, {
    headers: { "User-Agent": UA, Accept: "application/json" },
    signal: AbortSignal.timeout(20_000),
  });
  if (!r.ok) throw new Error(`sec ${r.status}`);
  return r.json();
}
function pad10(cik) { return String(cik).replace(/^0+/, "").padStart(10, "0"); }
module.exports = {
  meta: { id: "sec", name: "SEC EDGAR", category: "data", docsUrl: "https://www.sec.gov/edgar/sec-api-documentation", auth: "none", realImpl: true },
  fields: [],
  configured() { return true; },
  async health() {
    try {
      const r = await fetch("https://www.sec.gov/cgi-bin/browse-edgar?action=getcompany&CIK=0000320193&type=10-K&dateb=&owner=include&count=1", {
        headers: { "User-Agent": UA }, signal: AbortSignal.timeout(15_000),
      });
      return { ok: r.ok, label: r.ok ? "live · public-company filings" : `http ${r.status}` };
    } catch (e) { return { ok: false, reason: e.message }; }
  },
  actions: {
    // Look up a company by ticker, returns CIK + name
    async "ticker_lookup"({ ticker }) {
      if (!ticker) throw new Error("ticker required");
      const d = await call("https://www.sec.gov/files/company_tickers.json");
      const t = String(ticker).toUpperCase();
      for (const v of Object.values(d || {})) {
        if (v.ticker === t) return { cik: pad10(v.cik_str), ticker: v.ticker, name: v.title };
      }
      return { error: "not_found" };
    },
    async "submissions"({ cik }) {
      if (!cik) throw new Error("cik required");
      const d = await call(`https://data.sec.gov/submissions/CIK${pad10(cik)}.json`);
      return {
        cik: d.cik, name: d.name, sic: d.sic, sicDescription: d.sicDescription,
        tickers: d.tickers, exchanges: d.exchanges,
        recent_filings: (d.filings?.recent?.form || []).slice(0, 20).map((form, i) => ({
          form,
          filingDate: d.filings.recent.filingDate[i],
          accessionNumber: d.filings.recent.accessionNumber[i],
          primaryDocument: d.filings.recent.primaryDocument[i],
        })),
      };
    },
    async "company_concept"({ cik, taxonomy, tag }) {
      if (!cik || !tag) throw new Error("cik + tag required");
      const t = taxonomy || "us-gaap";
      const d = await call(`https://data.sec.gov/api/xbrl/companyconcept/CIK${pad10(cik)}/${t}/${tag}.json`);
      return d;
    },
  },
};