SAM.gov holds every U.S. federal contract opportunity — 5,591,183 notices back to 2000, about 49,702 active at any moment, roughly 2,500 published a day. It is the largest genuinely public procurement dataset in the world, and the main obstacle to using it is that most tooling asks you to register for a key first.
Why there is no API key to get
There is an official keyed API, and there is also the endpoint the SAM.gov search page itself calls. The second is public and needs no credential, which is what this Actor uses: nothing to register for, nothing to renew, no quota that runs out mid-run. Why that distinction matters.
Step 1 — choose the time window
Sweeping is by publication date, one calendar day per request, newest first. publishedWithinDays defaults to 7; publishedFrom and publishedTo override it for a fixed range.
activeOnly defaults to true, which is the ~49,700 notices still open or recently awarded. Turn it off to reach the 5.6-million-notice archive — useful for research, unnecessary for monitoring.
Step 2 — narrow with the filters that are well filled
| Filter | Present on | Notes |
|---|---|---|
pscCodes | 97.8% | The product or service being bought. Marginally the better filter. |
naicsCodes | 96.1% | The industry of the supplier. |
setAsideCodes | 46.1% | Absence means open competition, not missing data. |
agencyIds | 99.4% | Changes attachment rates more than notice type does. |
officeStates | — | Better filled than place of performance. |
placeOfPerformanceStates | ~33% | Only about a third of notices state one. |
All of these filter server-side, so a notice they exclude is never delivered and never charged. NAICS or PSC covers the choice between the two classification systems.
Step 3 — decide about documents
scrapeAttachments adds the attachment list to every notice at one extra request each. Before turning it on for a large run, note the hit rate: 36.7% ± 4.6 of active notices carry a real file, and it swings from ~100% at Veterans Affairs to ~28% at the Department of Defense.
Step 4 — run it
{
"maxResults": 500,
"activeOnly": true,
"publishedWithinDays": 7,
"noticeTypes": ["o", "k"],
"naicsCodes": ["541511"],
"setAsideCodes": ["SBA"],
"scrapeAttachments": true
}Notice types o (Solicitation) and k (Combined Synopsis/Solicitation) are the ones that carry documents and represent live opportunities. Rows are deduplicated by notice_id before delivery, so a duplicate is never charged.
Reading the deadline correctly
There are three deadline fields, and using the wrong one puts fictional times in your calendar. response_due_precision is instant on 61.0% of deadlines — only then is response_due_utc a true instant you can convert. On the 38.8% that are date-only, SAM.gov stamps midnight UTC and this Actor refuses to pass that through. Why there are three.
Reading the output
62 fields, snake_case, null where a value is genuinely absent and never a missing key — so row.award_amount is safe on every row. Contact email is at 99.2% and contact phone at 32.7%, which tells you which channel the data actually supports.
Award fields sit at ~23% of all notices because most notices are not awards; within award notices they are effectively always present. The full field reference.
What a run costs
$0.0025 per notice, $0.0005 per attachment listed, $0.0005 per exclusion record, $0.00001 to start. Five hundred notices with attachments is about $1.50. You are not charged for error rows and not charged for duplicates, and charging happens as each row is delivered rather than in a batch.
Notices are immutable once published — an amendment creates a new notice that links back — so cached rows stay correct and maxCacheAgeDays defaults to a generous 90.
Four mistakes that waste a run
- Scheduling against a date-only deadline. Midnight UTC is not when the bid is due.
- Enabling attachments on award notices. 14% hit rate, one request each.
- Filtering on place of performance. Only about a third of notices state one, so you silently drop two thirds.
- Treating an absent set-aside as missing data. It means open competition.


