SAM.gov Contracts & Exclusions API - No Scraping, No Start Fee
Pricing
from $4.00 / 1,000 opportunity records
SAM.gov Contracts & Exclusions API - No Scraping, No Start Fee
Federal contract opportunities + exclusions via the official api.sam.gov REST API - not fragile site-scraping. FAR 52.209-6 batch subcontractor screening included (rare). No per-run start fee (others charge $0.10/run). Honest coverage disclosures - never a silent partial result.
Pricing
from $4.00 / 1,000 opportunity records
Rating
0.0
(0)
Developer
Moose & Raven
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
3 days ago
Last modified
Categories
Share
SAM.gov Contract Opportunities & Exclusions API (JSON)
Who this is for: GovCon business-development teams hunting new NAICS-matched contract opportunities, and prime contractors who have to clear every subcontractor against SAM.gov exclusions before award. This actor delivers both jobs from one normalized dataset - get alerted to new opportunities that match your NAICS codes, and screen your whole subcontractor list against exclusions before you sign, in one actor, one schema, one SAM.gov API key.
Worked example: Set naicsCodes: ["541511"] with a postedFrom/postedTo window and the
actor returns opportunity records for newly posted solicitations in that NAICS code - real
fields like title, noticeType, responseDeadline, setAsideType, and uiLink (the
human-readable SAM.gov page you'd hand to a bid team). Before awarding a subcontract, submit
batchScreenList: [{ "entityName": "Acme Corp" }] and get back one batch-screen-result per
entry: matched: false means clear to proceed, matched: true returns the full matched
exclusion record (excluding agency, exclusion type, dates) attached - so you can satisfy the
FAR 52.209-6 written-notification duty before you sign, without a second tool or a manual SAM.gov
lookup.
Search federal contract opportunities and screen against the SAM.gov Exclusions (debarment/
suspension) list - in one normalized, NAICS-filterable dataset, via the official api.sam.gov
REST API (bring your own API key). No scraping of SAM.gov's website. Optional NAICS-filtered
incremental monitoring keeps scheduled runs pulling only what's new, and a batch-screening mode
checks a whole vendor/subcontractor list against exclusions in one run.
1. What makes this different from other SAM.gov actors
We checked the leading SAM.gov actor in the Apify Store (154 users, 1.0-star rating from its own
reviewers) directly: its own published documentation states it hits "the SAM.gov frontend search
API" - an undocumented internal endpoint, not the official api.sam.gov. SAM.gov's Terms of Use
explicitly prohibit exactly that: "systematic access (electronic harvesting)... of the website"
is banned, and a detected violation gets the associated account denied SAM.gov access outright -
a real, standing risk for anyone relying on it, not just a theoretical ToS technicality. This actor
calls the official, sanctioned api.sam.gov REST API exclusively. It does not touch SAM.gov's
website, frontend or otherwise.
Three concrete differences from that competitor, not just "we're more careful":
- No per-run start fee. That competitor charges $0.10 the moment any run starts, before a single record is returned - a real cost on every test run or near-empty query. This actor charges only for records actually delivered; a run that finds nothing costs nothing.
- FAR 52.209-6 batch subcontractor screening. Submit a vendor list, get a matched/clear verdict
per entry in one run (see
batchScreenListbelow) - not offered by that competitor. - Coverage disclosures on every run, not just failures. Every real-key run ends with a
run-coveragerecord stating exactly what was checked this run (which datasets, which NAICS filter, which exclusions filter, how many identifiers screened) - not just a flag when something broke. Combined withrun-status/batch-screen-statusrecords on any truncation, a result is never silently partial. This is the structural fix for exactly the kind of thin/wrong-looking output that tends to earn a 1-star review.
The tradeoff is the official API's real limitation: 10 requests/day (unregistered), 1,000/day (registered non-federal), 10,000/day (federal system account), tracked per API key. This actor does not and cannot bypass that - it is not a "workaround." Its value is making the sanctioned, rate-limited API easy to run well: bundled opportunities + exclusions in one normalized schema, NAICS-code filtering, incremental/delta monitoring that spends your quota only on what's new since the last run, and honest failure/coverage reporting instead of silent partial results.
2. Why this matters for contract-award and subcontracting compliance
Federal Acquisition Regulation 9.405 requires contracting officers to check SAM.gov exclusions
before soliciting offers, awarding a contract, or consenting to a subcontract, absent a
compelling-reason determination - and to check again immediately before award. FAR 9.405-2(b) and
the FAR 52.209-6 clause extend a parallel duty to prime contractors: before subcontracting over
$45,000 (commercial off-the-shelf items are exempt), a prime must verify the subcontractor is not
excluded and must notify the contracting officer in writing before proceeding if it is. This
actor's batchScreenList mode (below) is built specifically for that prime-contractor-side check
- submit your subcontractor list, get a matched/clear verdict per entry.
3. How it works
- You bring your own SAM.gov API key (
samGovApiKeyinput). Register a free SAM.gov account and request a public API key from the Account Details page - no cost, no KYC, not affiliated with Apify. Each run consumes quota from your key only; this actor never pools multiple customers' usage behind a shared key, so your quota isn't affected by other users. Run it without a key first to see how it works: a keyless run succeeds and returns one informational record explaining what a real key unlocks, at no charge - no real SAM.gov query is made until you add one. - Fetches Opportunities (
GET /opportunities/v2/search) and/or Exclusions (GET /entity-information/v4/exclusions), paginated automatically. - Optional NAICS code filtering: the API accepts one NAICS code per request, so the actor issues one paginated request sequence per code you supply.
- Optional incremental monitor mode: on scheduled runs, remembers the last successful checkpoint (via a persistent named key-value store) and only requests opportunities posted / exclusions updated since then, instead of a full re-pull every time.
- A quota guard (
maxRecordsPerRun) caps total records fetched in a single run so a misconfigured input can't blow through your whole daily API allowance in one run. - Batch-screen a vendor/subcontractor list (
batchScreenListinput): submit an array of{ id?, ueiSAM?, cageCode?, entityName? }entries and get onebatch-screen-resultrecord back per entry - a real match (with the full matched exclusion record attached), a clean/no-match result, or an error (e.g. no identifier supplied).ueiSAM/cageCodeare precise lookups;entityNameis a fuzzy name search, flagged as such in the output viasearchMethod. Runs independently ofdatasets/monitorMode- a targeted per-entity check, not a bulk pull - and shares this run'smaxRequestsPerRunbudget with the opportunities/exclusions datasets (same daily SAM.gov quota). If the quota runs out mid-batch, abatch-screen-statusrecord names exactly which entries were NOT screened - never mistake a missing result for a cleared one.
4. Input
| Field | Type | Notes |
|---|---|---|
samGovApiKey | string (secret) | Bring your own SAM.gov API key - see How it works above. Optional: without one, the run succeeds and returns a single free api-key-required explainer record instead of querying SAM.gov |
datasets | array | "opportunities", "exclusions", or both - default both |
naicsCodes | array | One request sequence issued per code |
opportunityKeyword, placeOfPerformanceState, setAsideType | Opportunity-search filters, passed through to the API | |
postedFrom / postedTo | string | MM/DD/YYYY - required unless monitorMode is on; capped at a 1-year range by the API itself |
exclusionSearch | object | Filters for the exclusions dataset (e.g. exclusionType) |
batchScreenList | array | { id?, ueiSAM?, cageCode?, entityName? } entries - see How it works above |
monitorMode | boolean | Persistent incremental "new since last checkpoint" tracking |
maxRecordsPerRun | integer | Caps total records fetched across datasets |
maxRequestsPerRun | integer | Shared request budget across opportunities/exclusions/batch-screen - protects your daily SAM.gov quota from a misconfigured run |
See the Input tab for the full form with inline descriptions.
5. Output fields
Each dataset item has a recordType of "opportunity", "exclusion", "run-status" (emitted
only when a dataset's fetch was truncated or skipped this run), "batch-screen-result" (one per
batchScreenList entry), "batch-screen-status" (emitted only when the batch screen itself
was truncated by the request budget), "api-key-required" (emitted instead of any real data,
only on a run with no samGovApiKey - see below), or "run-coverage" (emitted once at the end
of every real-key run, stating what was actually covered - see below).
opportunity
| Field | Type | Notes |
|---|---|---|
noticeId, title, solicitationNumber | string | null | SAM.gov's own identifiers |
noticeType, baseType | string | null | e.g. "Sources Sought" |
active | string | null | "Yes"/"No", SAM.gov's own string encoding, not a boolean |
postedDate, responseDeadline, archiveDate | string | null | |
naicsCode, classificationCode | string | null | |
setAsideType, setAsideDescription | string | null | e.g. "SBA" / "Total Small Business Set-Aside (FAR 19.5)" |
organizationPath, organizationCode | string | null | Full agency hierarchy, e.g. "DEPT OF DEFENSE.DEPT OF THE ARMY.AMC..." |
placeOfPerformance | object | null | Nested address object, passed through as SAM.gov provides it |
pointOfContact | array | null | Nested contact objects |
award | object | null | Populated only for award-notice types |
descriptionLink, additionalInfoLink, uiLink, resourceLinks | string/array | null | uiLink is the human-readable SAM.gov page; descriptionLink is a further API call, not fetched by this actor |
fetchedAt, sourceApi | sourceApi is "api.sam.gov/opportunities/v2/search" |
exclusion
| Field | Type | Notes |
|---|---|---|
ueiSAM, cageCode | string | null | Present for entity exclusions, null for most individual exclusions |
entityName, individualName | string | null | For an individual exclusion, both are populated with the same name |
classificationType | string | null | "Individual" or an entity classification |
exclusionType, exclusionProgram | string | null | e.g. "Ineligible (Proceedings Complete)" / "Reciprocal" |
excludingAgencyName, excludingAgencyCode | string | null | |
activateDate, terminationDate, terminationType, recordStatus | string | null | terminationType: "Definite" pairs with a real terminationDate; some exclusions are indefinite |
createDate, updateDate | string | null | |
city, stateOrProvinceCode, countryCode | string | null | |
fetchedAt, sourceApi | sourceApi is "api.sam.gov/entity-information/v4/exclusions" |
batch-screen-result (one per batchScreenList entry, always)
| Field | Type | Notes |
|---|---|---|
inputId | string | null | Echoes your entry's id, or an auto-assigned "#0"-style index if omitted |
queriedUeiSAM, queriedCageCode, queriedEntityName | string | null | Echoes whichever identifier(s) you submitted |
searchMethod | string | null | "ueiSAM", "cageCode", or "entityName" - entityName is a fuzzy search, flagged as such here |
matched | boolean | null | null only when error is set (the entry couldn't be checked at all) |
matchCount | number | null | |
matches | array | null | Full matched exclusion record(s) attached when matched: true |
error | string | null | e.g. no identifier supplied - this entry was NOT checked |
checkedAt | string (ISO datetime) |
Status records (run-status, batch-screen-status) each carry a message explaining exactly
what was cut short and why - read it before treating an absence from the output as a clean result.
api-key-required (only when samGovApiKey is absent)
| Field | Type | Notes |
|---|---|---|
status | string | "NO_DATA_KEYLESS_RUN" |
message | string | Explains no SAM.gov query was made and how to get a free key |
detectedAt | string (ISO datetime) |
This is the only record a keyless run produces - no opportunity/exclusion/batch-screen-*
records are ever mixed in. The run still exits SUCCEEDED and is not charged; this is intentional
(see "Running it without a key" below), not an error state.
run-coverage (once per real-key run, always - free, never charged)
| Field | Type | Notes |
|---|---|---|
datasetsRequested | array | Which of "opportunities"/"exclusions" this run actually requested |
naicsCodesFilter | array | null | NAICS codes this run filtered opportunities by, or null if unfiltered (all codes) |
opportunitiesWindow | object | null | The postedFrom/postedTo window actually used, or null if opportunities wasn't requested |
exclusionsFilterDescription | string | null | Human-readable description of the exclusions filter actually applied (or "no filter - full active exclusions list...") |
batchScreenCount | number | How many identifiers were submitted for batch screening |
monitorMode | boolean | Whether this was an incremental monitor-mode run |
message | string | Prose summary of the above |
detectedAt | string (ISO datetime) |
This is a scope statement, not a result - it says what was attempted, not whether it
completed. Read it alongside any run-status/batch-screen-status records to know both what
this run covered and whether that coverage was cut short. Unlike the failure-only run-status/
batch-screen-status records, run-coverage is emitted on every real-key run, including a
completely clean one - so a result set is never ambiguous about its own scope.
Real sample output
Live-fetched (NAICS 541511, January 2026) - not a fabricated example:
{"recordType": "opportunity","noticeId": "6f6b4d02f80441d4a59781e40d1621c1","title": "Construction Cost Estimating Software Licenses ","solicitationNumber": "W911S226QBOOK","noticeType": "Sources Sought","baseType": "Sources Sought","active": "Yes","postedDate": "2026-01-06","responseDeadline": "2026-01-16T10:00:00-05:00","archiveDate": "2027-01-06","naicsCode": "541511","classificationCode": "7A21","setAsideType": "SBA","setAsideDescription": "Total Small Business Set-Aside (FAR 19.5)","organizationPath": "DEPT OF DEFENSE.DEPT OF THE ARMY.AMC.ACC.MISSION INSTALLATION CONTRACTING COMMAND.419TH CSB.W6QM MICC-FT DRUM","organizationCode": "021.2100.AMC.ACC.MICC.419TH CSB.W911S2","placeOfPerformance": { "streetAddress": "", "zip": "13602" },"pointOfContact": [{ "fax": "", "type": "primary", "email": "cody.a.bresette.civ@army.mil", "phone": "3157723397", "title": null, "fullName": "Cody Bresette" }],"award": null,"descriptionLink": "https://api.sam.gov/prod/opportunities/v1/noticedesc?noticeid=6f6b4d02f80441d4a59781e40d1621c1","additionalInfoLink": null,"uiLink": "https://sam.gov/workspace/contract/opp/6f6b4d02f80441d4a59781e40d1621c1/view","resourceLinks": null,"fetchedAt": "2026-07-18T01:40:08.487Z","sourceApi": "api.sam.gov/opportunities/v2/search"}
{"recordType": "exclusion","ueiSAM": null,"cageCode": null,"entityName": "John Billy Donnahoo","individualName": "John Billy Donnahoo","classificationType": "Individual","exclusionType": "Ineligible (Proceedings Complete)","exclusionProgram": "Reciprocal","excludingAgencyName": "JUSTICE, DEPARTMENT OF","excludingAgencyCode": "DOJ","activateDate": "03-31-2026","terminationDate": "03-31-2227","terminationType": "Definite","recordStatus": "Active","createDate": "05-05-2026","updateDate": "05-05-2026","city": "McMinnville","stateOrProvinceCode": "OR","countryCode": "USA","fetchedAt": "2026-07-18T01:41:14.302Z","sourceApi": "api.sam.gov/entity-information/v4/exclusions"}
{"recordType": "batch-screen-result","inputId": "#0","queriedUeiSAM": null,"queriedCageCode": null,"queriedEntityName": "nonexistent test vendor xyz","searchMethod": "entityName","matched": false,"matchCount": 0,"matches": [],"error": null,"checkedAt": "2026-07-18T01:40:44.667Z"}
A matched: false, zero-match clean result is the honest, expected shape for a real vendor with
no exclusions on file - the same "confirmed clean, not just absent" principle this fleet's other
screening actors apply.
6. Use cases
- Contracting officers & acquisition teams - check SAM.gov exclusions before soliciting, awarding, or consenting to a subcontract, per FAR 9.405.
- Prime contractors -
batchScreenListyour subcontractor roster before award, satisfying the FAR 52.209-6 written-notification duty for subcontracts over $45,000. - GovCon business development - monitor NAICS-filtered opportunity feeds for new solicitations
in your capability area, with
monitorModedelivering only what's new since your last check. - Compliance-SaaS platforms - embed federal opportunity + exclusion data into an existing GovCon workflow tool via one normalized feed instead of two separate API integrations.
- Legal / due diligence - screen an acquisition target's own federal contracting exposure and exclusion history before closing.
7. Setting up monitoring alerts (Apify Schedules + your own notification channel)
This actor produces structured data; wiring that into an email/Slack/Teams alert is done with Apify's own platform features, not actor-specific code:
- Create an Apify Schedule pointing at this actor, with
monitorMode: trueand yournaicsCodes/datasetsset. Daily is typical, and respects your API key's daily quota better than more frequent polling. - Attach a Webhook to the schedule/actor for the
ACTOR.RUN.SUCCEEDEDevent. Apify's webhook payload includes the run's dataset ID, so your webhook target can fetch the new items. - Route the webhook to Slack/Teams via their native "incoming webhook" URL, or to email via
a lightweight relay (e.g. a Zapier/Make webhook-to-email step) - since
monitorModeonly delivers genuinely new opportunities/exclusions since the last checkpoint, every triggered notification represents real new information. - Because the dataset always distinguishes
opportunity/exclusionrecords fromrun-statusstatus records, your alert logic can (and should) treat aTRUNCATED/SKIPPEDstatus as "monitoring degraded this run" - e.g. your daily API quota ran out - rather than silently trusting a thin result as complete.
8. Pricing
Pay per event (current live pricing - confirmed via the Apify API at time of writing):
opportunity-record- $0.004 per contract opportunity deliveredexclusion-record- $0.003 per exclusion (debarment/suspension) record deliveredbatch-screen-record- $0.005 per identifier screened inbatchScreenListmode (charged per identifier submitted, not per match found)monitor-run- $0.05 per scheduled monitor-mode run, in addition to per-record charges
No per-run start fee. A run that finds nothing (a narrow filter, an empty batch result, a keyless test run) costs nothing beyond what it actually returns - unlike at least one other SAM.gov actor in the Store, which charges a flat fee the moment any run starts regardless of what it finds.
See the actor's Pricing tab for the authoritative, current numbers.
9. Use from Claude, Cursor, and other AI agents (MCP)
This actor can be called as a tool by any MCP-compatible AI client (Claude Desktop, Cursor, VS Code, etc.) via Apify's hosted MCP server, without any actor-specific integration code:
- Point your MCP client at
https://mcp.apify.com, authenticated with your Apify API token (as a bearer header or via OAuth - see Apify's MCP docs for client-specific config). - Expose this actor specifically with the
toolsquery parameter:https://mcp.apify.com?tools=mooseandraven/samgov-federal-contracting-suite - Your agent can then call it directly - e.g. "check if Acme Corp is excluded from federal
contracting" or "find open Sources Sought notices under NAICS 541511" maps naturally onto
batchScreenListornaicsCodes/opportunityKeyword. You'll need your own SAM.gov API key either way (see How it works above) - this actor doesn't provide one. The actor's own input schema (this page's Input tab) is what the MCP tool schema is built from, so any filter documented here is available to the agent.
10. FAQ
Do I need a SAM.gov API key? For real data, yes - this is the one actor in this fleet that
requires bringing your own key. Register a free SAM.gov account and request a Public API Key from
your Account Details page. No cost, no KYC beyond SAM.gov's own registration. You can run the
actor without one first: it succeeds and returns a single api-key-required record explaining
what a real key unlocks, at no charge - no SAM.gov query is made until you add one.
Why does my key stop working after a while? SAM.gov's own public API keys are time-limited
(~89 days, confirmed empirically) and must be periodically regenerated via
sam.gov/profile/details - this is SAM.gov's own key-expiry policy, not something this actor
controls. Treat a key-expiry error as a real, expected event to plan around, not a bug.
My api.data.gov key doesn't work - why? api.data.gov and api.sam.gov are separate key
systems (confirmed empirically) - an api.data.gov-issued key will not authenticate against
api.sam.gov. You need a key issued directly from your SAM.gov profile.
What's the pricing/subscription model? Pay-per-event, not a flat subscription - see Pricing above. Note your own SAM.gov API quota (10/1,000/10,000 requests per day depending on your account tier) is a separate, real constraint this actor respects but cannot raise.
Why do I sometimes get a run-status or batch-screen-status record instead of just data?
This actor deliberately surfaces its own limits instead of hiding them - see Output fields above.
A capped/skipped pull (often your own daily SAM.gov quota running out) is never silently reported
as complete.
11. What it does NOT do / Limitations
- Wage determinations are NOT included. SAM.gov publishes Davis-Bacon/SCA wage determinations
only as individual web pages (e.g.
sam.gov/wage-determination/{id}/{revision}) with no official API or bulk-download endpoint. Building that would require scraping the website, which SAM.gov's Terms of Use prohibit. This is a deliberate scope cut, not an oversight. - The Exclusions API's synchronous JSON endpoint pages at 10 records/page, 10,000 records total.
Pulling a large exclusions result set burns quota fast (100 requests per 1,000 records). Narrow
your
exclusionSearchfilters for anything beyond a small pull. - Date-range requests are capped at 1 year by the API itself (
postedFrom/postedTo), andpostedFrom/postedToare required unlessmonitorModeis on. - D&B-sourced fields: some exclusion identification fields are Dun & Bradstreet-derived, which SAM.gov's terms restrict from bulk redistribution outside authorized use. This actor surfaces those fields for the requesting user's own compliance/vetting use only.
- This actor requires your own API key and is bound by YOUR key's daily quota - it cannot pool quota across users or bypass SAM.gov's own rate limits, by design (see What makes this different above).
Local development
npm installnpm test # recorded-fixture unit tests, no API key needednpx apify-cli run # local end-to-end run - requires a real samGovApiKey in# storage/key_value_stores/default/INPUT.json
Support
Built and maintained by Moose & Raven. Questions or issues: support@mooseandraven.com.