SAM.gov Scraper – New Contract Opportunity Alerts
Pricing
from $4.00 / 1,000 opportunities
SAM.gov Scraper – New Contract Opportunity Alerts
SAM.gov scraper for federal contract opportunities by NAICS, set-aside, agency and state. Newest 25 per run, or only new/amended with a saved monitor name.
Pricing
from $4.00 / 1,000 opportunities
Rating
0.0
(0)
Developer
Chris Grant
Maintained by CommunityActor stats
0
Bookmarked
1
Total users
0
Monthly active users
a day ago
Last modified
Categories
Share
SAM.gov Scraper & Federal Contract Alerts (Opportunity Radar)
SAM.gov scraper and RFP alert tool for federal contract opportunities (solicitations, presolicitations, sources sought, combined synopses and awards) that match your NAICS codes, set-asides, agency and state. Set a monitor name and it returns only new or amended notices; without one, each run returns the newest notices in your lookback window (25 by default), with no change tracking. No API key needed: it reads SAM.gov's official public data. Add your free SAM.gov key for real-time results. Each run gives you clean, flat rows for Google Sheets, Slack, Zapier, Make or your CRM, and with a monitor name you only pay for what's new.
GovCon suite by CCE Partners
- SAM.gov Scraper – New Contract Opportunity Alerts (this Actor): new and amended SAM.gov opportunities by NAICS, set-aside, agency and state.
- Expiring Federal Contracts – Recompete Radar: federal contracts ending in 3–18 months (a recompete pipeline from USAspending.gov); only some are matched to an open SAM.gov notice.
- SEC Form D Scraper – New Startup Funding Raises: newly funded private companies (prospective teaming partners and commercial buyers).
- Federal Contract Awards – New Winners Feed: who won the contract, from USAspending.gov: winner, current parent company, amount, set-aside and competition details; only new awards with a monitor name.
SAM.gov saved search alerts vs this Actor
SAM.gov saved searches email you links, and you still have to log in and click through to see each notice. This Actor returns the notice itself as a row (title, notice type, deadline, agency, NAICS, set-aside, place of performance, description and samUrl) that you can filter, sort and send anywhere.
- Only new or changed. Set a monitor name and it remembers every notice it has seen, returning a notice again only when it's amended (title, response deadline, set-aside, NAICS/PSC, award).
- No key required. Keyless mode uses SAM.gov's official public nightly extract (up to 24h old). Add a key for real-time results.
- No personal contact data. No names, emails or phone numbers, so the output is safe to share and store.
- Pay per result, no platform usage fees. With a monitor name you pay only for new or changed notices; unchanged ones are free, even when you output them with
emitUnchanged. Without a monitor name every notice a run returns is charged (up to 25 by default), including notices an earlier run already returned (unless you have saved state from before 0.2.8; see "Monitor names").
Use cases
1. 8(a) IT services firm: daily new solicitations
{"naicsCodes": ["541511", "541512", "541519"],"setAside": "8A","noticeTypes": ["o", "k"],"lookbackDays": 7,"monitorName": "8a-it-daily"}
Schedule it daily and connect Google Sheets. Each morning you get only the new or amended IT solicitations.
2. SDVOSB contractor: Texas construction bids and sources sought
{"naicsCodes": ["236220", "238210"],"setAside": "SDVOSBC","state": "TX","noticeTypes": ["o", "p", "r"],"lookbackDays": 14,"monitorName": "sdvosb-tx-construction","aiTagging": true}
Sources sought (r) and presolicitations (p) give you early warning before the RFP drops. Tags add the sector, set-aside category and days to respond.
3. Capture team: cybersecurity across all agencies
{"keywords": "cybersecurity","noticeTypes": ["o", "k", "r"],"lookbackDays": 30,"maxItems": 2000,"monitorName": "cyber-capture"}
Monitor names: get only new results
- With a monitor name (
monitorName, e.g."8a-it-daily"): the Actor saves a fingerprint of every notice this monitor returned, in a key-value store in your own Apify account. Later runs with the same name return only new or amended notices (changeTypenew or changed). Every matching notice in the window is compared (keyless: up to 10,000 newest; API key: the first 1,000, ormaxItemsif larger);maxItems(default 1,000) caps how many new or changed notices one run returns, and the rest come on the next run. Use one name per filter set; a new name starts fresh. Use this for schedules, n8n, Make, Zapier and Sheets. - Without a monitor name (the default): no records are written (one exception for older users, below). Each run returns the newest notices posted in the last
lookbackDaysdays, up to 25 (setmaxItemsto change it), withchangeTypeempty. Good for a first look or a one-off search. The run's status message reminds you: "Set a monitor name to get only new results." - Existing users without a monitor name keep their tracking. If you ran this Actor without a monitor name before stateless became the default (version 0.2.7 or earlier), your saved state for that filter set is still used: those runs keep returning, and charging, only new or changed notices (default
maxItems1,000, as before). The log names the monitor name that already holds that state, for example: "Stateless (latest rows) is now the default for runs without a monitor name; your existing saved state is kept. Set monitorName "07f8910e3b79" to keep it explicitly." (the status message has a short form). Set exactly thatmonitorName(case-sensitive) and the run uses the same saved state: nothing is re-sent or re-charged. Any other new name starts fresh (its first run returns and charges all matching notices once). Only runs with no saved state for their filters are stateless; they write no records (on a brand-new account an empty storage may still be created). - Exception: runs with no filters at all, and the Store's example input (an empty input, an empty
monitorNamewith no filters, or the example input NAICS 236220) are always stateless, even if you have saved state from 0.2.7 or earlier for them. They return the newest notices as above and never open, read or write the saved-state store at all (since 0.2.19 they don't even check whether such state exists, so the log no longer names it). If you used this Actor that way in 0.2.7 or earlier, your saved state is kept under a fixed monitor name:"07f8910e3b79"for runs with no filters,"c2a2d3548358"for NAICS 236220 only. Set thatmonitorNameto pick it up with nothing re-sent or re-charged. - You're charged per new or changed notice returned (and per notice in stateless runs), so a scheduled run without a monitor name pays again for notices it already returned. With a monitor name and
emitUnchangedon, notices returned only because they're unchanged (changeType: "unchanged") are free; the status message shows how many.
What is saved (state keys)
Only runs with a monitor name (or an older no-name run that still uses its saved state, see above) write anything. They use one key-value store in your own account, samgov-radar-state, with these records per monitor (<name> = the monitor name; for older no-name state, a 12-character code such as 07f8910e3b79):
FP2-STATE_<name>: the monitor's saved state (fingerprints of the notices it has seen).FP2B-STATE_<name>(since 0.2.19): a second copy of the same record, written right after it, so a late write from a run that lost its monitor can't undo newer progress. Same size asFP2-STATE_<name>. Older versions ignore it.STATE_<name>: the same state in the format of versions 0.2.7 and older (kept so you can switch back).PENDING-STATE_<name>!<run ID>(since 0.2.21;PENDING-STATE_<name>in older versions): each run's copy of the batch it is delivering, cleared once it's pushed (left behind only by an interrupted run; the next run re-sends it free of charge and deletes it). A monitor can have several of these after interrupted runs.LOCK-STATE_<name>: the monitor's lock while a run works (removed when the run ends; expires after 10 minutes without a heartbeat). Monitor names with leading or trailing spaces also keep their pre-0.2.8STATE_record. Deleting a monitor's records makes its next run start fresh (all matching notices are returned and charged once); delete all of them together, and never while a run on that monitor is active.
How change detection works (and its limits)
With a monitor name, each notice gets a fingerprint of these fields: notice ID, title, solicitation number, notice type, posted date, response deadline date, set-aside code, primary NAICS code, PSC code, and award number, date and amount. Values are normalized so keyless mode and API-key mode give the same fingerprint for the same notice (checked on 999 live notices: 999 identical). A notice comes back as changed (and is charged) only when one of these fields changes. Archiving, archive-date edits, agency or office renames, link formats, descriptions and attachments are not changes.
Known limits:
- Only date-level deadline changes and wording changes count. A response deadline moved to another time on the same date, or a title edit that only changes capitalization, punctuation or accented/special characters, is not reported as changed.
- Upgrade or downgrade gap. When a monitor first runs on a new fingerprint version (or after going back to an older build and returning), notices it already knew are re-baselined silently: not returned, not charged. A real amendment published exactly in that gap is absorbed once and not reported.
- Interrupted runs. Results are saved, pushed and charged in batches, and the monitor's memory is saved before each batch is pushed and charged, so no notice is ever charged twice. Before each batch is saved, the Actor also keeps a copy of it as "pending" and clears it once the batch is pushed. A graceful stop lets the batch in progress finish its push and charge before the run ends: a graceful abort (the Abort run API with
gracefully=true; Apify gives the run 30 seconds), a stop signal or the run-timeout guard (below). The run waits for that batch until just before its run timeout. The default Abort button in Apify Console is a forced, immediate abort, just like an out-of-memory crash or a run timeout. Such a stop gives the run no time to finish anything. If it lands between a batch's save and its push, the next run on that monitor re-sends that batch free of charge, and so does any batch a graceful abort cuts off after its 30 seconds. A forced stop right after the push can make that free re-send a duplicate of rows you already have, and a stop before the charge leaves the batch uncharged. Either way, no notice is ever charged twice. What to expect from re-sent rows:- They can be shortened. A re-sent row is the pending copy, and that copy has to fit in one 8 MB record. For a very large batch, long descriptions in the copy are cut short (they end in "…"). In extreme cases other long fields are cut too, or the row keeps only its key fields (notice ID, title, dates, agency, NAICS, set-aside, link) and gets
pendingShortened: true. The rows delivered by the original run, when it got that far, are always complete. - They can come late. The pending copy doesn't expire. It's re-sent by the next run on that monitor, whenever that is, for example days later, or after you switch to an older version and back.
- They can fall outside your current filters. They were picked with the filters of the interrupted run. If you've changed the monitor's filters since, the re-sent rows are still delivered once, because they were already recorded as delivered.
- They can be shortened. A re-sent row is the pending copy, and that copy has to fit in one 8 MB record. For a very large batch, long descriptions in the copy are cut short (they end in "…"). In extreme cases other long fields are cut too, or the row keeps only its key fields (notice ID, title, dates, agency, NAICS, set-aside, link) and gets
- One run per monitor at a time. A run with a monitor name locks that monitor while it works. So does a run without a monitor name that still uses saved state from 0.2.7 or earlier for its filters (see "Monitor names"); all other runs without a monitor name never lock anything. If you start a second run on the same monitor (for example a manual run during a scheduled one), the second run stops right away with a "Locked" status message and returns and charges no notices, so nothing is delivered twice. It still pays the Actor start charge ($0.01 per GB of run memory, at least $0.01), like every run. A monitor run also stops itself 15 seconds before its run timeout (a third of the time left for very short timeouts), releases the lock and ends as failed with a clear message; notices already returned are saved and the next run continues. A graceful abort (the Abort run API with
gracefully=true) or a stop signal releases the lock before the run ends. The default forced abort, an out-of-memory crash or a container kill gives the run no time to release it, so it can block the monitor for up to 10 minutes; runs started in that time stop as "Locked" and charge no notices. - Switching between keyless and API-key mode. The keyless nightly extract can be up to ~24 h behind the API. An amendment API-key mode already returned is not returned or charged again when a keyless run still sees the older version, or when the extract catches up (for 7 days after API-key mode returned it). One exception: a notice whose first-ever appearance was in API-key mode, already amended, can come back once as
changed(showing the older text) if you switch to keyless before the extract has the amendment. The reverse also applies: if a notice is reverted, within those 7 days, to the content it had before the amendment API-key mode returned, and only keyless runs see the revert, the revert is not reported. - Memory cleanup. To keep saved state small, a monitor forgets notices posted more than a year ago, and notices whose response deadline passed more than 90 days ago that it hasn't seen for 90 days. Above a hard cap of 50,000 notices it forgets closed notices (deadline passed) first, then notices without a deadline, and open notices only as a last resort (the run log warns when that happens). If a forgotten notice shows up again later (for example after you widen your filters), it is returned and charged once as new.
SAM.gov API rate limits: how this avoids them
- Keyless mode (default) makes no SAM.gov API calls. It downloads SAM.gov's public nightly extract once per run, so there is no daily quota to hit.
- With your own key, search results come 1,000 per page, so a typical daily alert uses only a few requests. Description lookups are off by default (
maxDescriptionFetches= 0) because SAM.gov gives non-federal keys without a role only 10 requests/day (1,000/day with a role). - Failed requests are retried with exponential backoff instead of hammering the API.
Export SAM.gov opportunities to Google Sheets, Slack or n8n
Schedule the Actor (for example daily) in Apify Console with a monitorName, then add an integration: Apify's built-in Google Sheets, Slack, Zapier and Make integrations, or the Apify node in n8n. With a monitor name, each run sends only new or amended notices, so the sheet or channel gets no duplicates.
Notice type codes
o = Solicitation, p = Presolicitation, k = Combined Synopsis/Solicitation, r = Sources Sought, s = Special Notice, a = Award, u = J&A, g = Sale of Surplus, i = Intent to Bundle.
Set-aside codes
SBA (Total Small Business), SBP (Partial SB), 8A / 8AN (8(a)), HZC / HZS (HUBZone), SDVOSBC / SDVOSBS (Service-Disabled Veteran-Owned), WOSB / WOSBSS / EDWOSB / EDWOSBSS (Women-Owned), VSA / VSS (Veteran).
FAQ
Do I need a SAM.gov API key? No. Without a key the Actor uses SAM.gov's official public nightly extract, which can be up to 24 hours old. A free key (sam.gov → Account Details → Public API Key) gives you real-time results.
How does "only new or changed" work? Each monitorName keeps a fingerprint of every notice it has seen. A notice is returned again only if it's amended: its title, solicitation number, notice type, posted date, response deadline date, set-aside, NAICS, PSC or award number/date/amount changed. Archiving, agency or office renames, links, and switching between keyless and API-key mode don't count as changes and aren't charged (an amendment is charged once, even though the keyless extract shows it up to ~24 h after the API; see the one rare exception under "Known limits"). Turn on resetState to start fresh. Without a monitor name no records are written and there is no change tracking (except for filter sets that already had saved state from version 0.2.7 or earlier; see "Monitor names"): each run returns the newest notices in the lookback window (25 by default), and every returned notice is charged; see "Monitor names" above.
Can I run several searches? Yes. Give each filter set its own monitorName.
Do I have to log in to SAM.gov to see the details? No. Each row already has the title, type, agency, deadlines, NAICS, set-aside, place of performance and the description (in keyless mode; with an API key, opt in with maxDescriptionFetches). samUrl links to the notice if you want the attachments.
Does it include contracting officer emails or phone numbers? No, by design. Use the samUrl link to see contacts on SAM.gov.
How much does it cost? $0.01 per run plus $4 per 1,000 opportunities returned, and $2 per 1,000 AI tags if you turn them on. With a monitor name only new or changed notices are returned and charged, so a daily alert with 20 new notices costs about $0.09. Without a monitor name every returned notice is charged: a default run (25 notices) costs about $0.11, also when it repeats notices from earlier runs.
Can I use it from an AI agent? Yes. Call it through the Apify MCP server or API. The output is flat JSON with stable fields such as noticeId, title, responseDeadline and samUrl.
Is this affiliated with SAM.gov or GSA? No. It's an independent tool that uses SAM.gov's official public data.
Two data modes
| Keyless (default when no key) | API key (recommended for real-time) | |
|---|---|---|
| Source | Official SAM.gov public nightly extract (Data Services) | SAM.gov Get Opportunities Public API |
| Freshness | Up to 24h old (file regenerated nightly) | Real-time |
| Setup | None | Free key: sam.gov > Account Details > Public API Key |
| Daily quota | None from SAM.gov | Your key's daily limit |
If you supply samApiKey, the Actor always uses the API. Official public nightly extract, up to 24h old; add a free SAM.gov API key for real-time results.
We never scrape SAM.gov web pages. Keyless mode uses only the public bulk extract that SAM.gov publishes for download.
Field differences in keyless mode
awardeeUeiis always empty (not in the extract).naicsCodes(secondary NAICS list) is empty;naicsCode(primary) is present.popCityhas the name only, no city code.agencyPathCodeis built from CGAC / FPDS / AAC codes and may differ from the API's code path;organizationCodefiltering is approximate.keywordsis a local case-insensitive title match (the API runs its own title search), so counts can differ slightly.- Covers notices currently in the extract (active notices, roughly the last year), latest version only.
Output
Same columns in both modes: noticeId, title, solicitationNumber, noticeType, baseType, active, postedDate, responseDeadline, archiveDate, department, subTier, office, agencyPathCode, naicsCode, pscCode, setAsideCode, setAside, office/place-of-performance location, award fields, samUrl, additionalInfoUrl, changeType, optional tag_* columns, plus description: the full notice text, so you don't need to log in to read it. No contact names, emails or phones, in either mode (emails, phone numbers and names after labels like "Point of Contact:" are removed from the description).
Full details and attachment keywords
includeDetails(default on). Keyless: the description comes from the nightly extract at no extra cost. With an API key: one SAM.gov request per returned notice, only if you opt in withmaxDescriptionFetches(default 0, so no descriptions in key mode unless you set it). Non-federal keys without a SAM.gov role get only 10 requests/day; with a role, 1,000/day.attachmentKeywords(optional, API key required). Downloads each returned notice's public attachments through the official APIresourceLinkswith your key, searches PDF and text files, and returnsattachmentKeywordHits(which keywords matched),attachmentsScannedandattachmentsSkipped. Attachment text is never returned. Capped bymaxAttachmentDownloads(default 10); each download counts toward your daily SAM.gov limit. Word/Excel files are skipped. Not available keyless: the public extract has no attachments, and we don't scrape SAM.gov pages.
Cost
A keyless test run with 512 MB memory and maxItems 10 took about 6 s and cost about $0.0003 in platform usage.