# SAM.gov Scraper: Federal Contract Opportunities & RFP Alerts (`mosaicked_sponge/samgov-opportunities-monitor`) Actor

SAM.gov scraper for U.S. federal government contract opportunities and RFPs. Filter by keyword, NAICS, PSC, set-aside, agency, state and response deadline, and get daily alerts for new and updated bids. Official public data, no API key.

- **URL**: https://apify.com/mosaicked_sponge/samgov-opportunities-monitor.md
- **Developed by:** [Jason](https://apify.com/mosaicked_sponge) (community)
- **Categories:** Lead generation, Automation, Business
- **Stats:** 2 total users, 1 monthly users, 66.7% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.40 / 1,000 opportunity returneds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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 Scraper: Federal Contract Opportunities & RFP Alerts

Get a clean feed of **new and updated U.S. federal contract opportunities** from SAM.gov, filtered to what your company can bid on. Schedule it daily and each run returns **only the notices you haven't seen yet**, so you can send them to e-mail, Slack, a spreadsheet or your CRM.

![SAM.gov Scraper: Federal Contract Opportunities & RFP Alerts - sample output](https://api.apify.com/v2/key-value-stores/QpfrAb6wndEDB0tDd/records/output-FXzD4qn8saKdqJMTO.png)

- ✅ **Official data, no API key.** It reads SAM.gov's own public *Contract Opportunities* daily extract, the file SAM.gov publishes for data sharing. It avoids the 10-requests-a-day API limit, scraping and logins.
- ✅ **Monitor mode** returns only new notices, plus notices whose deadline, description, set-aside or award changed.
- ✅ **Real filters**:
  - boolean keywords (`cyber AND (cloud OR "zero trust") NOT training`)
  - NAICS and PSC codes or prefixes
  - set-aside (SBA, 8(a), HUBZone, SDVOSB, WOSB…)
  - agency or office
  - notice type
  - state
  - posted date
  - **response deadline within N days**
  - award amount
- ✅ **Ready-to-send digest.** Each run also saves an HTML and CSV summary that your integration can e-mail.
- ✅ **Privacy-safe.** Contracting-officer names, e-mails and phone numbers are dropped, and e-mails and phone numbers are scrubbed from all text.
- ✅ **Fast and reliable.** It downloads the ~200 MB file, verifies it is complete, then scans it with bounded memory, so a scan takes seconds. Broken or interrupted downloads are resumed or restarted automatically (nothing is returned from a partial file), and it falls back to the public S3 copy if needed. Small input mistakes (an unknown mode, a malformed date, a dangling AND/OR) are corrected with a warning instead of failing the run.

### Who it's for

- **Small businesses and 8(a)/HUBZone/SDVOSB/WOSB firms** that want a daily list of bids that match their NAICS codes.
- **BD and capture teams, and proposal consultants**, who track agencies, competitors and recompetes.
- **GovCon newsletters, analysts and lead-gen agencies** that need structured opportunity and award data.

### How to use

1. Set your filters, for example:
   - **NAICS codes:** `541511`, `5415` (a prefix covers the whole group).
   - **Set-aside types:** e.g. *Small Business Set-Aside (Total)*, *SDVOSB Set-Aside*.
   - **Keywords:** one query per line. Supports `AND`, `OR`, `NOT`, parentheses, `"exact phrases"` and `prefix*`.
   - **Response due within (days):** e.g. `21`, to see only bids you still have time for.
   - Optional: agency/office, notice types, states, posted within N days, award amount range.
2. Set **Mode** to *New and updated notices (monitor)*. The form opens on *All matching notices* so your first test run always shows results.
3. **Schedule** the Actor daily (SAM.gov refreshes the extract once a night, around 11:30 PM ET). Connect the dataset or the `DIGEST_HTML` record to e-mail, Slack, Google Sheets, Zapier or Make.

> **First run:** every matching notice counts as new. Enable **First run: record baseline only** to start fresh without paying for the backlog.
> **Several saved searches?** Use a different **Monitor ID** for each.
> **Same file as last time?** If SAM.gov hasn't published a new extract yet, the run stops in under a second, and you aren't charged the run fee.

### Output

One item per notice:

```json
{
  "noticeId": "abbeb9ff66624ee880a67d9120638145",
  "samUrl": "https://sam.gov/opp/abbeb9ff66624ee880a67d9120638145/view",
  "solicitationNumber": "36C10B26Q0610",
  "title": "DA10--PUPPET ENTERPRISE SOFTWARE (VA-26-00062749)",
  "noticeType": "Justification",
  "agencyPath": "VETERANS AFFAIRS, DEPARTMENT OF > TECHNOLOGY ACQUISITION CENTER NJ (36C10B)",
  "naicsCode": "541519",
  "pscCode": "DA10",
  "setAsideCode": null,
  "postedDate": "2026-10-18T20:04:35",
  "responseDeadline": null,
  "officeState": "NJ",
  "awardNumber": "NNG15SD27B",
  "awardAmount": 518996.0,
  "awardee": "V3GATE, LLC COLORADO SPRINGS 80921",
  "description": "Puppet Enterprise Platform",
  "changeType": "new",
  "firstSeenAt": "2026-10-20T00:15:20+00:00"
}
```

Full field list:

- **Notice:** `noticeId`, `samUrl`, `solicitationNumber`, `title`, `noticeType`, `baseType`, `active`
- **Agency:** `department`, `subTier`, `office`, `agencyPath`, `cgac`, `fpdsCode`, `aacCode`
- **Dates:** `postedDate`, `responseDeadline`, `responseDeadlineUtc`, `archiveType`, `archiveDate`
- **Classification:** `setAsideCode`, `setAside`, `naicsCode`, `pscCode`
- **Place of performance:** `popStreet`, `popCity`, `popState`, `popZip`, `popCountry`
- **Contracting office:** `officeCity`, `officeState`, `officeZip`, `officeCountry`
- **Award:** `awardNumber`, `awardDate`, `awardAmount`, `awardAmountText`, `awardee`
- **Content:** `additionalInfoLink`, `description`, `descriptionTruncated`
- **Monitoring:** `changeType`, `firstSeenAt`, `scrapedAt`, `monitorId`

Download it as JSON, CSV, Excel, XML or RSS, or use the API. The dataset has an **Opportunities** view and an **Awards** view.

Unbilled extras in the run's key-value store:

- `OUTPUT`: run summary with the file date, rows scanned, matches, and new/changed/removed counts.
- `ERRORS`
- `DIGEST_HTML` and `DIGEST_CSV`: the first 500 returned notices, ready to e-mail.

### Pricing (pay per event)

| Event | Price |
|---|---|
| Opportunity returned (each notice written to the dataset) | **$3.00 per 1,000**. Volume tiers: $2.70 (Bronze), $2.55 (Silver), $2.40 (Gold) |
| Daily extract processed (once per run that scans a new file) | $0.02 |
| Actor start | Apify's standard start fee |

In monitor mode you pay only for new or updated notices. A typical small business that checks daily and gets about 20 matches a day pays about $0.08/day, or about **$2.40/month**. Set **Max cost per run** to cap spending. *Max notices per run* defaults to 1,000 so that a first run without filters can't return all ~70,000 notices. Anything not returned because of either limit is returned on the next run.

### Good to know

- **Freshness:** SAM.gov publishes the extract once a day, so notices posted after that night's file was built appear the next day. For same-hour alerts, use SAM.gov's own saved searches alongside this Actor.
- **Coverage:** the extract contains **active** notices (about 70,000, including recent award notices). Archived notices are not in it.
- **Descriptions:** SAM.gov truncates very long descriptions in the extract (about 32,000 characters). `descriptionTruncated` tells you when, and `samUrl` opens the full notice with its attachments.
- **State filter:** about 3 in 4 notices have no place-of-performance state, so the contracting office's state is used for those by default. You can switch this off.
- **Dates:** `postedDate` is shown exactly as SAM.gov publishes it. `responseDeadlineUtc` normalizes deadlines that carry a timezone.

### Legal and data

The Actor uses only SAM.gov's public Contract Opportunities data extract, which is published by the U.S. General Services Administration for public reuse. It doesn't log in, call rate-limited APIs or scrape web pages. Personal contact details of contracting officers are removed. This Actor is not affiliated with or endorsed by GSA or SAM.gov.

### FAQ

**Do I need a SAM.gov account or API key?** No.

**Why did a run return nothing?** Either SAM.gov hasn't published a new file since your last run (see `OUTPUT.status = "file-unchanged"`), or nothing new matched your filters.

**I changed my filters. Why is everything "new"?** Monitor memory is per Monitor ID. Use a new Monitor ID for a new search, or enable *First run: record baseline only*.

**Can it read attachments or the full description?** Not in this version. Open `samUrl`. An optional bring-your-own-API-key mode is planned.

# Actor input Schema

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

One query per line; a notice matches if ANY line matches. Supports AND, OR, NOT, parentheses, "exact phrases" and prefix\* wildcards, e.g. cyber AND (cloud OR "zero trust") NOT training. Whole-word, case-insensitive.

## `excludeKeywords` (type: `array`):

Drop notices matching any of these queries (same syntax), e.g. janitorial, "brand name only".

## `keywordScope` (type: `string`):

Where keywords are matched.

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

Full 6-digit codes or prefixes, e.g. 541511 or 5415 (all computer systems design services).

## `pscCodes` (type: `array`):

Product Service Codes or prefixes, e.g. DA01, D3 or R4.

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

Keep only these set-asides (leave empty for all). 'No set-aside' matches full-and-open notices.

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

Keep only these notice types (matched against the current and the original type). Leave empty for all.

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

Case-insensitive match on department, sub-tier, office, AAC or CGAC code, e.g. 'VETERANS AFFAIRS', 'DEFENSE LOGISTICS', 'SPE4A6', '036'.

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

Two-letter codes, e.g. FL, VA, DC. Many notices have no place of performance; see the next option.

## `stateMatchesOfficeIfNoPlaceOfPerformance` (type: `boolean`):

About three in four notices have no place-of-performance state. If enabled, the contracting office's state is used for those.

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

Keep notices posted in the last N days (0 = any).

## `postedAfter` (type: `string`):

Keep notices posted on or after this date (YYYY-MM-DD).

## `responseDueWithinDays` (type: `integer`):

Keep notices whose response deadline is between today and N days from now (0 = any). Notices without a deadline are dropped when this is set.

## `excludeExpired` (type: `boolean`):

The extract still lists notices for a while after their response deadline.

## `awardAmountMin` (type: `integer`):

Award notices only - notices without an award amount are dropped when set.

## `awardAmountMax` (type: `integer`):

Award notices only.

## `mode` (type: `string`):

Monitor modes return only notices not returned before (plus, in the default mode, notices whose title, deadline, description, set-aside or award changed). The first run returns everything that matches. The form opens on "All matching notices" so a first test run always shows results; pick a monitor mode for scheduled runs. API calls that omit this field use "New and updated notices".

## `monitorId` (type: `string`):

Name of this saved search. Runs with the same ID share memory of returned notices; use one ID per search.

## `firstRunBaseline` (type: `boolean`):

The first run remembers current matches without returning (or charging for) them; later runs return only what's new.

## `forceReprocess` (type: `boolean`):

SAM.gov publishes the extract once a day. By default a monitor run that finds the same file its search already fully processed stops immediately (no run fee). Changed filters, or a previous run that stopped at Max notices or your max cost, always re-scan. Enable to re-scan anyway.

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

Include the notice description text (e-mail addresses and phone numbers removed).

## `maxResults` (type: `integer`):

Stop returning after this many notices (0 = no limit). Protects you from an unfiltered first run returning the whole extract (~70,000 notices). Notices not returned are returned next run, even if SAM.gov has not published a new file yet.

## `digest` (type: `boolean`):

Save DIGEST_HTML and DIGEST_CSV (first 500 returned notices) to the run's key-value store for e-mail or Slack integrations.

## `stateStoreName` (type: `string`):

Named key-value store that keeps monitor memory between runs.

## Actor input object example

```json
{
  "keywords": [
    "\"software development\" OR \"cloud migration\""
  ],
  "keywordScope": "titleAndDescription",
  "naicsCodes": [
    "5415"
  ],
  "stateMatchesOfficeIfNoPlaceOfPerformance": true,
  "postedWithinDays": 0,
  "responseDueWithinDays": 0,
  "excludeExpired": true,
  "mode": "all",
  "monitorId": "default",
  "firstRunBaseline": false,
  "forceReprocess": false,
  "includeDescription": true,
  "maxResults": 1000,
  "digest": true,
  "stateStoreName": "samgov-monitor-state"
}
```

# Actor output Schema

## `notices` (type: `string`):

One normalized item per contract notice returned by this run (in monitor mode: only notices new or updated since the previous run with the same Monitor ID).

## `summary` (type: `string`):

OUTPUT record: extract file date, rows scanned, matched, new, updated, limits and warnings.

## `digestHtml` (type: `string`):

DIGEST_HTML record: e-mail-ready table of the returned notices (first 500).

## `digestCsv` (type: `string`):

DIGEST_CSV record: the returned notices as CSV (first 500).

## `errors` (type: `string`):

ERRORS record: download or processing problems, empty when the run succeeded.

# 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 = {
    "keywords": [
        "\"software development\" OR \"cloud migration\""
    ],
    "naicsCodes": [
        "5415"
    ],
    "mode": "all"
};

// Run the Actor and wait for it to finish
const run = await client.actor("mosaicked_sponge/samgov-opportunities-monitor").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 = {
    "keywords": ["\"software development\" OR \"cloud migration\""],
    "naicsCodes": ["5415"],
    "mode": "all",
}

# Run the Actor and wait for it to finish
run = client.actor("mosaicked_sponge/samgov-opportunities-monitor").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 '{
  "keywords": [
    "\\"software development\\" OR \\"cloud migration\\""
  ],
  "naicsCodes": [
    "5415"
  ],
  "mode": "all"
}' |
apify call mosaicked_sponge/samgov-opportunities-monitor --silent --output-dataset

```

## MCP server setup

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

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/FXzD4qn8saKdqJMTO/builds/7Pim9hmp8Yc17gHYS/openapi.json
