DEV Community

Plain Signal
Plain Signal

Posted on

How to Find Startups That Just Raised Money with Python (SEC Form D Data)

A company that just raised money has budget, and it's about to spend it on hiring, tools and agencies. For sales teams, recruiters and investors, "who raised this week?" is one of the most useful questions there is.

The usual answers are Crunchbase or PitchBook (expensive) or news-based funding trackers. Those only know about rounds a company announces, and amounts are often "undisclosed".

There's a better source that most people overlook: SEC Form D. US companies that raise money privately (most venture rounds, from pre-seed to late stage) have to file a Form D with the SEC, which is supposed to happen within 15 days of the first sale. The filing is public and includes the amount raised, the industry, the address and the executives. It often appears before any press release, and plenty of smaller rounds are never announced at all.

This tutorial shows how to turn those filings into clean JSON or CSV: without code, with a short Python script, as a CSV lead list, and as a daily Slack alert. It uses the Funding Rounds Tracker on Apify, which I built for exactly this. All code is in the examples repo.

Why not just use EDGAR directly?

You can. EDGAR is free and has a full-text search API. But there are a few catches:

  • Most Form D filings aren't startups. Investment funds, SPVs and real-estate vehicles make up the majority. You have to filter them out.
  • The useful data is in a separate XML document per filing, so one day of filings means a few hundred extra requests.
  • SEC fair-access rules: at most 10 requests per second and a User-Agent that identifies you, or you get blocked.
  • Amendments (Form D/A) repeat earlier raises, so you have to deduplicate.

The tracker does all of that and returns one clean record per raise.

What you get per raise

A real record from a run on 2026-10-02 (executives left out here):

{
  "companyName": "CScale, Inc.",
  "filingDate": "2026-10-01",
  "dateOfFirstSale": "2026-07-17",
  "totalAmountSoldUsd": 144999891,
  "totalOfferingAmountUsd": 194999885,
  "industry": "Other Technology",
  "entityType": "Corporation",
  "yearOfIncorporation": 2022,
  "incorporatedWithinFiveYears": true,
  "city": "PALO ALTO",
  "state": "CA",
  "revenueRange": "Decline to Disclose",
  "securities": ["equity"],
  "numberOfInvestors": 37,
  "exemptions": ["06b"],
  "filingUrl": "https://www.sec.gov/Archives/edgar/data/1975623/000197562326000005/0001975623-26-000005-index.htm"
}
Enter fullscreen mode Exit fullscreen mode

Plus the phone number, street address, minimum investment, sales commissions, previous company names and relatedPersons: the executive officers, directors and promoters named in the filing, with their roles.

Option 1: no code

  1. Open the Funding Rounds Tracker and click Try for free. A free Apify account is enough.
  2. Pick a date range (default: the last 7 days).
  3. Optionally filter by industry (Other Technology, Biotechnology…), state (CA, NY), minimum amount raised, or young companies only (incorporated in the last five years).
  4. Click Start, then download the results as CSV, Excel or JSON.

Option 2: Python

Install the Apify client and set your API token (Console → Settings → Integrations):

pip install apify-client
export APIFY_TOKEN=...
Enter fullscreen mode Exit fullscreen mode

This script lists US operating companies that raised $1M or more in the last 7 days, largest first:

import os
from decimal import Decimal

from apify_client import ApifyClient

client = ApifyClient(os.environ["APIFY_TOKEN"])

run = client.actor("plain-signal/form-d-funding-tracker").call(
    run_input={
        "daysBack": 7,
        "companyType": "operating",  # drops funds, SPVs and real-estate vehicles
        "minAmountSoldUsd": 1_000_000,
        "includeRelatedPersons": True,
        "maxItems": 50,
    },
    max_total_charge_usd=Decimal("1.00"),  # 50 records × $0.02
)
rounds = list(client.dataset(run.default_dataset_id).iterate_items())

for r in sorted(rounds, key=lambda r: r["totalAmountSoldUsd"] or 0, reverse=True):
    people = ", ".join(p["name"] for p in r["relatedPersons"][:2])
    print(f"${r['totalAmountSoldUsd']:>13,}  {r['companyName']} ({r['industry']}, {r['city']}, {r['state']})  {people}")
Enter fullscreen mode Exit fullscreen mode

The top of the output from a run on 2026-10-02 (names cut):

$  416,188,472  Longtail Insurance Holdings Ltd. (Insurance, HAMILTON, D0)  …
$  144,999,891  CScale, Inc. (Other Technology, PALO ALTO, CA)  …
$  128,500,000  POPHOUSE AVATAR NETWORK INC. (Other, LAS VEGAS, NV)  …
$  122,649,481  Quartermaster AI, Inc (Other Technology, ARLINGTON, VA)  …
$   49,999,999  Click Therapeutics, Inc. (Other Health Care, NEW YORK, NY)  …
Enter fullscreen mode Exit fullscreen mode

Two things worth knowing:

  • max_total_charge_usd is a hard cap on what the run can cost. When I first ran this with a $0.50 cap, it stopped cleanly at 25 records. Set the cap to maxItems × $0.02 if you want all of them.
  • D0 is EDGAR's code for Bermuda. Non-US companies that raise from US investors file Form D too, with EDGAR country codes in state.

Option 3: a CSV lead list of new startups

For sales or recruiting you usually want young companies in a few industries, plus a name and a phone number to start with:

import csv
import os
from collections import Counter
from decimal import Decimal

from apify_client import ApifyClient

client = ApifyClient(os.environ["APIFY_TOKEN"])

