# SAM.gov Federal Contract Opportunities (`maxwellai/sam-gov-opportunities`) Actor

Official SAM.gov Get Opportunities API: federal contract leads by NAICS, set-aside, and deadline. Optional title keywords (they under-sample). Notice URLs and attachments. PPE target $3.00 per 1,000 records. No invented win scores.

- **URL**: https://apify.com/maxwellai/sam-gov-opportunities.md
- **Developed by:** [Max Mattox](https://apify.com/maxwellai) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $3.00 / 1,000 opportunity records

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.

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 Federal Contract Opportunities

**MaxwellAI** Apify Actor for **federal contract leads** from the official GSA [Get Opportunities Public API v2](https://open.gsa.gov/api/get-opportunities-public-api/). Default filters: **NAICS**, **set-aside**, and **posted window / deadline**. Title keywords are optional and under-sample. Each row is one notice: title, agency, deadline, SAM.gov URL, and attachment links.

> **Store keywords:** SAM.gov scraper · federal contract opportunities · federal contract leads · NAICS filter · set-aside · 8(a) · SDVOSB · HUBZone · WOSB · solicitation tracker · GSA opportunities API · govcon · PPE Actor

| | |
|---|---|
| **Actor name** | `sam-gov-opportunities` |
| **Store title** | SAM.gov Federal Contract Opportunities |
| **Namespace (on publish)** | `maxwellai/sam-gov-opportunities` (confirm username on login) |
| **Pricing (PPE target)** | **$3.00 per 1,000 records** — event `opportunity-record` @ **$0.003**. Not live until Console monetization is set. |
| **Data source** | Official `GET https://api.sam.gov/opportunities/v2/search` only. No HTML scrape. |
| **Status** | Scaffold + SAMPLE\_MODE. Not on the Store. Verified paid runs: **$0**. |

This is a **data Actor**. It does not write proposals, score win probability, or invent award amounts.

***

### Honest market (live Store pages, 2026-09-13)

This niche is **not empty**. Do not claim first, only, or Featured.

| Actor | Users (on page) | Monthly (on page) | Notes |
|---|---|---|---|
| `jungle_synthesizer/samgov-scraper` | 161 | 3 | 1.00/5; no-key frontend scrape; attachments |
| `fortuitous_pirate/sam-gov-scraper` | 109 | 14 | ratings BLIND |
| `alizarin_refrigerator-owner/sam-gov-contracts---federal-opportunities-search` | 9 | 1 | ratings BLIND |
| `jdepablos/samgov-opportunities-feed` | 2 | 1 | PPE from $4/1k; no key |
| `skootle/sam-gov-federal-contracts` | 1 | 1 | PPE from $7/1k; API key + USAspending join |
| `ryanclinton/sam-gov-contract-monitor` | BLIND | BLIND | official API |

**Wedge we can defend:** official API (buyer brings a free SAM.gov key as an Actor secret), first-class **NAICS + set-aside + deadline** (not a hard title string), incremental `sinceLastRun`, attachment URLs, PPE cheaper than `$4–$7/1k` listings, copy that does not invent fit scores. USAspending join is a later toggle — not a current claim.

**Live probe (2026-09-13, key confirmed HTTP 200):** `ncode=339113` + `ptype=o` over 30d → **23** notices (Endo Dry Compressor, EKG machine, Surgical Lights). Same NAICS + title `personal protective equipment` over 7d → **0**. v1 has no `ptype` filter; still default to NAICS + set-aside and leave `keywords` empty.

***

### What this Actor does

- Queries `https://api.sam.gov/opportunities/v2/search` with your filters
- **NAICS** codes and **set-aside** codes first; optional **keywords** (SAM.gov `title` param — under-samples)
- **postedWithinDays** lookback (clamped to SAM’s 365-day window)
- Optional **sinceLastRun** watermark in the Key-Value Store
- De-duplicates by `opportunityId` across NAICS × set-aside combos
- Pushes clean records to the default Dataset and charges PPE per record

Ideal for BD teams, GovCon consultants, and lead pipelines that need fresh **federal contract leads** without building against SAM.gov yourself.

***

### Input

Matches `.actor/input_schema.json`. Empty filters = unfiltered search for that field.

| Field | Type | Description |
|-------|------|-------------|
| `keywords` | string | Optional title keyword (SAM.gov `title`). Leave empty — title strings under-sample |
| `naics` | string\[] | NAICS codes (2–6 digits). Each code is queried separately, then de-duped |
| `setAsideTypes` | string\[] | Official set-aside codes (`SBA`, `8A`, `HZC`, `SDVOSBC`, `WOSB`, …) |
| `postedWithinDays` | number | Lookback window (1–365). Ignored if `sinceLastRun` has a watermark |
| `maxItems` | number | Cap on records returned / charged (1–10,000) |
| `sinceLastRun` | boolean | Only opportunities posted after the last successful run |

API key is **not** an input field. Set secret `SAM_API_KEY` in Apify Console (see Environment).

See `sample_input.json` (labeled **SAMPLE**). Recommended: `naics` + `setAsideTypes` + `postedWithinDays=30`, `keywords` empty.

#### Common set-aside codes

| Code | Meaning |
|------|---------|
| `SBA` | Total Small Business Set-Aside |
| `SBP` | Partial Small Business Set-Aside |
| `8A` | 8(a) Set-Aside |
| `HZC` | HUBZone Set-Aside |
| `SDVOSBC` | SDVOSB Set-Aside |
| `WOSB` | Women-Owned Small Business Set-Aside |
| `EDWOSB` | EDWOSB Set-Aside |
| `VSA` | Veteran-Owned Small Business (VA) |

Full list: [GSA Opportunities API — Set-Aside Values](https://open.gsa.gov/api/get-opportunities-public-api/).

#### Not in v1 (do not advertise)

- Notice-type filter (`ptype`: solicitation / sources sought / …). API has it; v1 does not filter on it
- Place-of-performance state filter
- `daysUntilDeadline`, contact / POC, or description body (API can return POC; v1 does not emit it)
- USAspending award join
- Bid/no-bid or win scores

`noticeType` is still **emitted** on each row when SAM returns `type`.

***

### Output

Each dataset item (see `.actor/dataset_schema.json`):

| Field | Description |
|-------|-------------|
| `opportunityId` | SAM.gov notice ID |
| `title` | Opportunity title |
| `agency` | Agency path (`fullParentPathName`) |
| `naics` | NAICS code |
| `setAside` | Set-aside description / code |
| `postedDate` | Posted date |
| `responseDate` | Response / offer deadline |
| `placeOfPerformance` | Formatted place string |
| `noticeUrl` | Public SAM.gov notice URL |
| `attachmentUrls` | Array of attachment / resource URLs |

Extra fields (`solicitationNumber`, `noticeType`, `classificationCode`, `active`) are included for power users. Unknown values are `null` or `[]`. We do not fill them with guesses.

See `sample_output.json` (**SAMPLE** — fictional IDs, `example.invalid` hosts). Not live SAM.gov data.

***

### Environment variables

| Variable | Required | Description |
|----------|----------|-------------|
| `SAM_API_KEY` | Yes (live) | Free public API key from SAM.gov. Actor secret `@SAM_API_KEY` |
| `ACTOR_TEST_PAY_PER_EVENT` | No | `true` to simulate PPE locally |
| `SAMPLE_MODE` | No | `true` to push `sample_output.json` without calling SAM.gov |

#### How to get a free SAM.gov API key

1. Create / sign in at <https://sam.gov>
2. Open **Account Details** (Workspace → Profile / Account Details)
3. Request / view your **Public API Key** (password re-entry may be required)
4. Store it as `SAM_API_KEY` — never commit it; this Actor redacts `api_key` in logs

**Rate limits** depend on account role (basic public keys have lower daily caps than entity / system accounts). Prefer entity registration for higher volume. Official docs: [open.gsa.gov — Get Opportunities Public API](https://open.gsa.gov/api/get-opportunities-public-api/).

***

### FAQ

**Is this the official API?** Yes. Get Opportunities v2. Not a headless browser.

**Why do I need a key?** GSA requires `api_key` on every search. We cannot hide that. Competitors that skip the key are scraping HTML.

**Do you include attachments?** Yes — `attachmentUrls` from SAM `resourceLinks` when present.

**Do you join USAspending?** Not in v1.

**Do you invent win scores?** No.

**Should I search “PPE” in the title?** No. A live probe of NAICS `339113` + title `personal protective equipment` over 7d returned **0**. Same NAICS + solicitation type over 30d returned **23**. Use NAICS + set-aside.

**Do you emit POC / contact?** Not in v1. The API can return it; we do not advertise or map it yet.

**Why is pricing $3 / 1,000?** That is a **target** for Console monetization on publish day. It is not a live Store price and not a sales claim.

***

### Run locally

```bash
cd apify-actors/sam-gov-opportunities
npm install

## Demo without a SAM key (uses sample_output.json)
SAMPLE_MODE=true npm start

## Live API (requires key) + local PPE simulation
export SAM_API_KEY=your_key_here
cp sample_input.json storage/key_value_stores/default/INPUT.json
ACTOR_TEST_PAY_PER_EVENT=true npm start
```

#### With Apify CLI (recommended)

```bash
npm install -g apify-cli
apify run -p
```

Create `.env` from `.env.example` and fill in `SAM_API_KEY`.

Without `SAM_API_KEY`, the Actor **falls back to SAMPLE\_MODE** so `npm start` still succeeds for scaffolding / CI.

***

### Monetization (PPE)

- **Model:** Pay-per-event
- **Event name:** `opportunity-record`
- **Target price:** **$3.00 / 1,000 records** → **$0.003 per event**
- **Implementation:** `Actor.pushData(item, 'opportunity-record')`. If charge is unavailable locally, the Actor stubs charge and still pushes data.

Configure the event price in **Apify Console → Actor → Monetization** on publish day (pricing is platform-side; code only emits the event name). Local test:

```bash
ACTOR_TEST_PAY_PER_EVENT=true SAMPLE_MODE=true npm start
## Inspect storage/datasets/charging-log/ for simulated charges
```

This scaffold documents a **pricing target**. There are no live sales, Store rankings, or revenue claims.

***

### Project layout

```
sam-gov-opportunities/
├── .actor/
│   ├── actor.json
│   ├── input_schema.json
│   ├── output_schema.json
│   └── dataset_schema.json
├── src/
│   ├── main.js          # Actor entry — fetch, map, push, PPE
│   ├── sam-client.js    # Official SAM.gov v2 search client
│   └── mapper.js        # Response → output schema
├── sample_input.json
├── sample_output.json
├── Dockerfile
├── package.json
├── README.md
└── PUBLISH-CHECKLIST.md
```

***

### Publish (when Apify account is unlocked)

Do **not** push until Max has Apify credentials. Then follow **`PUBLISH-CHECKLIST.md`**.

```bash
apify login
apify push
## Then set PPE pricing in Console: opportunity-record @ $0.003
## Publish to Store under maxwellai/sam-gov-opportunities (confirm namespace)
```

***

### Compliance notes

- Official public API only (no HTML scraping of authenticated SAM.gov pages)
- API key stays in env / Apify secrets (`@SAM_API_KEY`)
- Date windows respect the **1-year** SAM.gov search limit
- Sample files are labeled **SAMPLE** and use `example.invalid` attachment hosts

***

### Categories (Store)

Lead generation · Business · Open data · Developer tools

***

### Do not claim

- User counts, “#1”, Featured
- “45,000+ active opportunities” unless we count them this run
- Revenue, win rates, bid/no-bid scores
- That no other SAM.gov Actor exists
- Live Store price until Console monetization is saved
- Notice-type / state / contact / description / USAspending fields that v1 does not emit or filter

***

### Support / MaxwellAI

Built for MaxwellAI (federal contract lead gen).
API reference: [GSA Open Technology — Get Opportunities Public API](https://open.gsa.gov/api/get-opportunities-public-api/)

# Actor input Schema

## `keywords` (type: `string`):

Optional title keyword (SAM.gov `title` param). Title strings under-sample — a live 2026-09-13 probe of NAICS 339113 + title "personal protective equipment" over 7d returned 0 notices. Leave empty and filter by NAICS + set-aside.

## `naics` (type: `array`):

One or more NAICS codes (up to 6 digits). Each code is queried separately and results are de-duplicated by opportunityId. Each code must be 2–6 digits.

## `setAsideTypes` (type: `array`):

SAM.gov set-aside codes (e.g. SBA, 8A, HZC, SDVOSBC, WOSB). Empty = no set-aside filter.

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

Only return opportunities posted in the last N days. Ignored when sinceLastRun is true and a prior run timestamp exists. Max effective window is 365 days (SAM.gov API limit).

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

Maximum opportunity records to return (and charge for under PPE).

## `sinceLastRun` (type: `boolean`):

If true, only fetch opportunities posted after the previous successful run (stored in the Actor key-value store). Falls back to postedWithinDays on first run.

## Actor input object example

```json
{
  "naics": [
    "339113",
    "423450"
  ],
  "setAsideTypes": [
    "SBA"
  ],
  "postedWithinDays": 30,
  "maxItems": 100,
  "sinceLastRun": false
}
```

# Actor output Schema

## `opportunities` (type: `string`):

Dataset of federal contract opportunity records scraped from SAM.gov.

# 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": "",
    "naics": [
        "339113",
        "423450"
    ],
    "setAsideTypes": [
        "SBA"
    ],
    "postedWithinDays": 30,
    "maxItems": 100,
    "sinceLastRun": false
};

// Run the Actor and wait for it to finish
const run = await client.actor("maxwellai/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 = {
    "keywords": "",
    "naics": [
        "339113",
        "423450",
    ],
    "setAsideTypes": ["SBA"],
    "postedWithinDays": 30,
    "maxItems": 100,
    "sinceLastRun": False,
}

# Run the Actor and wait for it to finish
run = client.actor("maxwellai/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 '{
  "keywords": "",
  "naics": [
    "339113",
    "423450"
  ],
  "setAsideTypes": [
    "SBA"
  ],
  "postedWithinDays": 30,
  "maxItems": 100,
  "sinceLastRun": false
}' |
apify call maxwellai/sam-gov-opportunities --silent --output-dataset

```

## MCP server setup

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