SAM.gov Contracts & Exclusions API - No Scraping, No Start Fee avatar

SAM.gov Contracts & Exclusions API - No Scraping, No Start Fee

Pricing

from $4.00 / 1,000 opportunity records

Go to Apify Store
SAM.gov Contracts & Exclusions API - No Scraping, No Start Fee

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

Moose & Raven

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

3 days ago

Last modified

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 batchScreenList below) - not offered by that competitor.
  • Coverage disclosures on every run, not just failures. Every real-key run ends with a run-coverage record 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 with run-status/batch-screen-status records 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

  1. You bring your own SAM.gov API key (samGovApiKey input). 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.
  2. Fetches Opportunities (GET /opportunities/v2/search) and/or Exclusions (GET /entity-information/v4/exclusions), paginated automatically.
  3. Optional NAICS code filtering: the API accepts one NAICS code per request, so the actor issues one paginated request sequence per code you supply.
  4. 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.
  5. 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.
  6. Batch-screen a vendor/subcontractor list (batchScreenList input): submit an array of { id?, ueiSAM?, cageCode?, entityName? } entries and get one batch-screen-result record 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/cageCode are precise lookups; entityName is a fuzzy name search, flagged as such in the output via searchMethod. Runs independently of datasets/monitorMode - a targeted per-entity check, not a bulk pull - and shares this run's maxRequestsPerRun budget with the opportunities/exclusions datasets (same daily SAM.gov quota). If the quota runs out mid-batch, a batch-screen-status record names exactly which entries were NOT screened - never mistake a missing result for a cleared one.

4. Input

FieldTypeNotes
samGovApiKeystring (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
datasetsarray"opportunities", "exclusions", or both - default both
naicsCodesarrayOne request sequence issued per code
opportunityKeyword, placeOfPerformanceState, setAsideTypeOpportunity-search filters, passed through to the API
postedFrom / postedTostringMM/DD/YYYY - required unless monitorMode is on; capped at a 1-year range by the API itself
exclusionSearchobjectFilters for the exclusions dataset (e.g. exclusionType)
batchScreenListarray{ id?, ueiSAM?, cageCode?, entityName? } entries - see How it works above
monitorModebooleanPersistent incremental "new since last checkpoint" tracking
maxRecordsPerRunintegerCaps total records fetched across datasets
maxRequestsPerRunintegerShared 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

FieldTypeNotes
noticeId, title, solicitationNumberstring | nullSAM.gov's own identifiers
noticeType, baseTypestring | nulle.g. "Sources Sought"
activestring | null"Yes"/"No", SAM.gov's own string encoding, not a boolean
postedDate, responseDeadline, archiveDatestring | null
naicsCode, classificationCodestring | null
setAsideType, setAsideDescriptionstring | nulle.g. "SBA" / "Total Small Business Set-Aside (FAR 19.5)"
organizationPath, organizationCodestring | nullFull agency hierarchy, e.g. "DEPT OF DEFENSE.DEPT OF THE ARMY.AMC..."
placeOfPerformanceobject | nullNested address object, passed through as SAM.gov provides it
pointOfContactarray | nullNested contact objects
awardobject | nullPopulated only for award-notice types
descriptionLink, additionalInfoLink, uiLink, resourceLinksstring/array | nulluiLink is the human-readable SAM.gov page; descriptionLink is a further API call, not fetched by this actor
fetchedAt, sourceApisourceApi is "api.sam.gov/opportunities/v2/search"

exclusion

FieldTypeNotes
ueiSAM, cageCodestring | nullPresent for entity exclusions, null for most individual exclusions
entityName, individualNamestring | nullFor an individual exclusion, both are populated with the same name
classificationTypestring | null"Individual" or an entity classification
exclusionType, exclusionProgramstring | nulle.g. "Ineligible (Proceedings Complete)" / "Reciprocal"
excludingAgencyName, excludingAgencyCodestring | null
activateDate, terminationDate, terminationType, recordStatusstring | nullterminationType: "Definite" pairs with a real terminationDate; some exclusions are indefinite
createDate, updateDatestring | null
city, stateOrProvinceCode, countryCodestring | null
fetchedAt, sourceApisourceApi is "api.sam.gov/entity-information/v4/exclusions"

batch-screen-result (one per batchScreenList entry, always)

FieldTypeNotes
inputIdstring | nullEchoes your entry's id, or an auto-assigned "#0"-style index if omitted
queriedUeiSAM, queriedCageCode, queriedEntityNamestring | nullEchoes whichever identifier(s) you submitted
searchMethodstring | null"ueiSAM", "cageCode", or "entityName" - entityName is a fuzzy search, flagged as such here
matchedboolean | nullnull only when error is set (the entry couldn't be checked at all)
matchCountnumber | null
matchesarray | nullFull matched exclusion record(s) attached when matched: true
errorstring | nulle.g. no identifier supplied - this entry was NOT checked
checkedAtstring (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)

FieldTypeNotes
statusstring"NO_DATA_KEYLESS_RUN"
messagestringExplains no SAM.gov query was made and how to get a free key
detectedAtstring (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)

FieldTypeNotes
datasetsRequestedarrayWhich of "opportunities"/"exclusions" this run actually requested
naicsCodesFilterarray | nullNAICS codes this run filtered opportunities by, or null if unfiltered (all codes)
opportunitiesWindowobject | nullThe postedFrom/postedTo window actually used, or null if opportunities wasn't requested
exclusionsFilterDescriptionstring | nullHuman-readable description of the exclusions filter actually applied (or "no filter - full active exclusions list...")
batchScreenCountnumberHow many identifiers were submitted for batch screening
monitorModebooleanWhether this was an incremental monitor-mode run
messagestringProse summary of the above
detectedAtstring (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 - batchScreenList your 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 monitorMode delivering 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:

  1. Create an Apify Schedule pointing at this actor, with monitorMode: true and your naicsCodes/datasets set. Daily is typical, and respects your API key's daily quota better than more frequent polling.
  2. Attach a Webhook to the schedule/actor for the ACTOR.RUN.SUCCEEDED event. Apify's webhook payload includes the run's dataset ID, so your webhook target can fetch the new items.
  3. 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 monitorMode only delivers genuinely new opportunities/exclusions since the last checkpoint, every triggered notification represents real new information.
  4. Because the dataset always distinguishes opportunity/exclusion records from run-status status records, your alert logic can (and should) treat a TRUNCATED/SKIPPED status 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 delivered
  • exclusion-record - $0.003 per exclusion (debarment/suspension) record delivered
  • batch-screen-record - $0.005 per identifier screened in batchScreenList mode (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:

  1. 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).
  2. Expose this actor specifically with the tools query parameter: https://mcp.apify.com?tools=mooseandraven/samgov-federal-contracting-suite
  3. 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 batchScreenList or naicsCodes/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 exclusionSearch filters for anything beyond a small pull.
  • Date-range requests are capped at 1 year by the API itself (postedFrom/postedTo), and postedFrom/postedTo are required unless monitorMode is 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 install
npm test # recorded-fixture unit tests, no API key needed
npx 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.