DEV Community

Michalis Solomou
Michalis Solomou

Posted on

Form D Filings: The Funding Data TechCrunch Misses

Ask most developers where funding data comes from and they'll say "Crunchbase" or "TechCrunch." Neither is actually the source — they're both downstream of a much less glamorous document: the SEC Form D.

This is a technical walkthrough of what a Form D filing actually contains, why most of them never become a press release, and how to pull the raw structured data yourself in Python.

What Form D actually is

Form D is a notice private companies file with the SEC within 15 days of the first sale of securities in an exempt offering — most commonly a Regulation D private placement. It exists so the SEC has a record of who's raising private capital without going through a full public registration.

It's a legal filing, not a marketing document. Companies file it because they have to, not because they want press. That's exactly why it's a better data source than funding news: it doesn't depend on anyone deciding the round is newsworthy.

Where to actually find it

SEC EDGAR publishes every filing at sec.gov/cgi-bin/browse-edgar and a daily full-text index. A raw Form D filing is XML, and looks roughly like this (trimmed):

<edgarSubmission>
  <primaryIssuer>
    <entityName>Acme Robotics, Inc.</entityName>
    <jurisdictionOfInc>DE</jurisdictionOfInc>
  </primaryIssuer>
  <offeringData>
    <industryGroup>
      <industryGroupType>Technology</industryGroupType>
    </industryGroup>
    <offeringSalesAmounts>
      <totalAmountSold>12000000</totalAmountSold>
    </offeringSalesAmounts>
    <typeOfFiling>
      <newOrAmendment>New Notice</newOrAmendment>
    </typeOfFiling>
  </offeringData>
</edgarSubmission>
Enter fullscreen mode Exit fullscreen mode

The fields that matter for lead-scoring purposes:

  • entityName — the legal company name (not always the brand name — "Acme Robotics, Inc." vs. the product name "AcmeBot")
  • industryGroupType — SEC's own classification, a controlled vocabulary (~30 categories)
  • totalAmountSold — how much was actually raised in this offering
  • newOrAmendment — "New Notice" vs. "D/A" (amendment) — this distinction matters more than it looks, see below

The two things that make raw Form D data noisy

If you pull the raw daily index and expect a clean list of "companies that just raised," you'll hit two problems immediately.

1. Amendments look like new filings. A company files an initial Form D, then amends it weeks or months later as the offering progresses (more investors close, the total changes). Each amendment is its own filing in the index. Naively counting filings roughly triples your apparent "new raises" — most of what you're counting is the same round being updated, not a new one.

2. A large fraction of filings aren't operating companies at all. VC funds, hedge funds, and special-purpose vehicles (SPVs) file Form D too — they're also raising private capital, just from their own LPs, not customers with a product budget. SEC's own industryGroupType catches some of these (there's a literal "Pooled Investment Fund" category), but plenty get filed under "Investing" or even "Other" instead. A more reliable tell, if you want to build this filter yourself: fund/SPV legal names very often combine an investment-vehicle noun (Fund, Partners, Capital, Ventures) with a roman-numeral vintage (II, III) — a pattern real operating companies essentially never have. "Acme Ventures III, LP" is a fund. "Acme Robotics, Inc." is not.

Pulling it with Python

The full daily index is public and free — no API key needed to fetch it:

import requests

# SEC requires a descriptive User-Agent identifying your app/contact —
# generic or missing UAs get blocked.
headers = {"User-Agent": "YourApp research@yourdomain.com"}resp = requests.get(
    "https://www.sec.gov/cgi-bin/browse-edgar",
    params={"action": "getcompany", "type": "D", "dateb": "", "owner": "include", "count": 40},
    headers=headers,
)
Enter fullscreen mode Exit fullscreen mode

In practice you'll want the daily full-text index rather than scraping the browse UI — it's more reliable for a scheduled job. Either way, once you have a batch of filings you still need to: parse the XML, drop amendments, filter funds/SPVs by industry + name pattern, and turn totalAmountSold + filing recency into something you can actually rank leads by.

Skipping the parsing

That pipeline (fetch → parse → dedupe → fund-filter → score) is exactly what Funding Signals does — it runs daily and exposes the result as a scored REST endpoint:

import requests

resp = requests.get("https://funding-signals-api.onrender.com/v1/sample?limit=5")
for company in resp.json():
    print(f"{company['score']:>3}  {company['company_name']:<28} "
          f"${company['amount_usd']:,}  ({company['industry']})")
Enter fullscreen mode Exit fullscreen mode

No XML parsing, no amendment/fund filtering to write yourself — same underlying SEC data, already cleaned up. Free tier, no card required: docs.

Why this matters more than "another funding API"

The reason Form D is worth building on directly, instead of just consuming a funding-news aggregator: most Form D filings never get press coverage at all. Smaller rounds, non-VC-backed companies, and anything outside the SF/NY startup press cycle mostly go unreported. If your data only comes from funding news, you're seeing a minority of actual raises — and competing with everyone else reading the same newsletter for the ones you do see.

Reading the primary filing means seeing the raise the day (or days) it becomes public record, whether or not anyone ever writes about it.

Top comments (0)