SAM.gov Opportunities Scraper avatar

SAM.gov Opportunities Scraper

Pricing

from $1.79 / 1,000 results

Go to Apify Store
SAM.gov Opportunities Scraper

SAM.gov Opportunities Scraper

Collect public SAM.gov opportunities and return structured records with titles, notice types, solicitation numbers, agencies, dates, NAICS codes, and contact details. Search by multiple keywords, set a modification-date lookback, and use scheduled runs to emit only new or changed notices.

Pricing

from $1.79 / 1,000 results

Rating

0.0

(0)

Developer

Axiom Works

Axiom Works

Maintained by Community

Actor stats

0

Bookmarked

1

Total users

1

Monthly active users

11 days ago

Last modified

Categories

Share

Returns: one row per SAM.gov opportunity with title, noticeType, solicitationNumber, agency, modifiedDate, naicsCode and contactEmail. Input: {"keywords":["cybersecurity"],"postedWithinDays":30,"maxItems":20} Best for: contract opportunity monitoring, procurement research, and bid pipeline review.

{"id": "351fd0d0842f411fadbe22471214399a", "title": "INDUSTRY DAY: Fort Carson Equipment Concentration Site - Construction Management at Risk using Other Transaction Authority (CM@Risk w/OTA)", "noticeType": "Special Notice", "solicitationNumber": "W912QR-IndustryDay-FortCarsonECS-CMARwithOTA", "agency": "DEPT OF DEFENSE", "naicsCode": "236220", "modifiedDate": "2026-10-13T21:29:50+00:00"}

What does SAM.gov Opportunities Scraper do?

SAM.gov Opportunities Scraper collects public federal contracting notices using keyword searches. It returns structured records for reviewing opportunities, comparing agencies, and tracking changed notices without opening the search page every day. This SAM.gov scraper reads the public website's search and opportunity detail endpoints. No SAM.gov login or official SAM.gov API key is required.

Search results are requested in modification date order. The Actor retrieves details for each matching notice, applies any NAICS filter, and saves qualifying records to an Apify dataset. Multiple search terms share one overall result limit. Notices that appear under more than one term are emitted once per run, using their stable opportunity IDs.

This tool is independently developed and is not affiliated with or endorsed by SAM.gov, the General Services Administration, or any contracting agency. The dataset represents what the public service returned at collection time. It is a research input, not a determination of eligibility or an assurance that a procurement remains open.

What data can you get?

Each dataset row describes one opportunity. Fields that the source does not supply are null. Titles and dates come from search records, while classification and contact information come from opportunity details. The following table lists all output fields so consumers can build a predictable import mapping.

FieldMeaning
idStable SAM.gov opportunity identifier
sourceUrlPublic detail API endpoint used for the record
titleOpportunity title as published
noticeTypeNotice type label, such as Solicitation
solicitationNumberPublished solicitation reference
agencyTop-level department name in the hierarchy
subAgencySecond-level organization name
officeContracting office name, or deepest available organization
publishDatePublication date from the search response
modifiedDateModification date from the search response
responseDateResponse deadline when published
naicsCodePrimary NAICS code, or first available NAICS code
classificationCodeProduct or service classification code
setAsidePublished set-aside code when available
placeOfPerformanceAddress components joined into a readable string
contactNamePrimary point of contact name, or first listed contact
contactEmailEmail address from that same contact
contactPhonePublished phone for that same contact, or null
archiveDateDate SAM.gov archives the notice (YYYY-MM-DD), or null
descriptionPublished description body, possibly containing HTML
placeOfPerformanceCityPublished work-location city name, or null
placeOfPerformanceStatePublished work-location state code, or null
placeOfPerformanceCountryPublished work-location country code, or null

Keep identifiers and codes as text to preserve leading zeros. Dates retain source formatting and offsets. Sanitize description markup before displaying it in an application.

How to use

To focus a capture pipeline, set agencyNames to one or more organization-name fragments, such as ["DEFENSE"]. Matching is case-insensitive and checks every organization in the published hierarchy, including offices. Alternatives within the list are combined with OR; this filter and every other selected filter must all match. Agency filtering happens before detail retrieval, avoiding detail requests for unrelated organizations.

Use placeOfPerformanceStates with published state codes, such as ["CO"], to find work in a specific location. This filter checks the work location after detail retrieval, rather than the contracting office address. A missing work-location state cannot match a selected state. The separate placeOfPerformanceCity, placeOfPerformanceState and placeOfPerformanceCountry columns let you group or route opportunities without parsing the existing readable placeOfPerformance string. Default empty lists keep the previous search behavior.

Open the Actor, enter one or more keywords, choose your lookback window, and run it. Start with a modest result limit and inspect the dataset before scheduling repeated collection. Once your search matches the work you need, save the input as an Apify task and configure an appropriate schedule in Apify Console.

The lookback window uses modification time, rather than the original publication date. A notice first published months ago can qualify if its deadline or description changed recently. Active search records can still contain deadlines in the past, so compare responseDate separately when building a bid pipeline. The tool does not decide whether a deadline extension or amendment makes a notice actionable.

