# SAM.gov Contracts Monitor & API — Federal Bid Alerts (`cranewell/sam-gov-monitor`) Actor

Monitor new U.S. federal contract opportunities from official SAM.gov data. It works with no key, using SAM.gov's nightly public file, or you can add your free SAM.gov API key for live same-day data. Filter by NAICS, set-aside, agency, state, deadline, keywords and minimum value. Only-new alerts.

- **URL**: https://apify.com/cranewell/sam-gov-monitor.md
- **Developed by:** [Cranewell](https://apify.com/cranewell) (community)
- **Categories:** Business, Lead generation, Automation
- **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 Contracts Monitor & API — Federal Bid Alerts

> Built by Cranewell. Maintained on a best-effort basis by automated checks; **no custom support, consulting, or SLA is offered.** Report issues via the Store's Issues tab.

### What does SAM.gov Contracts Monitor do?

It pulls U.S. federal contract opportunities from **official SAM.gov data**: solicitations, combined synopsis/solicitations, sources sought, presolicitations, awards, and more. Records come out clean and normalized. Schedule it with **Only new since last run** on, and each run returns only opportunities you haven't seen. That gives you a federal bid alert feed for email, Slack, Sheets or a webhook.

- **No API key or account needed.** By default it reads SAM.gov's public Contract Opportunities extract, which is rebuilt nightly.
- **Optional live mode.** Add your own free SAM.gov API key and it queries the official Get Opportunities Public API for same-day notices.
- **Filters:** posted date range or **last N days**, NAICS codes or prefixes, PSC code, notice type, set-aside (SBA, 8(a), HUBZone, SDVOSB, WOSB…), place-of-performance state, agency/organization name, response-deadline range, keywords, **exclude keywords**, and **minimum value**.
- **Webhook-ready run summary** (counts + up to 50 new items) saved as `SUMMARY` in the run's key-value store.
- Official data only: no website scraping, no login automation.

### How to get SAM.gov contract alerts without an API key

Leave **SAM.gov public API key** empty. The Actor downloads SAM.gov's official public extract of active Contract Opportunities (`ContractOpportunitiesFullCSV.csv`, about 70,000 notices, ~210 MB, no login). The file is sorted newest first, so the Actor stops reading once it passes your date window. In our test on 2026-10-28, the default 7-day window finished in under a second.

#### How fresh is the no-key data?

SAM.gov rebuilds the file once a day. On 2026-10-28 it was last modified at 03:30 UTC and its newest notice had been posted the previous evening. A notice posted today usually appears in tomorrow's file, so this mode can lag the live site by up to about a day.

#### Differences without a key

- Active notices only, no archived ones.
- **State** matches the extract's PopState column, which is blank on many notices (about 3 in 4 on 2026-10-28). Those notices drop out when you set a state.
- **Organization name** and **Title contains** are plain "contains" matches.
- **Max API pages** is ignored.
- Modification/amendment/cancel notices are included when you don't choose notice types.

### How to use the SAM.gov API for live, same-day data (optional)

Create a free SAM.gov account, open **Account Details → Public API Key**, and paste the key into the **SAM.gov public API key** input. It's a secret input and is never logged or output. SAM.gov sets daily limits per key (open.gsa.gov):

| Your SAM.gov account | Requests/day |
|---|---|
| Non-federal user, no role | 10 |
| Non-federal user with a role (e.g. entity registration) | 1,000 |
| Federal user / non-federal system account | 1,000 |

Each page is one request of up to 1,000 opportunities. **Max API pages** defaults to 3, so a daily run fits a 10-request key.

### Input example

```json
{ "keywords": ["cybersecurity"], "procurementTypes": ["o", "k"], "daysBack": 7, "maxItems": 50 }
```

Narrower, with a NAICS code and set-aside:

```json
{ "daysBack": 2, "codes": ["541511"], "procurementTypes": ["o", "k", "r"], "setAside": "SBA", "onlyNew": true }
```

Add `"apiKey": "YOUR_SAM_KEY"` for live mode.

#### Filter by NAICS, set-aside, agency, deadline and value

| Field | What it does |
|---|---|
| `postedFrom` / `postedTo` or `daysBack` | Posted window (default last 7 days; max 1 year) |
| `codes` | NAICS codes or prefixes (e.g. `5415`) |
| `classificationCode` | PSC code |
| `procurementTypes` | Notice types |
| `setAside` | Set-aside type |
| `state` | Place-of-performance state (2 letters) |
| `organizationName`, `title` | Agency name / title contains |
| `deadlineFrom` / `deadlineTo` | Response-deadline range |
| `keywords` / `excludeKeywords` | Match or drop words in title/buyer |
| `minValue` | Minimum value. Notices without a value are dropped when this is set |
| `onlyNew`, `resetState`, `maxItems` | Monitoring state and per-run cap (default 1,000) |
| `webhookSummary` | Write the `SUMMARY` record (default on) |

With a key, a single 6-digit NAICS code is filtered server-side; several codes or prefixes are filtered after download.

### Output example (real record from a test run, 2026-10-28)

```json
{
  "id": "SAM-ff1938baf47b4f96a9771245eb299cec",
  "source": "sam.gov",
  "title": "Triumph 5 year V22 PCA Repair LTC",
  "buyer": "NAVSUP WEAPON SYSTEMS SUPPORT",
  "agencyPath": "DEPT OF DEFENSE.DEPT OF THE NAVY.NAVSUP.NAVSUP WEAPON SYSTEMS SUPPORT.NAVSUP WSS PHILADELPHIA.NAVSUP WEAPON SYSTEMS SUPPORT",
  "country": "USA",
  "codes": ["336413"],
  "codeType": "NAICS",
  "classificationCode": "1680",
  "solicitationNumber": "N0038327RS054",
  "noticeType": "Presolicitation",
  "setAside": null,
  "publishedDate": "2026-10-28",
  "deadline": "2026-11-12",
  "value": null,
  "currency": null,
  "url": "https://sam.gov/opp/ff1938baf47b4f96a9771245eb299cec/view",
  "isNew": true,
  "fetchedAt": "2026-10-29T01:09:26.430Z"
}
```

**Fields:**

- `id, source, title, buyer, agencyPath, country, codes, codeType, classificationCode, solicitationNumber, noticeType, setAside, publishedDate, deadline, value, currency, url, isNew, fetchedAt`.
- `value` is the award amount, so it only appears on award notices (3 of 20 in our test). In that test, 19 of 20 had a NAICS code, 16 a deadline, and 11 a set-aside.
- **Not included:** point-of-contact personal data (intentionally), awardee details, office address, full description text, and attachments. Descriptions and attachments are links on SAM.gov.

### How much does it cost to monitor SAM.gov?

| Event | Price |
|---|---|
| Actor start | $0.005 per run |
| Result | $0.003 per opportunity written to the dataset ($3 per 1,000) |

Example: a daily run returning 30 new opportunities costs $0.095, about $2.85 a month. With **Only new** on, you pay only for new items. You can set a **maximum total charge** on a run to cap spend; the Actor is designed to stop cleanly when it's reached (covered by our automated tests). Platform compute is billed by Apify as usual. SAM.gov data is free, and with a key the daily quota is your own.

### Limits

- Notices beyond **Max results** aren't marked as seen, so later runs deliver them.
- Volume check (2026-10-28): all notice types posted in the last 3 days came to 3,174 opportunities, or 4 API requests. Narrow filters if your key allows only 10 requests a day.
- SAM.gov sometimes returns 504 errors. Retries can count against your key's quota. An invalid key fails fast.
- The API returns only the latest active version of each notice.

### FAQ

#### Do I need a SAM.gov API key?

No. Without a key the Actor uses SAM.gov's nightly public extract. Add your free key only if you need same-day notices.

#### How do I get a free SAM.gov API key?

Sign in to SAM.gov and go to Account Details → Public API Key. See open.gsa.gov for limits.

#### Does it include contact names and emails?

No. Point-of-contact personal data is intentionally excluded.

#### Can I get alerts in Slack or email?

Yes. Schedule a Task and attach an Apify integration, or a webhook that reads `SUMMARY`.

#### Can I also monitor EU tenders?

Yes. Use our twin Actor **[EU TED Tenders Monitor & API](https://apify.com/cranewell/ted-monitor)**. It outputs the same core fields (without the SAM-specific `agencyPath`, `classificationCode` and `solicitationNumber`), so US and EU tenders fit one workflow.

### Data source & terms

- Get Opportunities Public API: https://open.gsa.gov/api/get-opportunities-public-api/
- API key rate limits: https://open.gsa.gov/api/entity-api/
- Public extract: https://sam.gov/data-services/Contract%20Opportunities/datagov?privacy=Public
- SAM.gov terms of use: https://sam.gov/about/terms-of-use. These terms name SAM.gov's APIs and data-services extracts as the sanctioned way to get data. With a key, you use it under your own SAM.gov account terms.
- This Actor isn't affiliated with or endorsed by GSA or SAM.gov.

# Actor input Schema

## `apiKey` (type: `string`):

Optional. Leave empty to use SAM.gov's official public Contract Opportunities extract (no key, no account; rebuilt once a day, so it can lag the live site by about a day). Add your own free key (sam.gov > Account Details > Public API Key) to query the live SAM.gov Opportunities API instead. Non-federal keys without a SAM.gov role get 10 requests/day; with a role 1,000/day.

## `postedFrom` (type: `string`):

Posted from (YYYY-MM-DD).

## `postedTo` (type: `string`):

Default today. Max range 1 year (API rule; also applied without a key).

## `daysBack` (type: `integer`):

When no from-date is set, look back this many days.

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

Keep notices whose title/buyer contains any of these words.

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

Drop notices whose title/buyer contains any of these words.

## `title` (type: `string`):

Title contains this text (server-side with a key; matched on the extract without a key).

## `codes` (type: `array`):

One full 6-digit code is filtered by the API; several codes or prefixes are filtered after download.

## `classificationCode` (type: `string`):

PSC classification code.

## `procurementTypes` (type: `array`):

Notice types.

## `setAside` (type: `string`):

Set-aside.

## `state` (type: `string`):

2-letter place-of-performance state code, e.g. VA. Without a key this matches the extract's PopState column, which is blank on many notices (about 3 in 4 on 2026-10-28), so those notices are excluded when this is set.

## `organizationName` (type: `string`):

Agency / organization name.

## `deadlineFrom` (type: `string`):

Response deadline from.

## `deadlineTo` (type: `string`):

Response deadline to.

## `minValue` (type: `integer`):

Drop notices with a value below this (notices without a value are dropped when set).

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

Output only notices not seen in previous runs with the same filters (state is kept in a named key-value store, keyed by a hash of your filters).

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

Forget previously seen notices for this filter set before running.

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

Hard cap on results output (and charged) per run.

## `webhookSummary` (type: `boolean`):

Save a compact JSON summary (counts + new items) to the default key-value store under key SUMMARY, for use in Apify webhooks/integrations.

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

Named key-value store for seen IDs. Leave default unless you want separate state.

## `maxPages` (type: `integer`):

API mode only (with a key): each page is 1 API request (up to 1,000 notices). Keep low on 10-requests/day keys.

## Actor input object example

```json
{
  "daysBack": 7,
  "onlyNew": true,
  "resetState": false,
  "maxItems": 1000,
  "webhookSummary": true,
  "maxPages": 3
}
```

# Actor output Schema

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

All notices pushed to the default dataset, shown in the Overview view.

# 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("cranewell/sam-gov-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("cranewell/sam-gov-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 cranewell/sam-gov-monitor --silent --output-dataset

```

## MCP server setup

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