run = client.actor("plain-signal/form-d-funding-tracker").call(
    run_input={
        "daysBack": 14,
        "companyType": "operating",
        "onlyYoungCompanies": True,  # incorporated within the last five years
        "industries": ["Other Technology", "Computers", "Telecommunications"],
        "minAmountSoldUsd": 500_000,
        "includeRelatedPersons": True,
        "maxItems": 100,
    },
    max_total_charge_usd=Decimal("2.00"),
)
rounds = list(client.dataset(run.default_dataset_id).iterate_items())

with open("startups.csv", "w", newline="") as f:
    w = csv.writer(f)
    w.writerow(["company", "raised_usd", "first_sale", "city", "state", "phone", "executive", "filing"])
    for r in rounds:
        execs = [p for p in r["relatedPersons"] if "Executive Officer" in p["roles"]] or r["relatedPersons"]
        w.writerow([r["companyName"], r["totalAmountSoldUsd"], r["dateOfFirstSale"], r["city"], r["state"],
                    r["phone"], execs[0]["name"] if execs else "", r["filingUrl"]])

print(f"{len(rounds)} startups saved to startups.csv")
print("Top states:", ", ".join(f"{s} {n}" for s, n in Counter(r["state"] for r in rounds).most_common(3)))
Enter fullscreen mode Exit fullscreen mode

Output from a run on 2026-10-02:

49 startups saved to startups.csv
Top states: CA 9, GA 4, VA 3
Enter fullscreen mode Exit fullscreen mode
company,raised_usd,first_sale,city,state,phone,executive,filing
"Zaps Worldwide, Inc.",4055000,2026-09-08,PHOENIX,AZ,…
US Positronix Inc.,2030000,2026-09-16,OBERLIN,OH,…
Enter fullscreen mode Exit fullscreen mode

The run took about a minute and a half and checked 512 filings to find those 49. Import the CSV into your CRM, or add a website/contact finder if you need emails (Form D doesn't include them).

Option 4: a daily Slack alert for new raises

The tracker has a monitor mode: give it a monitorName, and each run returns only filings that earlier runs with the same name haven't returned. Schedule it daily and you have a funding alert:

import json
import os
import urllib.request
from decimal import Decimal

from apify_client import ApifyClient

client = ApifyClient(os.environ["APIFY_TOKEN"])

run = client.actor("plain-signal/form-d-funding-tracker").call(
    run_input={
        "daysBack": 3,
        "companyType": "operating",
        "industries": ["Other Technology", "Computers"],
        "minAmountSoldUsd": 2_000_000,
        "includeRelatedPersons": False,
        "maxItems": 50,
        "monitorName": "tech-raises-2m",
    },
    max_total_charge_usd=Decimal("1.00"),
)
rounds = list(client.dataset(run.default_dataset_id).iterate_items())

lines = [f"*{r['companyName']}* raised ${r['totalAmountSoldUsd']:,} ({r['city']}, {r['state']})\n{r['filingUrl']}"
         for r in sorted(rounds, key=lambda r: r["totalAmountSoldUsd"] or 0, reverse=True)]
text = f"{len(rounds)} new raises\n\n" + "\n\n".join(lines) if rounds else "No new raises today."

webhook = os.environ.get("SLACK_WEBHOOK_URL")
if webhook:
    req = urllib.request.Request(webhook, data=json.dumps({"text": text}).encode(),
                                 headers={"Content-Type": "application/json"})
    urllib.request.urlopen(req)
else:
    print(text)
Enter fullscreen mode Exit fullscreen mode

The first run in my test returned 9 raises:

9 new raises

*CScale, Inc.* raised $144,999,891 (PALO ALTO, CA)
https://www.sec.gov/Archives/edgar/data/1975623/000197562326000005/0001975623-26-000005-index.htm
...
Enter fullscreen mode Exit fullscreen mode

Running it again right away returned No new raises today. Schedule it with cron or GitHub Actions. If you'd rather not run code at all, create a schedule in the Apify Console and connect the Slack, Google Sheets, HubSpot or email integration to the actor.

What does it cost?

The tracker charges $0.02 per funding record ($20 per 1,000), with no monthly fee. Filings removed by your filters are free. The 50-record run above cost $1.00 and the 49-startup lead list $0.98. A typical week has about 250 new raises by operating companies, so a daily monitor for all of them comes to about $20 a month, and with narrow filters it costs cents. Apify's free plan includes monthly platform credit, which is enough to try all of the above.

Good to know

  • Amount sold vs. offering amount: totalAmountSoldUsd is what was sold at filing time, totalOfferingAmountUsd the planned round size. CScale above had sold $145M of a planned $195M.
  • Filing dates: the 15-day deadline isn't always met. CScale's first sale was in July and the filing came in October. dateOfFirstSale tells you when the round actually started.
  • No round names: Form D doesn't say "Seed" or "Series A". Use the amount, yearOfIncorporation and numberOfInvestors as proxies.
  • Addresses: some companies list a registered agent (often in Delaware) instead of their office.
  • History: set Filed from / Filed to to any range since 2009 for backtesting or market research.
  • Personal data: Form D is public, but if you store or contact the people in relatedPersons, you're responsible for doing that lawfully (GDPR, CCPA, CAN-SPAM). Turn off includeRelatedPersons if you only need companies.

Wrapping up

All four examples are in the plain-signal/examples repo, along with examples for the Google Jobs Scraper (who's hiring, with salaries and apply links). Funding plus hiring is a strong combination for lead generation: a company that just raised and is posting jobs is about to grow. Questions and feature requests are welcome in the comments or on the actor's Issues tab.

This article was written with AI assistance. All code was run against the live tracker before publishing, and the outputs shown are real.

Top comments (0)