import { readFile, writeFile } from "node:fs/promises";
const statePath = new URL("./state.json", import.meta.url);
const apiKey = process.env.SECAPI_API_KEY;
if (!apiKey) throw new Error("SECAPI_API_KEY is required");
let state = { seen: [] };
try {
state = JSON.parse(await readFile(statePath, "utf8"));
} catch (error) {
if (error?.code !== "ENOENT") throw error;
}
const seen = new Set(state.seen ?? []);
const retainedAccessions = [];
const retained = new Set();
const reviewQueue = [];
for (const ticker of ["AAPL", "MSFT", "NVDA"]) {
let cursor;
do {
const query = new URLSearchParams({ ticker, forms: "8-K,8-K/A", limit: "50" });
if (cursor) query.set("cursor", cursor);
const response = await fetch(`https://api.secapi.ai/v1/filings?${query}`, {
headers: { "x-api-key": apiKey },
});
if (!response.ok) throw new Error(`${ticker}: ${response.status} ${await response.text()}`);
const payload = await response.json();
for (const filing of payload.data ?? []) {
const accessionNumber = filing.accessionNumber;
if (!accessionNumber) continue;
if (!retained.has(accessionNumber)) {
retainedAccessions.push(accessionNumber);
retained.add(accessionNumber);
}
if (seen.has(accessionNumber)) continue;
reviewQueue.push({
ticker: filing.ticker,
form: filing.form,
filingDate: filing.filingDate,
accessionNumber,
filingUrl: filing.filingUrl ?? filing.provenance?.filingUrl ?? null,
requestId: payload.requestId,
});
seen.add(accessionNumber);
}
cursor = payload.hasMore ? payload.nextCursor : null;
} while (cursor);
}
for (const accessionNumber of state.seen ?? []) {
if (retainedAccessions.length >= 5000) break;
if (!retained.has(accessionNumber)) {
retainedAccessions.push(accessionNumber);
retained.add(accessionNumber);
}
}
await writeFile(statePath, JSON.stringify({ seen: retainedAccessions }, null, 2));
console.log(JSON.stringify({ newFilings: reviewQueue.length, reviewQueue }, null, 2));