For monitoring, enable onlyNewSinceLastRun. The Actor stores emitted IDs and their modification timestamps in the named key-value store sam-gov-opportunities-monitor. A later run suppresses an unchanged ID and emits it again when its modification timestamp changes. Only successfully emitted records update that state; records beyond maxItems remain eligible for a later run.

Monitoring tasks share history in the named store. Disable onlyNewSinceLastRun for a complete view. Avoid concurrent monitoring runs because state updates can overwrite each other.

Input

ArgumentDefaultHow to choose it
keywordsRequiredArray of search terms; one search is made for each term
noticeTypesEmpty arrayRestrict results to selected notice categories
naicsCodesEmpty arrayMatch primary NAICS codes from detail records
pscCodesEmpty arrayMatch classificationCode against exact PSC codes
setAsideCodesEmpty arrayMatch setAside against exact eligibility codes
agencyNamesEmpty arrayMatch organization-name fragments, case-insensitive
placeOfPerformanceStatesEmpty arrayMatch exact published work-location state codes
postedWithinDays7Modification lookback, from 1 to 3650 days
onlyNewSinceLastRunfalsePersist emitted IDs and suppress unchanged notices
maxItems50Overall maximum, from 1 to 10000 results
proxyConfigurationApify proxy enabledDatacenter proxy with retries on fresh sessions; switches to residential IPs by itself if SAM.gov keeps blocking datacenter requests
{
"keywords": ["cybersecurity"],
"noticeTypes": ["Solicitation", "Sources Sought"],
"naicsCodes": [],
"pscCodes": [],
"setAsideCodes": [],
"agencyNames": [],
"placeOfPerformanceStates": [],
"postedWithinDays": 30,
"onlyNewSinceLastRun": false,
"maxItems": 20,
"proxyConfiguration": {"useApifyProxy": true}
}

Accepted notice types are Presolicitation, Solicitation, Combined Synopsis/Solicitation, Sources Sought, Special Notice, Award Notice, Intent to Bundle Requirements, Justification, and Sale of Surplus Property. Names are matched without regard to capitalization. Leaving noticeTypes empty requests all supported categories from the search service.

NAICS, PSC, and set-aside filtering happens after detail retrieval, because the detail response supplies the primary code. Details are fetched in batches of up to five, capped by the remaining result limit. This avoids fetching surplus details when only one or two more rows are needed. A narrow filter can require more requests before the dataset fills. maxItems limits emitted rows, rather than search candidates or requests. When sufficient qualifying notices exist, the Actor continues across pages until that number is reached. When fewer exist, the dataset contains the available matches.

PSC codes are compared with classificationCode, while set-aside codes are compared with setAside. Values within each filter are alternatives; different filters must all match. Codes are compared without regard to capitalization. These filters are off by default, and missing source values cannot satisfy a selected filter. For example, pscCodes ["D302", "R499"] includes either code, while setAsideCodes ["SBA"] further restricts the same search to that published set-aside code. NONE is an explicit source value, not a substitute for missing information.

contactPhone comes from the same primary or first point of contact used for contactName and contactEmail. It preserves the published string, including any formatting or extension. Some notices publish the phone as 'Telephone:

If a detail request remains unavailable after retries, that notice is skipped rather than saved as an incomplete search-only row. The log identifies the notice and the run continues. This also prevents a failed detail request from bypassing a selected classification filter. An unexpected response shape is caught for the current search; other search terms can still return results. A blocked or empty search ends successfully with an actionable status, without saving an error row. Empty-result messages suggest broadening keywords, increasing postedWithinDays, removing filters, or disabling onlyNewSinceLastRun to include unchanged notices. Persistent request failures suggest retrying later or changing proxyConfiguration.

The keyword field accepts ordinary search terms, including Unicode text. It does not accept an opportunity URL as a special direct lookup instruction. Use the public search vocabulary that appears in contracting documents. If a term returns nothing, try a broader phrase or extend the lookback window. Missing keywords or invalid numerical settings cause a clear input failure.

Output

The opening example above is a real record from a local run for cybersecurity, with maxItems set to one. The following second example was collected locally with pscCodes ["1660"] and setAsideCodes ["none"]. It shows the new phone field and the exact source codes used by the filters:

{
"id": "f88dcff0842841ae80f852b5151ac325",
"sourceUrl": "https://sam.gov/api/prod/opps/v2/opportunities/f88dcff0842841ae80f852b5151ac325",
"title": "Remanufacture of B-1B Heat Exchangers",
"noticeType": "Presolicitation",
"solicitationNumber": "FD20302502453",
"agency": "DEPT OF DEFENSE",
"naicsCode": "336413",
"classificationCode": "1660",
"setAside": "NONE",
"contactName": "Matthew Churchwell",
"contactEmail": "matthew.churchwell.2@us.af.mil",
"contactPhone": "405-739-4809"
}

