DEV Community

Gio Rich
Gio Rich

Posted on

Building a subcontractor shortlist for a federal machining bid from SBA Small Business Search with Python

I built this actor; it's a paid tool on Apify with a free trial credit.

If you are a prime contractor writing a federal bid, sooner or later you need small business subcontractors: a machine shop for the parts, a woman-owned firm for the subcontracting plan, a HUBZone partner for the set-aside. The US Small Business Administration keeps the list. It is called SBA Small Business Search (the old Dynamic Small Business Search, DSBS), and every firm in it registered in SAM.gov and chose to be found by federal buyers.

The website is fine for looking up one company. It is slow for the real question: "which machine shops in Ohio could I put on this bid next week?". That needs the whole list in a table, plus a few filters the search page does not do for you.

I built SBA Small Business Search Scraper to return those profiles as clean JSON rows. This post takes 20 real "cnc machining" results from Ohio (run on 9 November 2026) and turns them into a shortlist.

Limits first: there are no contact person names, no revenue, no employee counts and no contract award history. E-mails come out only when they sit on the business's own domain; gmail-style mailboxes are dropped because they are often a person's private address.

Step 1: pull the list

from apify_client import ApifyClient

client = ApifyClient("<YOUR_APIFY_TOKEN>")

run = client.actor("rel8ble/sba-small-business-search-scraper").call(run_input={
    "searchTerm": "cnc machining",
    "states": ["Ohio"],
    "maxResults": 20,
})
firms = client.dataset(run["defaultDatasetId"]).list_items().items
print(len(firms), "firms")
Enter fullscreen mode Exit fullscreen mode

Each row has the legal name, UEI, CAGE code, NAICS codes, SBA certifications with dates, self-certifications from SAM.gov, quality standards, capabilities text, website, business e-mail, phone, address, year established, SAM status and the date the profile was last updated.

What the 20 rows looked like:

field rows
SAM.gov registration active 17
lists ISO 9000 11
lists MIL-STD-45662A 3
profile updated in the last 12 months 17
profile updated in the last 3 months 9
business e-mail / phone / website 19 / 19 / 16
exporter 4
any active SBA certification 1

Primary NAICS was mostly 332710 (machine shops, 6 firms) and 332721 (precision turned products, 3). The oldest shop was founded in 1945, the newest in 2025, median 1992.

Step 2: drop the firms you cannot name on a bid

Three filters do most of the work:

  1. Active SAM.gov registration. An expired registration means the firm cannot receive a federal award today. 3 of 20 were expired.
  2. A quality standard. A prime buying machined parts usually wants ISO 9000 or a MIL standard on file.
  3. A recent profile. A profile untouched for years is a hint the firm is not chasing federal work any more.
from datetime import date, timedelta

cutoff = (date(2026, 11, 10) - timedelta(days=365)).isoformat()

shortlist = [
    f for f in firms
    if f["samRegistration"] == "active"
    and f["qualityStandards"]
    and (f["lastUpdated"] or "") >= cutoff
    and f["email"]
]

for f in sorted(shortlist, key=lambda f: f["lastUpdated"], reverse=True):
    print(f'{f["name"]:<35} {f["city"]:<14} {f["primaryNaics"]}  '
          f'{"+".join(f["qualityStandards"]):<45} {f["lastUpdated"][:10]}')
Enter fullscreen mode Exit fullscreen mode

That left 10 of 20. The top of the list, newest profile first:

business city primary NAICS quality standards founded profile updated
MARKLEY ENTERPRISES LLC Cincinnati 332710 ISO 9000 1999 2026-10-16
UNITED METAL FABRICATORS, INC. Maple Heights 332996 ISO 9000 1945 2026-10-16
SOLMET TECHNOLOGIES, INC. Canton 332721 ISO 9000 1985 2026-10-09
TWO M PRECISION CO. INC. Willoughby 332721 ISO 9000 1977 2026-10-07
UNITED TITANIUM, LLC Wooster 332722 ISO 9000, MIL-Q-9858, MIL-STD-45662A 2025 2026-09-29
UNITED TOOL & MACHINE INC Lakeview 336390 ISO 9000, MIL-Q-9858, MIL-STD-45662A 1973 2026-07-22

Ten real candidates from one search, each with a CAGE code you can check in SAM.gov before you call.

Step 3: watch the certification trap

This part surprised me. Six of the 20 firms self-certified in SAM.gov as woman-owned, veteran-owned, service-disabled veteran-owned or small disadvantaged. Only one firm had an active SBA certification (VOSB and SDVOSB), and that firm's SAM.gov registration had expired.

Self-certification and SBA certification are not the same thing. Since 2024, SDVOSB set-asides need SBA certification, not just the SAM.gov checkbox, and 8(a), HUBZone and WOSB set-asides have always needed the SBA program status. If your plan counts on a certified partner, filter on sbaCertifications, not selfCertifications, and check the SAM status on the same row:

certified = [
    f for f in firms
    if f["sbaCertifications"] and f["samRegistration"] == "active"
]
print(len(certified), "certified and able to receive awards")
Enter fullscreen mode Exit fullscreen mode

On this sample it prints 0. To find certified partners, put the certification in the search input instead of filtering afterwards:

run = client.actor("rel8ble/sba-small-business-search-scraper").call(run_input={
    "naicsCodes": ["332710", "332721"],
    "states": ["OH", "Michigan", "Indiana"],
    "sbaCertifications": ["HUBZone", "SDVOSB"],
    "activeSamOnly": True,
    "onlyWithEmail": True,
    "maxResults": 0,
})
Enter fullscreen mode Exit fullscreen mode

activeSamOnly and onlyWithEmail run before the row is saved, so you do not pay for rows you would throw away.

How it works

SBA Small Business Search is a single-page app on top of a public JSON search API. The actor sends one search request to that API over plain HTTP, with no browser and no login, reads the answer as a stream and flattens each record into a row as it arrives. A few things from building it:

  • State and certification names must match the SBA's own spelling. The actor accepts OH or Ohio and maps it; an unknown value stops the run with an input error before anything is charged.
  • WOSB includes EDWOSB. The SBA search treats them as one family, so asking for WOSB also returns EDWOSB firms.
  • E-mails are filtered for privacy. Free and ISP mailboxes become null; onlyWithEmail counts only the e-mails that pass.

The 20-row demo took 3.2 s in an Apify cloud run.

Price and caveats

$0.80 per 1,000 rows, no start fee. Searches that find nothing, input errors and failed requests are not charged. The 20-row list above costs under two cents.

The data is what each business entered into SAM.gov and the SBA profile, so a capability line is a claim, not a verified fact. Check the SAM.gov entity record before you name anyone on a bid. The actor is not affiliated with or endorsed by the US Small Business Administration.

Actor page: https://apify.com/rel8ble/sba-small-business-search-scraper

Need a custom scraper or a ready dataset? Hire me on Fiverr: https://www.fiverr.com/rel8ble

Top comments (0)

⌂ 换网址