# Sam Gov Federal Contracts Rfp Opportunities Scraper (`arezdev/sam-gov-federal-contracts-rfp-opportunities-scraper`) Actor

Find public SAM.gov contract opportunities by keyword, NAICS and notice type. Extract response deadlines, set-asides, published government contacts and attachment links. Export structured notices to build and monitor your GovCon pipeline.

- **URL**: https://apify.com/arezdev/sam-gov-federal-contracts-rfp-opportunities-scraper.md
- **Developed by:** [Arezdev](https://apify.com/arezdev) (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 contract opportunity extracteds

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

**Find active federal contract opportunities, response deadlines, public government contacts, and current attachment links from SAM.gov in one structured dataset.**

Build a GovCon pipeline around solicitations, presolicitations, combined synopsis/solicitations, Sources Sought, and award notices. Search by keyword, narrow by NAICS, and focus on small business programs. Export clean JSON, CSV, Excel, or XML from Apify.

Best for building a GovCon opportunity pipeline. Each successful row is a notice; enable details for public contacts and attachment links. This Actor uses public SAM.gov website endpoints, whose availability and response formats can change.

### Ready-to-run example

The Input form includes public example values. A fresh `apify run` uses the input schema defaults. To explicitly select the small sample, including when input was saved by an earlier local run, use:

```sh
apify run --input-file .actor/input.example.json
```

You can also paste [.actor/input.example.json](.actor/input.example.json) into the JSON input editor in Apify Console. Replace the example sources with your own targets and increase the limits when you are ready.

### How to use

1. Enter keywords such as `cybersecurity`, `software`, or a service your business supplies.
2. Optionally add a NAICS code and choose the notice types that interest you. Add `a` to include Award Notices; the default types are `o`, `p`, `k`, and `r`.
3. Keep **Fetch details, contacts, and attachments** enabled to retrieve public POC emails, phone numbers, classifications, performance locations, and attachment links.
4. Choose a maximum number of results and start the Actor. The prefilled example saves at most three rows.
5. Open the Output tab, inspect the table, and export the results or connect the dataset to your workflow.

Example input:

```json
{
  "keywords": "cybersecurity",
  "noticeTypes": ["o", "p", "k", "r"],
  "onlySmallBusinessSetAside": false,
  "fetchFullDetailsAndContacts": true,
  "maxItems": 3,
  "timeoutSecs": 15,
  "proxyConfiguration": { "useApifyProxy": false }
}
```

No SAM.gov account or API key is required for the website endpoints used by this Actor. Direct requests are attempted first. To guarantee that no proxy is used, keep `useApifyProxy: false` as in the example.

Schedule daily or hourly runs in Apify Console to monitor changes. Use `opportunityId` as your upsert key and compare `responseDeadline`, `title`, and attachment URLs across runs. Each run produces its own snapshot; historical deduplication belongs in the destination system.

### What data can you extract?

| Fields | Meaning |
| --- | --- |
| `opportunityId`, `samUrl` | Notice identifier and canonical SAM.gov link |
| `status`, `errorMessage` | Extraction outcome and safe diagnostic information |
| `solicitationNumber`, `title`, `noticeType` | Procurement identifiers and human-readable notice type |
| `departmentName`, `agencyName` | Publishing department and agency from the organization hierarchy |
| `naicsCode`, `setAsideDescription` | Primary NAICS classification and small business eligibility program |
| `postedDate`, `responseDeadline`, `daysUntilDeadline` | Published timestamps and remaining time |
| `placeOfPerformanceCity`, `placeOfPerformanceState` | Published performance location, when available |
| `primaryPocName`, `primaryPocEmail`, `primaryPocPhone` | Public primary government contact |
| `secondaryPocEmail` | Public secondary contact email |
| `attachmentUrls` | Current public attachment downloads and external resource links |
| `scrapedAt` | Extraction timestamp in UTC |

Every row contains all 22 output fields. Missing text is an empty string; missing attachments are `[]`; an unknown deadline produces `daysUntilDeadline: null`. All timestamps are normalized to UTC. Date-only and timezone-free timestamps are interpreted as UTC. `daysUntilDeadline` is the ceiling of the difference in 24-hour periods, so one hour remaining is `1` and a deadline more than one day ago is negative.

The primary contact is selected by its published `primary` designation, even if listed after a secondary contact. The performance location comes from the notice rather than the contracting office address. The NAICS filter can match any published NAICS code; the output reports the primary code.

### Input settings

| Setting | Default | Description |
| --- | --- | --- |
| `keywords` | `"software"` | Up to 500 characters; an empty string searches all active notices. Console prefill: `"cybersecurity"`. |
| `naicsCode` | Empty | Optional two to six digit NAICS code or sector/subsector prefix, for example `541512` or `54`. |
| `noticeTypes` | `["o","p","k","r"]` | Notice codes below. An empty list includes all supported notice types. |
| `onlySmallBusinessSetAside` | `false` | Include small business set-asides and the programs listed below. |
| `fetchFullDetailsAndContacts` | `true` | Fetch details, public POCs, performance location, and public resource URLs. |
| `maxItems` | `50` | Total saved rows, including errors; range `1–2000`. Console prefill: `3`. |
| `timeoutSecs` | `15` | Per-request total timeout, including DNS, redirects, and fallback attempts; range `3–60`. |
| `maxConcurrency` | `25` | Concurrent opportunities; range `1–100`. Each enriched opportunity makes up to two parallel requests. |
| `proxyConfiguration` | Optional | Proxy settings described below. |

| Notice code | Notice type |
| --- | --- |
| `o` | Solicitation |
| `p` | Presolicitation |
| `k` | Combined Synopsis/Solicitation |
| `r` | Sources Sought |
| `a` | Award Notice |
| `s` | Special Notice |
| `u` | Justification |
| `g` | Sale of Surplus Property |
| `i` | Intent to Bundle Requirements |

The small business filter includes total/partial small business, 8(a), HUBZone, SDVOSB, WOSB, EDWOSB, veteran-owned small business, and Indian Small Business Economic Enterprise codes. Local-area and general Indian enterprise set-asides do not imply small business eligibility and are excluded by this filter.

When full enrichment is disabled, the Actor saves search metadata with blank contacts and attachment lists. NAICS and set-aside filtering still fetches classification details to verify matches. The website search response often omits classifications and performance location, so those fields may be blank in an unfiltered search-only run.

With no proxy configuration, the Actor starts with a direct request. On HTTP `403`, `429`, or `503`, it can try the Apify `SHADER` datacenter group and then the account's default datacenter proxy. Configured groups, including residential proxies, are tried only if you explicitly set `useApifyProxy: true`. Custom `proxyUrls` are used exclusively. `useApifyProxy: false` disables automatic proxies. Proxy access and traffic can have separate costs.

### Output example

Illustrative record with fictional contact details:

```json
{
  "opportunityId": "0123456789abcdef0123456789abcdef",
  "status": "SUCCESS",
  "solicitationNumber": "TEST-2026-001",
  "title": "Cybersecurity and software support",
  "noticeType": "Solicitation",
  "departmentName": "EXAMPLE DEPARTMENT",
  "agencyName": "EXAMPLE AGENCY",
  "naicsCode": "541512",
  "setAsideDescription": "Total Small Business Set-Aside (FAR 19.5)",
  "postedDate": "2026-10-19T20:20:30.570Z",
  "responseDeadline": "2026-10-25T17:00:00.000Z",
  "daysUntilDeadline": 6,
  "placeOfPerformanceCity": "Arlington",
  "placeOfPerformanceState": "VA",
  "primaryPocName": "Alex Example",
  "primaryPocEmail": "primary@example.gov",
  "primaryPocPhone": "+1 (202) 555-0100",
  "secondaryPocEmail": "second@example.gov",
  "attachmentUrls": [
    "https://sam.gov/api/prod/opps/v3/opportunities/resources/files/abcdef0123456789abcdef0123456789/download"
  ],
  "samUrl": "https://sam.gov/opp/0123456789abcdef0123456789abcdef/view",
  "scrapedAt": "2026-10-20T12:00:00.000Z",
  "errorMessage": ""
}
```

### Use the results in Clay, n8n, Make, or your API

- **Clay:** Import the dataset CSV or retrieve its JSON through an HTTP source. Map `opportunityId` to the unique key and use NAICS, department, and deadlines to segment your pipeline.
- **n8n:** Run the Actor through Apify's integration or an HTTP Request node, wait for completion, retrieve dataset items, and upsert by `opportunityId` into your CRM or database.
- **Make:** Schedule an Actor run, retrieve dataset items, and route rows to your chosen destination based on status, set-aside, or remaining days.
- **Your API:** Use the run's `defaultDatasetId` to retrieve dataset items through Apify's Dataset API. Obtain the generated dataset URL from the run or Output tab and manage authentication through your integration's credential store.

Filter for `status === "SUCCESS"` before using records in downstream automations. Keep `ERROR` rows for diagnostics or targeted reruns. Missing contact data means SAM.gov did not publish it; the Actor does not infer email addresses.

### Pricing and limits

The custom PPE event is **`opportunity-scraped`**. Only saved `SUCCESS` rows trigger this event. **`ERROR` rows cost `$0` in custom event fees**, including partial failures where search metadata or contact data could still be retained.

The event price is configured in Apify Console and displayed on the Actor's Store page. For a concrete illustration, at `$0.001` per successful opportunity, **1,000 successful opportunities cost `$1.00` in event fees**. If 900 succeed and 100 fail, the same illustrative rate produces `$0.90` in event fees. This is an example, not a published price. Apify compute, storage, and optional proxy traffic can still have separate costs.

Free Apify accounts can save at most **25 total rows per cloud run**, including `ERROR` rows. Paid accounts and local runs use the requested `maxItems`, up to 2,000. A run stops cleanly when its item cap or custom event budget is exhausted.

Search results are fetched in pages of 25, ordered by most recently modified notice. IDs are deduplicated within the run. SAM.gov limits the accessible search window to 10,000 matching notices; narrow your keywords or NAICS filter for broad searches. Active notices can have past response deadlines. The Actor follows SAM.gov's active flag and does not silently discard overdue notices.

### FAQ and troubleshooting

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

No. This Actor uses SAM.gov's publicly accessible website search and opportunity endpoints. The separate [GSA Get Opportunities Public API](https://open.gsa.gov/api/get-opportunities-public-api/) requires an API key; it is not called by this Actor.

**Why are contacts, locations, or attachments missing?**

Publication is optional. Some notices have no POCs or performance location, and some have no attachments. Deleted, restricted, export-controlled, explicitly gated, and unavailable resources are omitted. Published download URLs can still require a browser session or be unavailable when opened later. The Actor lists public links without downloading or checking attachment contents.

**Why does a row have `ERROR` even though some data was extracted?**

With full enrichment enabled, a failed detail or resource request marks the row `ERROR`. Available search metadata, successfully retrieved contacts, and public links are retained. The row does not trigger the custom PPE event. A failed search page produces one uncharged diagnostic row and preserves earlier results.

**What should I do about HTTP 403, 429, or timeouts?**

Lower `maxConcurrency`, narrow your search, or raise `timeoutSecs` within its allowed range. Enable an appropriate proxy configuration if you want proxy fallback. When the environment does not provide Apify proxy credentials, automatic proxy tiers are skipped without querying your account. Upstream availability and request throttling prevent any guarantee that every request will succeed.

**Does the Actor run with Limited Permissions?**

Yes. It uses the current run, its default dataset, SDK pricing state, and environment-provided account flags. It never requests `/v2/users/me`. Run it with `LIMITED_PERMISSIONS` in Apify Console. See [Apify Actor permissions](https://docs.apify.com/actors/running/permissions).

**Does it use a browser or bypass restricted attachments?**

Extraction uses streaming HTTP requests. It does not require a browser or access restricted resources.

**Can I run it locally or build the image?**

Use Node.js 22 or later:

```sh
npm ci --ignore-scripts
npm run build
npm test
npm run smoke
npm start
```

Local input can be placed in `storage/key_value_stores/default/INPUT.json`; without input, the requested defaults apply. Local SDK output is saved under `storage/datasets/default`. `npm run smoke` performs a real direct three-item extraction in an isolated worker and reports counts, elapsed time, and peak RSS without printing POC records. `npm run smoke -- --search-only` checks search metadata alone.

```sh
docker build -t sam-gov-opportunities .
docker run --rm sam-gov-opportunities
```

The image uses two `node:22-alpine` stages, strict TypeScript compilation, production dependencies, a non-root user, a 160 MB V8 heap, and `UV_THREADPOOL_SIZE=64`. HTML salvage support in the shared HTTP client is tested; incomplete JSON is always rejected.

**How should the Store listing be configured?**

Publish with 256 MB memory and Limited Permissions. In Apify Console, configure the custom event `opportunity-scraped` at your chosen unit price. Leave automatic `apify-default-dataset-item` charging disabled so diagnostic rows remain free of event fees. If automatic item pricing is enabled at a nonzero rate, the Actor stops before saving or billing records and reports `AUTOMATIC_ITEM_PRICING_MUST_BE_DISABLED`. Actor pricing is managed by the platform rather than embedded in `actor.json`; see [Apify PPE documentation](https://docs.apify.com/sdk/js/docs/3.3/guides/pay-per-event).

# Actor input Schema

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

Search terms passed to SAM.gov. An empty string searches all active notices. Try a specific service or product.

## `naicsCode` (type: `string`):

Optional two to six digit NAICS code, such as 541512. Shorter codes select a sector or subsector.

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

o: Solicitation; p: Presolicitation; k: Combined Synopsis/Solicitation; r: Sources Sought; a: Award Notice; s: Special Notice; u: Justification; g: Sale of Surplus Property; i: Intent to Bundle Requirements. An empty list includes all supported types.

## `onlySmallBusinessSetAside` (type: `boolean`):

Include small business, 8(a), HUBZone, SDVOSB, WOSB, EDWOSB, veteran-owned, and Indian small business set-asides. Classification details are fetched to verify matches, even when full enrichment is disabled.

## `fetchFullDetailsAndContacts` (type: `boolean`):

Retrieve NAICS, set-aside, performance location, primary and secondary public contacts, and current public attachment URLs. Partial failures are saved as uncharged ERROR rows with available data.

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

Maximum total SUCCESS and ERROR rows. Free Apify accounts are capped at 25 rows per cloud run.

## `timeoutSecs` (type: `integer`):

Total time budget per HTTP request, including DNS, redirects, protocol fallback, and proxy attempts.

## `maxConcurrency` (type: `integer`):

Maximum opportunities being enriched at once. Each may make two parallel requests. Lower this value if SAM.gov throttles your run.

## `proxyConfiguration` (type: `object`):

Optional. Direct connections run first. Blocked requests can fall back to Apify datacenter proxies. Set useApifyProxy to false for direct-only requests. Residential or configured Apify fallback requires explicitly enabling useApifyProxy. Custom proxyUrls are used exclusively.

## Actor input object example

```json
{
  "keywords": "cybersecurity",
  "noticeTypes": [
    "o",
    "p",
    "k",
    "r"
  ],
  "onlySmallBusinessSetAside": false,
  "fetchFullDetailsAndContacts": true,
  "maxItems": 3,
  "timeoutSecs": 15,
  "maxConcurrency": 25
}
```

# Actor output Schema

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

No description

# 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": "cybersecurity",
    "maxItems": 3
};

// Run the Actor and wait for it to finish
const run = await client.actor("arezdev/sam-gov-federal-contracts-rfp-opportunities-scraper").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": "cybersecurity",
    "maxItems": 3,
}

# Run the Actor and wait for it to finish
run = client.actor("arezdev/sam-gov-federal-contracts-rfp-opportunities-scraper").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": "cybersecurity",
  "maxItems": 3
}' |
apify call arezdev/sam-gov-federal-contracts-rfp-opportunities-scraper --silent --output-dataset

```

## MCP server setup

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

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/OTOipEtufXpFn5Rf3/builds/HHSgLaimcRbarrbBk/openapi.json
