# SAM.gov Contract Opportunities Scraper & Monitor (`vocal_rhapsody/sam-gov-opportunities`) Actor

SAM.gov scraper and monitor for US federal contract opportunities. Reads GSA’s official daily extract, no API key or login. Filter by NAICS, set-aside, state, agency and notice type; get only notices new since your last run.

- **URL**: https://apify.com/vocal_rhapsody/sam-gov-opportunities.md
- **Developed by:** [GovData Tools](https://apify.com/vocal_rhapsody) (community)
- **Categories:** Lead generation, Business
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $3.00 / 1,000 results

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## SAM.gov Contract Opportunities Scraper & Monitor

Get a filtered feed of US federal contract opportunities (solicitations, sources sought, presolicitations, award notices and more) as JSON, CSV or Excel, and receive only the notices that are new since your last run. Built for small GovCon firms, BD and capture consultants, and bid-alert services. No SAM.gov account or API key needed.

### Why this Actor

- **Official daily extract.** It reads the public Contract Opportunities CSV that GSA publishes for download on SAM.gov (Data Services, updated daily). It does not use the SAM.gov search API and needs no API key and no login.
- **New since last run, per saved task.** The Actor remembers which notices it already returned, separately for every distinct filter combination, so each saved task with its own filters (one per client or per NAICS list) keeps its own feed.
- **Filters that match how bids are found.** NAICS prefix, set-aside code, state, agency, notice type, title keywords, posted-within-N-days.
- **No contracting-officer contact details.** Names, emails and phone numbers from the extract's contact columns are dropped while parsing and never stored or output.

### What data you get

One flat item per notice. Dates are ISO 8601 as published (the posted date has no timezone in the source).

| Field | Meaning | Example (from a real run) |
|---|---|---|
| `noticeId`, `url` | SAM.gov notice ID and link to the notice | `30017ddaad2442e788d79c302bd4d239` |
| `title`, `solicitationNumber` | Notice title and solicitation number | `VM Ware VCF Licenses`, `12760426Q0213` |
| `department`, `subTier`, `office`, `officeCode` | Issuing agency hierarchy and contracting office | `AGRICULTURE, DEPARTMENT OF`, `FOREST SERVICE` |
| `postedDate`, `responseDeadline`, `archiveDate` | When posted, when responses are due, when archived | `2026-10-21T18:14:06`, `2026-10-27T17:00:00-04:00` |
| `noticeType`, `baseType` | Current and original notice type | `Combined Synopsis/Solicitation` |
| `setAsideCode`, `setAside` | Set-aside code and description | `SBA`, `Small Business Set Aside - Total` |
| `naicsCode`, `pscCode` | NAICS and product/service classification code | `541519`, `7A21` |
| `placeOfPerformanceCity/State/Zip/Country` | Where the work is performed (often blank in the source) | `Albuquerque`, `NM` |
| `officeCity/State/Zip/Country` | Contracting office location | `Albuquerque`, `NM`, `87102` |
| `active` | Whether the notice is flagged active | `true` |
| `awardNumber`, `awardDate`, `awardAmount`, `awardee` | Filled only when the notice carries award details | `47QTCA27D000X`, `2026-10-21`, `475000` |
| `additionalInfoLink` | Extra link from the notice, when given | `null` |
| `description` | Optional, only with `includeDescription` (see Privacy) | |
| `sourceUrl`, `scrapedAt` | The extract URL and the run timestamp | |

Fields the source leaves empty are returned as `null`.

### How to use

1. Open the Actor and set your filters, for example NAICS `5415` and set-aside `SBA`. Leave a filter empty to skip it.
2. Click **Start**. The first run returns matches posted in the last 3 days (`firstRunLookbackDays`) and marks all current matches as seen.
3. Save the input as a task and put it on an Apify Schedule (daily is a natural fit). Each later run returns only notices not seen before. Send results to Google Sheets, your CRM or a webhook with Apify integrations.

Example input:

```json
{
  "naicsCodes": ["5415"],
  "setAsideCodes": ["SBA", "8A"],
  "states": ["VA", "MD"],
  "agencies": ["AIR FORCE", "VETERANS"],
  "noticeTypes": ["Solicitation", "Combined Synopsis/Solicitation", "Sources Sought"],
  "onlyNew": true,
  "maxItems": 100
}
```

#### Filters

All filters are optional. Between filters it is AND, within one filter it is OR.

| Input | Meaning |
|---|---|
| `naicsCodes` | Prefix match: `5415` matches 541511, 541512 and so on |
| `setAsideCodes` | Set-aside code, case-insensitive. Common: `SBA`, `8A`, `HZC` (HUBZone), `SDVOSBC` (service-disabled veteran-owned), `WOSB`, `EDWOSB` |
| `states` | Two-letter state of the place of performance (or of the contracting office when none is given) |
| `agencies` | Case-insensitive text match on department, sub-tier agency or office, e.g. `AIR FORCE` |
| `noticeTypes` | Exact notice type, case-insensitive: `Solicitation`, `Combined Synopsis/Solicitation`, `Sources Sought`, `Presolicitation`, `Award Notice`, `Special Notice`, `Justification` and others present in the extract |
| `keywords` | Keeps notices whose title contains any of the given terms |
| `activeOnly` | Skip inactive notices. Default on |
| `postedWithinDays` | Only notices posted in the last N days |
| `maxItems` | Cap on results per run (default 100) |
| `includeDescription` | Add the description text, truncated to 5,000 characters, email addresses removed. Default off |

#### New since last run

With `onlyNew` on (off by default; turn it on in your scheduled task) the Actor stores a compact list of notice IDs in a named key-value store in your account (`sam-gov-opportunities-state`), one entry per distinct filter combination.

- **First run** for a filter combination: returns matches posted in the last `firstRunLookbackDays` days (default 3) and marks all current matches as seen.
- **Later runs**: return only notices not seen before. IDs that are no longer in the extract are dropped from the list.
- Results cut off by `maxItems` (or by a spending limit) are not marked as seen, so the next run returns them.
- `resetState` forgets the list for the given filters and treats the run as a first run.
- Leave `onlyNew` off (the default) to get all current matches on every run.

Changing a filter (for example adding a NAICS code) starts a new, separate list.

### Output example

Three real items from a run filtered to NAICS `5415` (`sourceUrl` is the extract URL):

```json
[
  {
    "noticeId": "30017ddaad2442e788d79c302bd4d239",
    "title": "VM Ware VCF Licenses",
    "solicitationNumber": "12760426Q0213",
    "department": "AGRICULTURE, DEPARTMENT OF",
    "subTier": "FOREST SERVICE",
    "office": "USDA FOREST SERVICE",
    "officeCode": "127604",
    "postedDate": "2026-10-21T18:14:06",
    "noticeType": "Combined Synopsis/Solicitation",
    "baseType": "Combined Synopsis/Solicitation",
    "archiveDate": "2026-11-11",
    "setAsideCode": "SBA",
    "setAside": "Small Business Set Aside - Total",
    "responseDeadline": "2026-10-27T17:00:00-04:00",
    "naicsCode": "541519",
    "pscCode": "7A21",
    "placeOfPerformanceCity": "Albuquerque",
    "placeOfPerformanceState": "NM",
    "placeOfPerformanceZip": null,
    "placeOfPerformanceCountry": "USA",
    "active": true,
    "awardNumber": null,
    "awardDate": null,
    "awardAmount": null,
    "awardee": null,
    "officeState": "NM",
    "officeCity": "Albuquerque",
    "officeZip": "87102",
    "officeCountry": "USA",
    "additionalInfoLink": null,
    "url": "https://sam.gov/workspace/contract/opp/30017ddaad2442e788d79c302bd4d239/view",
    "sourceUrl": "https://sam.gov/api/prod/fileextractservices/v1/api/download/Contract%20Opportunities/datagov/ContractOpportunitiesFullCSV.csv",
    "scrapedAt": "2026-10-22T12:30:16.369Z"
  },
  {
    "noticeId": "c80b68ea0a744c1ab71886cdec46739b",
    "title": "Cyber Security Engineering and Risk Management Framework Support",
    "solicitationNumber": "W91CRB-26-B-A006",
    "department": "DEPT OF DEFENSE",
    "subTier": "DEPT OF THE ARMY",
    "office": "W6QK ACC-APG",
    "officeCode": "W91CRB",
    "postedDate": "2026-10-21T09:15:28",
    "noticeType": "Presolicitation",
    "baseType": "Presolicitation",
    "archiveDate": "2026-10-22",
    "setAsideCode": "8A",
    "setAside": "8(a) Set-Aside (FAR 19.8)",
    "responseDeadline": "2026-10-22T17:00:00-04:00",
    "naicsCode": "541519",
    "pscCode": "DJ01",
    "placeOfPerformanceCity": "Aberdeen Proving Ground",
    "placeOfPerformanceState": "MD",
    "placeOfPerformanceZip": "21005",
    "placeOfPerformanceCountry": "USA",
    "active": true,
    "awardNumber": null,
    "awardDate": null,
    "awardAmount": null,
    "awardee": null,
    "officeState": "MD",
    "officeCity": "ABERDEEN PROVING GROU",
    "officeZip": "21005-5001",
    "officeCountry": "USA",
    "additionalInfoLink": null,
    "url": "https://sam.gov/workspace/contract/opp/c80b68ea0a744c1ab71886cdec46739b/view",
    "sourceUrl": "https://sam.gov/api/prod/fileextractservices/v1/api/download/Contract%20Opportunities/datagov/ContractOpportunitiesFullCSV.csv",
    "scrapedAt": "2026-10-22T12:30:16.369Z"
  },
  {
    "noticeId": "da84d7cf60f24b7695f57e20329102ea",
    "title": "Multiple Award Schedule",
    "solicitationNumber": "47QSMD20R0001",
    "department": "GENERAL SERVICES ADMINISTRATION",
    "subTier": "FEDERAL ACQUISITION SERVICE",
    "office": "GSA/FAS FURNITURE SYSTEMS MGT DIV",
    "officeCode": "47QSMA",
    "postedDate": "2026-10-21T15:20:53",
    "noticeType": "Award Notice",
    "baseType": "Combined Synopsis/Solicitation",
    "archiveDate": "2031-10-20",
    "setAsideCode": null,
    "setAside": null,
    "responseDeadline": "2026-10-21T11:12:13-05:00",
    "naicsCode": "541519",
    "pscCode": "99",
    "placeOfPerformanceCity": null,
    "placeOfPerformanceState": null,
    "placeOfPerformanceZip": null,
    "placeOfPerformanceCountry": null,
    "active": true,
    "awardNumber": "47QTCA27D000X",
    "awardDate": "2026-10-21",
    "awardAmount": 475000,
    "awardee": "CYBERPROS TECHNOLOGY LLC Chantilly VA 20151 USA",
    "officeState": "PA",
    "officeCity": "PHILADELPHIA",
    "officeZip": "19107",
    "officeCountry": "USA",
    "additionalInfoLink": null,
    "url": "https://sam.gov/workspace/contract/opp/da84d7cf60f24b7695f57e20329102ea/view",
    "sourceUrl": "https://sam.gov/api/prod/fileextractservices/v1/api/download/Contract%20Opportunities/datagov/ContractOpportunitiesFullCSV.csv",
    "scrapedAt": "2026-10-22T12:30:16.369Z"
  }
]
```

The awardee is returned as published, as one text string that can include name and address.

### Pricing

Pay per result: you are charged for each notice written to the dataset (Apify pay-per-event, event `result`). Notices that are filtered out or already seen are not charged. Set `maxItems` to cap the cost of a run. The current price per 1,000 results is shown on this page.

### Use cases

- **Daily bid alerts for a GovCon firm.** Filter to your NAICS codes and set-aside status, schedule daily, and get only the notices posted since yesterday into Sheets or your CRM.
- **Capture and BD consultants with several clients.** One saved task per client; each keeps its own new-since-last-run list.
- **Bid-alert and lead services.** Feed filtered notices into your own email, Slack or webhook pipeline.
- **Early-stage opportunity tracking.** Filter `noticeTypes` to Sources Sought and Presolicitation for specific agencies to see opportunities before the solicitation.
- **Award research.** Filter to Award Notice to collect award numbers, dates, amounts and awardees where notices include them.

### Privacy

The named contracting-officer contacts (names, emails, phones, fax) in the extract are dropped while parsing and never stored or output. With `includeDescription` on, email addresses inside description text are replaced with `[email removed]`. Free-text descriptions can still mention people or phone numbers, which is why descriptions are off by default.

### FAQ

**Do I need a SAM.gov account or API key?** No. The Actor downloads GSA's public Contract Opportunities extract and uses no login or key.

**How often is the data updated?** The extract is published daily by GSA, so a notice can appear in your results only after it appears in that file. Running more than once a day will not surface newer notices than the latest extract.

**Is the data complete?** It contains the notices that GSA's extract contains. Attachments and attachment links are not included, and no completeness is guaranteed.

**Why did my first run return few results?** The first run for a filter combination returns only matches posted in the last `firstRunLookbackDays` days (default 3) and `maxItems` caps the count. Increase either to see more, or turn `onlyNew` off.

**What time zone are the dates in?** Dates are returned as published. The posted date has no timezone in the source; response deadlines include an offset when SAM.gov provides one.

**What if SAM.gov changes the file?** If expected columns are missing or most rows look malformed, the run stops with an error instead of returning bad data. Temporary download errors are retried a few times.

**Is this legal to use?** The Actor reads a publicly available federal data file and does not log in to anything. You are responsible for how you use the data.

**Does it read the whole file on every run?** Yes when `onlyNew` is on, so the list of seen notices stays exact. The file is streamed, not loaded into memory. Each run writes a `RUN_SUMMARY` record with rows read, items emitted, runtime and peak memory.

### Limitations

- Data is as fresh as the official extract (updated once a day).
- The extract has no timezone on the posted date and no attachment links.
- Windows-1252 text in the source is converted to normal Unicode.
- Place-of-performance fields are often empty in the source; the `states` filter then falls back to the contracting office state.

### See also

Want to know who won the work? The companion [USAspending Contract Awards Scraper & Monitor](https://apify.com/vocal_rhapsody/usaspending-awards) returns new federal contract awards from USAspending.gov, with the same kind of daily new-since-last-run feed.
It shares NAICS, agency and keyword filters and field names such as `naicsCode`, `pscCode`, `department`, `subTier` and `awardAmount`, so opportunities here and awards there line up.

### Changelog

- 0.1: first release.

# Changelog

This Actor's version history is a separate document: https://apify.com/vocal_rhapsody/sam-gov-opportunities/changelog.md

# Actor input Schema

## `naicsCodes` (type: `array`):

Prefix match: "5415" matches 541511, 541512 and so on. Leave empty to include all NAICS codes.

## `setAsideCodes` (type: `array`):

Match the notice's set-aside code (case-insensitive). Common codes: SBA (small business), 8A (8(a)), HZC (HUBZone), SDVOSBC (service-disabled veteran-owned), WOSB (women-owned), EDWOSB (economically disadvantaged women-owned). Leave empty to include all.

## `states` (type: `array`):

Two-letter state codes, e.g. TX. Matches the place-of-performance state, or the contracting office state when no place of performance is given. Leave empty to include all.

## `agencies` (type: `array`):

Case-insensitive text match on department, sub-tier agency or office, e.g. "AIR FORCE", "VETERANS". A notice is kept if any entry matches. Leave empty to include all.

## `noticeTypes` (type: `array`):

Exact notice type names (case-insensitive), e.g. Solicitation, Combined Synopsis/Solicitation, Sources Sought, Presolicitation, Award Notice, Special Notice. Leave empty to include all.

## `keywords` (type: `array`):

Keep notices whose title contains any of these terms (case-insensitive). Leave empty to skip keyword filtering.

## `activeOnly` (type: `boolean`):

Skip archived and inactive notices.

## `postedWithinDays` (type: `integer`):

Only notices posted in the last N days. Leave empty for no limit.

## `onlyNew` (type: `boolean`):

Turn this on in your scheduled task to return only notices not seen in earlier runs with the same filters. The list of seen notices is kept per filter combination in the named key-value store "sam-gov-opportunities-state" in your account. Off (the default) gives all current matches on every run.

## `firstRunLookbackDays` (type: `integer`):

On the first run for a filter combination, return only matches posted within this many days. All current matches are then marked as seen.

## `resetState` (type: `boolean`):

Forget which notices were already seen for these filters and treat this run as a first run. Only applies when "Only new since last run" is on.

## `includeDescription` (type: `boolean`):

Add the notice description (truncated to 5,000 characters, email addresses removed). Off by default; descriptions can still mention people or phone numbers.

## `maxItems` (type: `integer`):

Stop after this many results. You are charged per result, so this keeps the cost of a run predictable.

## Actor input object example

```json
{
  "naicsCodes": [
    "5415"
  ],
  "activeOnly": true,
  "onlyNew": false,
  "firstRunLookbackDays": 3,
  "resetState": false,
  "includeDescription": false,
  "maxItems": 10
}
```

# Actor output Schema

## `results` (type: `string`):

One item per matching notice (new since last run when onlyNew is on).

## `runSummary` (type: `string`):

Rows scanned, matches, items pushed, runtime and peak memory.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "naicsCodes": [
        "5415"
    ],
    "maxItems": 10
};

// Run the Actor and wait for it to finish
const run = await client.actor("vocal_rhapsody/sam-gov-opportunities").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "naicsCodes": ["5415"],
    "maxItems": 10,
}

# Run the Actor and wait for it to finish
run = client.actor("vocal_rhapsody/sam-gov-opportunities").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "naicsCodes": [
    "5415"
  ],
  "maxItems": 10
}' |
apify call vocal_rhapsody/sam-gov-opportunities --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,vocal_rhapsody/sam-gov-opportunities"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/OnCnTTZTXkoILx5CB/builds/mg8rNK73GSGITKMTn/openapi.json
