Export Form D offering notices with JavaScript

Download a bounded page of quarterly Form D notices, preserve source strings and save archive provenance with the result.

WHAT YOU WILL BUILD

Query an explicitly imported period and save its data, pagination cursor and source version together. This example exports one page; larger exports must follow nextOffset and verify the same snapshot on every page.

Requirements: Node.js 22 or later; no extra packages. An API key from your dashboard, stored in the SEC_API_KEY environment variable.

Run the workflow

Save this as workflow.mjs and run node workflow.mjs. The production base URL is used by default. Keep credentials out of source control.

// Node.js 22+. Set SEC_API_KEY before running.
const key = process.env.SEC_API_KEY;
if (!key) throw new Error("Set SEC_API_KEY first");
const base = process.env.SEC_API_BASE_URL || "https://sec-api.dev";
async function request(path) {
  const response = await fetch(base + path, {
    headers: { Authorization: "Bearer " + key }
  });
  if (!response.ok) {
    throw new Error("HTTP " + response.status + ": " + await response.text());
  }
  return response.json();
}

const { writeFile } = await import("node:fs/promises");
const page = await request("/api/v1/offerings?" + new URLSearchParams({
  period: "2026q3", limit: "25", offset: "0"
}));
if (page.meta.period !== "2026q3" || !page.meta.sha256) {
  throw new Error("Missing archive provenance");
}
const accessions = new Set(page.data.map(row => row.accessionNumber));
if (accessions.size !== page.data.length) throw new Error("Duplicate submissions");
// Keep amounts as source strings: do not coerce Indefinite or blanks to zero.
await writeFile("form-d-page.json", JSON.stringify(page, null, 2));
console.log(JSON.stringify({
  rows: page.data.length,
  total: page.pagination.total,
  nextOffset: page.pagination.nextOffset,
  snapshotId: page.meta.snapshotId,
  source: page.meta.source
}, null, 2));

Choose the period explicitly

The endpoint defaults to the latest imported quarter. An explicit period makes the intended source clear; unavailable periods return 404. Current published coverage is 2026Q3, not every historical quarter.

Keep amendments and multiple issuers intact

Each submission appears once even if several issuers are attached. Preserve ISAMENDMENT and PREVIOUSACCESSIONNUMBER. Adding amended and original amounts can double count an offering.

Continue only within your request budget

Follow pagination.nextOffset until null for a complete filtered result. Compare meta.snapshotId and sha256 on every page; restart if they change. The maximum page size is 100, and every request uses account quota. This example deliberately saves one page.

Sources and further reading

The underlying SEC dataset behavior is documented in the official SEC API reference and SEC technical specifications. Response-field names shown here belong to the sec-api.dev endpoint contract. The scripts print actual results and do not embed fabricated filing or financial data.

All tutorials ←