Export the dataset as JSON for typed processing, CSV for spreadsheets, or Excel for analyst review. Treat null as missing source information, rather than proof that a requirement does not apply. In particular, set-aside and place-of-performance information can be sparse. A contracting document may contain details that are not populated in the structured response.

Use id as the primary key in a long-lived database and modifiedDate as an update marker. solicitationNumber is useful for human review but should not replace the opportunity ID because separate notices can share a procurement reference. The Actor does not download attachments or follow links mentioned inside descriptions.

How much does it cost?

This Actor is charged per result. Each saved opportunity triggers one result event. Consult the Pricing tab for the current charging terms and use maxItems to control the maximum number of emitted records. Empty searches do not create result events. Duplicate notices encountered across terms do not create an additional dataset row or result event.

There is no enrichment event and no AI enrichment processor in this implementation. The Actor copies public source fields and formats the performance address. A narrow NAICS filter, longer lookback, or several overlapping terms can increase the work needed to obtain the same number of rows, but result charging follows the records actually emitted.

Use with AI agents (MCP)

Connect an MCP client to Apify's MCP server and restrict the tools to this Store path:

{
"mcpServers": {
"apify": {
"url": "https://mcp.apify.com/?tools=axiomworks/sam-gov-opportunities-scraper"
}
}
}

Configure authentication through your client's supported secure mechanism. Ask the agent to provide keywords explicitly, since the editor's prefilled values are examples rather than API defaults. An example prompt is: “Run axiomworks/sam-gov-opportunities-scraper for cybersecurity opportunities modified in the last 30 days, with maxItems 20, and compare title, agency, responseDate and naicsCode.”

For repeated monitoring, ask: “Use keywords janitorial services, select Solicitation and Sources Sought, and return at most 10 notices. Keep the sourceUrl and solicitationNumber in the review table.” Have the agent distinguish a missing responseDate from an actual deadline and retain links or IDs for manual source checks.

Integrations and API

The same input object works through Apify tasks and the Actor API. These examples use environment variables for authentication. Keep tokens out of saved inputs, shared notebooks, and exported datasets. The API client starts a run and then reads the dataset; it does not require a SAM.gov credential.

Python example:

import os
from apify_client import ApifyClient
client = ApifyClient(os.environ["APIFY_TOKEN"])
run = client.actor("axiomworks/sam-gov-opportunities-scraper").call(
run_input={"keywords": ["cybersecurity"], "postedWithinDays": 30, "maxItems": 20}
)
items = list(client.dataset(run["defaultDatasetId"]).iterate_items())

JavaScript example:

import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('axiomworks/sam-gov-opportunities-scraper').call({
keywords: ['cybersecurity'], postedWithinDays: 30, maxItems: 20,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();

cURL example:

$curl --request POST --header "Authorization: Bearer ${APIFY_TOKEN}" --header 'Content-Type: application/json' --data '{"keywords":["cybersecurity"],"postedWithinDays":30,"maxItems":20}' 'https://api.apify.com/v2/acts/axiomworks~sam-gov-opportunities-scraper/run-sync-get-dataset-items'

For longer runs, use asynchronous execution and retrieve the dataset when the run succeeds. Connect completed datasets to spreadsheets, databases, or procurement dashboards using your existing Apify integration. Store the original IDs alongside any internal lead or bid identifiers so you can reconcile later amendments reliably.

FAQ

Why did the run return no items?

The search may not match active notices in the selected time window, or the filters may exclude every candidate. Monitoring mode can also suppress all unchanged records. A temporary site block or unavailable detail service can prevent collection. The status message and log distinguish these situations where possible; broaden the search or retry later as appropriate.

Does the Actor use the official SAM.gov API?

It uses the public website's internal JSON endpoints on sam.gov. It does not use the separate official service that requires an API key. Those internal endpoints can change without notice, so integrations should check run status and validate incoming records before replacing a production dataset.

Are descriptions summarized?

Descriptions are copied from the public detail response. They may contain HTML and can be long. No model rewrites, translates, ranks, or summarizes the text. Handle markup in the consuming application and keep the original body when provenance matters.

Does the tool submit bids or contact officials?

It collects notice records only. It does not submit proposals, send messages, access restricted documents, or evaluate vendor qualifications. Contact fields are published points of contact attached to the procurement notice.

This Actor is not affiliated with or endorsed by SAM.gov or the U.S. General Services Administration.

This Actor collects publicly accessible government contracting information without logging in. You are responsible for how you use and retain the resulting data, including any contact information. Review applicable rules and the source's terms before adopting a recurring workflow. Collection does not establish permission for unrelated outreach or access to restricted procurement materials.

The implementation uses modest request spacing and retries temporary blocks with backoff. It does not solve CAPTCHAs or access account-only content. Always verify procurement decisions against the current notice and applicable solicitation documents; the exported dataset can become outdated after a run finishes.

Feedback

Report a problem with the input you used, the run identifier, and an example opportunity ID. Describe the expected field and the value you observed so the extraction can be checked against the public source. Do not include tokens, proxy credentials, or private documents in a report. If the results met your needs, a rating on the Actor's Store page helps others assess the tool.