# SAM.gov Solicitation Changes & Deadline Monitor (Unofficial) (`tender_signall/sam-gov-solicitation-change-monitor`) Actor

Monitor official public SAM.gov contract opportunities for new solicitations, amendments, deadline changes, removals, and material updates - no API key required.

- **URL**: https://apify.com/tender\_signall/sam-gov-solicitation-change-monitor.md
- **Developed by:** [Tender Signall](https://apify.com/tender_signall) (community)
- **Categories:** Automation, Lead generation, Other
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $6.00 / 1,000 opportunity events

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?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## SAM.gov Solicitation Changes & Deadline Monitor

Turn the official public SAM.gov Contract Opportunities extract into a focused, stateful watchlist. The Actor reports **what changed, the before/after values, why it matters, and the official notice URL**.

No SAM.gov account, API key, browser, proxy, CAPTCHA service, or AI key is required.

> Unofficial product. Not affiliated with, endorsed by, or operated by the U.S. General Services Administration or the U.S. Government.

### Why this is different

Most SAM.gov extractors return today's records. This Actor compares successful source generations and produces decision-ready events:

- deadline shortened or extended, including the exact hour delta;
- deadline added or removed;
- set-aside, NAICS, or PSC changes;
- title or normalized description/scope changes;
- notice-type changes and strict cancellation signals;
- a new notice related to a previously tracked solicitation;
- disappearance from the active feed, confirmed across two distinct successful source generations;
- restoration after a temporary disappearance.

Every event has separate severity and relevanceScore fields. A critical deadline change is not hidden behind a generic keyword score.

### Quick start

1. Choose a quick-start market or Custom.
2. Keep the default actionable notice types.
3. Give the watchlist a stable monitorId.
4. Run once to create the baseline.
5. Schedule the same input daily. Do not enable resetState on scheduled runs.

Recommended first input:

```json
{
  "monitorId": "sam-cybersecurity-daily",
  "preset": "cybersecurity",
  "closingWithinDays": 90,
  "outputMode": "new_and_changes",
  "emitBaseline": true,
  "maxResults": 500
}
```

The first run labels current matches as baseline; it never falsely presents the initial snapshot as newly posted. Later runs emit only the selected event types.

### High-value custom watchlist

```json
{
  "monitorId": "va-small-business-it",
  "preset": "custom",
  "keywords": ["software", "cloud migration", "data analytics"],
  "excludeKeywords": ["hardware maintenance"],
  "naicsCodes": ["541511", "541512", "518210"],
  "departments": ["Veterans Affairs"],
  "setAsideCodes": ["SBA", "SDVOSBC"],
  "states": [],
  "noticeTypes": [
    "Solicitation",
    "Combined Synopsis/Solicitation",
    "Presolicitation",
    "Sources Sought"
  ],
  "minDaysRemaining": 1,
  "closingWithinDays": 120,
  "minimumSeverity": "low",
  "outputMode": "new_and_changes"
}
```

Codes accept prefixes: 5415 matches the broader NAICS family, while 541512 is precise.

### Output

Each dataset row is one stable event and includes:

- eventId, eventType, changeTypes, and severity;
- reason, relevanceScore, and the matched filters;
- title, agency, office, solicitation number, NAICS, PSC, and set-aside;
- previous/current deadline and deadlineDeltaHours;
- structured changedFields with before/after values;
- place of performance and the official SAM.gov notice link;
- source version timestamp and detection timestamp.

Example:

```json
{
  "eventType": "deadline_shortened",
  "severity": "critical",
  "previousDeadline": "2026-09-01T17:00:00-04:00",
  "currentDeadline": "2026-08-18T12:00:00-04:00",
  "deadlineDeltaHours": -317,
  "daysRemaining": 2,
  "reason": "Deadline shortened by 13.2 days. Response due in 2 days. Match: title matches software · NAICS 541512."
}
```

CSV and JSON exports are available from the default dataset. The OUTPUT key-value record contains source health, validation, state, event, delivery, and backlog metrics.

### Output modes

| Mode | Behavior |
|---|---|
| changes\_only | Amendments and material changes; suppresses new postings and unchanged records |
| new\_and\_changes | New matching postings plus every selected change |
| all\_current | Also returns unchanged current matches; useful for one-off exports, not recommended for daily billing |

Undelivered events caused by maxResults or the run spending limit are stored and delivered before newer events on the next run. Consumers should use the stable eventId as their deduplication key.

### Scheduling

The official bulk extract is not a real-time feed. A daily schedule is the normal cadence. Use the same monitorId and identical targeting fields on every scheduled run. Changing targeting fields intentionally creates a new baseline; changing output-only controls does not.

If the official ETag is unchanged, the Actor skips the full download. This saves time and compute and does not fabricate change events.

### Reliability controls

- Exact allowlist for the official public S3 object.
- Redirects and URL overrides are blocked.
- HEAD validation before download: size, ETag, timestamp, and freshness.
- True streaming parser with a hard byte cap and record-size cap.
- Required-column, row-count, row-drop, invalid-row, and duplicate-ID guards.
- Offset-aware deadline parsing; no guessed time zones.
- Whitespace, HTML, and Unicode normalization to suppress cosmetic noise.
- Versioned, sharded snapshots with manifest-last commit.
- Optimistic concurrency guard and previous-generation rollback data.
- A failed run preserves the previous successful snapshot.
- One missing generation is never labelled removal.

### Data source and responsible use

The Actor reads the [official public SAM.gov Contract Opportunities data extract](https://sam.gov/data-services/Contract%20Opportunities/datagov?privacy=Public).

Structured contact fields are deliberately ignored. Common email addresses and North American phone numbers are redacted from description excerpts. The Actor does not enrich people, automate outreach, submit bids, or access private records.

### Important semantics

- Events are observations between successful source generations, not a legal amendment ledger.
- Multiple changes between two source refreshes appear as one net before/after change.
- removed\_from\_active\_feed means absent from two distinct successful active-feed generations. It does not assert cancellation.
- Only an explicit official type/title signal is labelled explicit\_cancellation\_signal.
- Always verify the official notice before relying on a deadline or submitting a response.

### Cost controls

The Actor uses transparent Apify Pay Per Event pricing:

- **$0.02 per Actor start** at the default 512 MB memory;
- **$0.006 per delivered opportunity event** written to the default dataset;
- a successful no-change run costs $0.02;
- 10 delivered events cost $0.08, including the run start.

Only dataset opportunity events use the `opportunity-event` charge. Source validation, unchanged source checks, run summaries, and state maintenance do not create paid output events. A first baseline can contain multiple chargeable rows, so use `maxResults` and Apify's maximum run-cost control to bound it. Both controls are respected, and undelivered events remain queued for the next run.

### API

Every Actor run is available through the standard Apify API. Use the Actor's API tab to generate a request with your own Apify token. No SAM.gov credential is added to that request.

### Resetting safely

Enable resetState for one intentional run only when you want to discard the prior comparison baseline for that monitorId. Turn it off before scheduling. Using a new monitorId is the safest way to create an independent watchlist.

# Actor input Schema

## `preset` (type: `string`):

Choose a tested keyword and NAICS watchlist, or select Custom to supply your own targeting.

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

Stable ID for this watchlist. Reuse it on scheduled runs; use a new ID for a separate watchlist.

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

Custom mode only. Matched as whole words or phrases across title, description, solicitation, agency, office, classifications, and place of performance.

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

Discard an opportunity when any excluded whole word or phrase appears in its searchable fields.

## `matchMode` (type: `string`):

ANY requires at least one keyword. ALL requires every configured keyword.

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

Defaults to actionable pre-award notices. Award notices are intentionally excluded unless selected.

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

For example 541512 for a precise industry or 5415 for a broader family.

## `classificationCodes` (type: `array`):

Optional Product Service Code targeting, using an exact code or prefix.

## `departments` (type: `array`):

Optional partial names such as Department of Defense or Veterans Affairs.

## `offices` (type: `array`):

Optional partial office names or AAC codes.

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

US state or territory abbreviations such as VA, MD, CA, or DC.

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

Optional official codes such as SBA, 8A, SDVOSBC, WOSB, EDWOSB, or HZC.

## `publishedWithinDays` (type: `integer`):

Use 0 for any posting date. A positive value keeps only recently posted records.

## `minDaysRemaining` (type: `integer`):

Exclude deadlines sooner than this, calculated from the official offset-aware timestamp.

## `closingWithinDays` (type: `integer`):

Only keep opportunities whose deadline is within this many days.

## `includeNoDeadline` (type: `boolean`):

Useful for Sources Sought and Presolicitations. Their posting age is limited separately below.

## `noDeadlineMaxAgeDays` (type: `integer`):

Prevents old no-deadline notices from remaining actionable forever.

## `minimumSeverity` (type: `string`):

Low returns every tracked change. High focuses on shortened deadlines, scope, set-aside, and classification changes.

## `minScore` (type: `integer`):

Transparent 0–100 relevance score. This is separate from change severity.

## `outputMode` (type: `string`):

Changes only suppresses newly posted opportunities. New and changes is recommended for a complete opportunity feed. All current also repeats unchanged matches.

## `emitBaseline` (type: `boolean`):

First-run matches are labelled baseline, never falsely labelled as newly posted.

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

Undelivered events are queued and delivered before new events on the next run.

## `maxSnapshotItems` (type: `integer`):

Hard safety limit for broad watchlists. Narrow filters instead of raising this unless necessary.

## `allowBroadScan` (type: `boolean`):

Advanced option. Required when no keyword or structured targeting filter is configured.

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

Ignore the previous snapshot and create a new baseline. Leave disabled for schedules.

## Actor input object example

```json
{
  "preset": "it_software",
  "monitorId": "sam-it-opportunities",
  "keywords": [],
  "excludeKeywords": [],
  "matchMode": "any",
  "noticeTypes": [
    "Solicitation",
    "Combined Synopsis/Solicitation",
    "Presolicitation",
    "Sources Sought"
  ],
  "naicsCodes": [],
  "classificationCodes": [],
  "departments": [],
  "offices": [],
  "states": [],
  "setAsideCodes": [],
  "publishedWithinDays": 0,
  "minDaysRemaining": 0,
  "closingWithinDays": 90,
  "includeNoDeadline": true,
  "noDeadlineMaxAgeDays": 30,
  "minimumSeverity": "low",
  "minScore": 0,
  "outputMode": "changes_only",
  "emitBaseline": true,
  "maxResults": 500,
  "maxSnapshotItems": 10000,
  "allowBroadScan": false,
  "resetState": false
}
```

# Actor output Schema

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

Before/after changes, severity, relevance, deadlines, and official notice links.

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

Source version, validation metrics, event counts, queued results, and snapshot status.

# 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 = {};

// Run the Actor and wait for it to finish
const run = await client.actor("tender_signall/sam-gov-solicitation-change-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 = {}

# Run the Actor and wait for it to finish
run = client.actor("tender_signall/sam-gov-solicitation-change-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 '{}' |
apify call tender_signall/sam-gov-solicitation-change-monitor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,tender_signall/sam-gov-solicitation-change-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/vkQDAVfsRRh3poy9Q/builds/sxxJ7EgFATpdjPHTu/openapi.json
