# SAM.gov Scraper: Contract Opportunities & Bid Alerts (`studded_columbine/samgov-contract-opportunities`) Actor

Scrape SAM.gov federal contract opportunities: RFPs, RFQs, solicitations, sources sought and award notices. Filter by keyword, NAICS, PSC, set-aside (8(a), SDVOSB, WOSB, HUBZone), state, agency and deadline. Daily bid alerts return only new notices. No API key. Export CSV, Excel, JSON.

- **URL**: https://apify.com/studded_columbine/samgov-contract-opportunities.md
- **Developed by:** [Tenderly Data](https://apify.com/studded_columbine) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.00 / 1,000 contract opportunities

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

### What does SAM.gov Scraper do?

**SAM.gov Scraper** pulls **U.S. federal contract opportunities** from SAM.gov — RFPs, RFQs, solicitations, combined synopsis/solicitations, sources sought, presolicitations, special notices and award notices — and turns them into a clean spreadsheet or JSON feed. Filter by **keyword, NAICS code, PSC code, set-aside (8(a), SDVOSB, WOSB, EDWOSB, HUBZone, small business), state, agency, posted date and response deadline**, then schedule it as a **daily SAM.gov bid alert** that returns **only new notices**.

- **No SAM.gov account, API key or login.** The scraper reads SAM.gov's official public *Contract Opportunities* daily data extract (about 71,000 notices; about 20,000 of them open on a typical day), so there are no API rate limits to hit.
- **Fast:** a filtered search over the whole file typically finishes in under a minute.
- **Pay per result:** US$2 per 1,000 opportunities plus US$0.01 per run. A first test run of 25 notices costs about US$0.06.
- Export to **CSV, Excel, JSON, XML or HTML**, or send results to Google Sheets, Slack, email, Zapier, Make or your CRM with Apify integrations and the Apify API.

### Who is this SAM.gov scraper for?

- **Small businesses and GovCon BD teams** who want a morning list of new bids in their NAICS codes instead of re-running SAM.gov searches by hand.
- **Set-aside hunters:** for example every open *SDVOSB* construction opportunity in Texas, or *8(a)* IT services for the Army.
- **Proposal writers and consultants** tracking deadlines for several clients (one saved search per client).
- **Market researchers and subcontractors:** award notices with awardee and award amount, by agency and NAICS, to see who is winning work.
- **Developers and AI agents** who need SAM.gov contract data as normalized JSON (full descriptions included) for RAG, dashboards or lead scoring.

### How to scrape SAM.gov contract opportunities

1. Click **Try for free**. The form is prefilled with a small search (keyword "software", posted in the last 7 days, 25 results).
2. Add your filters: keywords, NAICS codes or prefixes (`5415` matches 541511, 541512…), set-aside types, states, agencies, deadlines.
3. Click **Start**. Results appear in the **Opportunities** table; switch to the **Awards** view for award notices.
4. **For daily SAM.gov alerts:** turn on *Only new since last run*, give the search a *Saved search name*, and add a daily **Schedule** in Apify. Each run then returns only notices this search has not returned before.

Apify's free plan includes US$5 of monthly credit to spend in Apify Store, which is enough for roughly 2,400 opportunities with this Actor.

### Input examples

**Daily bid alert:** new IT services opportunities set aside for service-disabled veteran-owned small businesses (SDVOSB) in Virginia, Maryland or DC, due in the next 30 days:

```json
{
  "naicsCodes": ["5415"],
  "setAsideCodes": ["SDVOSBC", "SDVOSBS"],
  "states": ["VA", "MD", "DC"],
  "deadlineWithinDays": 30,
  "onlyNewSinceLastRun": true,
  "stateKey": "sdvosb-it-dmv",
  "firstRunBehavior": "returnAll",
  "maxResults": 500
}
```

**Keyword search across all agencies**, solicitations only, soonest deadline first:

```json
{
  "keywords": ["zero trust", "cybersecurity"],
  "excludeKeywords": ["request for information"],
  "noticeTypes": ["Solicitation", "Combined Synopsis/Solicitation"],
  "sortBy": "responseDeadlineAsc",
  "maxResults": 200
}
```

**Who won contracts in my NAICS?** Award notices for construction (NAICS 236/237/238) posted in the last 30 days:

```json
{
  "naicsCodes": ["236", "237", "238"],
  "noticeTypes": ["Award Notice"],
  "postedWithinDays": 30,
  "activeOnly": false,
  "maxResults": 1000
}
```

### Filters

| Filter | How it works |
|---|---|
| Keywords | Any/all mode, phrases, whole-word matching ("AI" won't match "maintenance"), exclude keywords, title and/or description |
| NAICS codes | Codes or prefixes — `5415` matches 541511, 541512, 541519… |
| PSC codes | Product Service Code prefixes — `D3`, `R4`, `S201`… |
| Set-aside | Total/partial small business, 8(a), HUBZone, SDVOSB, WOSB, EDWOSB, VOSB, Indian enterprise, local area, or "no set-aside" |
| Location | State/territory and country; uses place of performance, falling back to the contracting office (only about 25% of notices list a place of performance) |
| Agency | Text match on department, sub-tier and office ("veterans affairs", "army", "NASA") |
| Dates | Posted date range or "last N days"; response-deadline range or "due within N days" |
| Notice type | Solicitation, combined synopsis/solicitation, sources sought, presolicitation, award notice, special notice… |
| Active only | Skips notices whose deadline or archive date has passed (on by default) |
| **Only new since last run** | Remembers what each saved search already returned; optional silent baseline on the first run so you only pay for genuinely new notices |

Results are sorted newest first by default (or soonest deadline, oldest, or file order) and capped by **Max results**.

**Privacy by default:** contracting-officer names, emails and phone numbers are **not** included unless you switch on *Include contracting officer contacts*. Use them only to respond to the solicitation.

### Output: SAM.gov data fields

One row per notice. Every row has the same flat set of fields (missing values are `null`), so CSV and Excel exports line up cleanly:

```json
{
  "noticeId": "83fb6bf76e624533b8a98b91f8c0cbc5",
  "title": "Program Executive Office (PEO) Defense Healthcare Management Systems (DHMS) Deployment Solutions",
  "solicitationNumber": "Deployment_Solutions_2026",
  "noticeType": "Award Notice",
  "department": "DEPT OF DEFENSE",
  "subTier": "DEFENSE HEALTH AGENCY (DHA)",
  "office": "DEFENSE HEALTH AGENCY",
  "postedDate": "2026-10-19T19:02:16",
  "responseDeadline": null,
  "daysUntilDeadline": null,
  "archiveDate": "2026-10-31",
  "active": true,
  "setAsideCode": "NONE",
  "setAside": "No Set aside used",
  "naicsCode": "541512",
  "pscCode": "DA01",
  "placeOfPerformanceCity": "Arlington",
  "placeOfPerformanceState": "VA",
  "placeOfPerformanceCountry": "USA",
  "officeCity": "ROSSLYN",
  "officeState": "VA",
  "awardNumber": "HT003826DE013",
  "awardDate": "2026-08-06",
  "awardAmount": 300000000,
  "awardee": "VISTA GLOBAL SOLUTIONS, LLC San Antonio TX 78248 USA",
  "url": "https://sam.gov/workspace/contract/opp/83fb6bf76e624533b8a98b91f8c0cbc5/view",
  "description": "A Square Group LLC*, Frederick, Maryland (HT003826DE002); Accenture Federal Services LLC, …"
}
```

Text is cleaned of the encoding errors (`â€™`, `�`) found in the raw SAM.gov file. A run summary (counts, effective filters, data timestamp) is saved to the key-value store as `OUTPUT`.

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

Pay per event. You only pay for opportunities actually returned, and platform compute is included:

| Event | Price |
|---|---|
| Run start | US$0.01 |
| Opportunity returned | US$0.002 (US$2 per 1,000) |

| Example | Cost |
|---|---|
| "Try for free" prefilled search (25 notices) | US$0.06 |
| 1,000 opportunities in one run | US$2.01 |
| Daily alert returning 25 new notices | US$0.06/day, about US$1.80/month |
| Daily alert with nothing new that day | US$0.01 |
| Baseline run (*Record a baseline, return nothing*) | US$0.01 |

Set *Max cost per run* in the run options to cap spending; the Actor stops cleanly at your limit.

### Tips for better SAM.gov searches

- **Broad NAICS + keywords** catches more than NAICS alone; many notices carry a loosely related NAICS code.
- **Set-aside "NONE"** returns full-and-open competitions and notices with no set-aside given.
- Use **Record a baseline** on the first run of an alert if you don't want (or pay for) the current backlog.
- Leave *Sort by* on *File order* with a small *Max results* for the fastest possible run.

### Data source and limitations

Data comes from the public SAM.gov *Contract Opportunities* data extract published by the U.S. General Services Administration on [SAM.gov](https://sam.gov/) (public-domain U.S. government data). The extract is refreshed once a day, so a notice posted after the latest extract appears on the next day's run. Descriptions in the extract are capped at about 32,000 characters and attachments are not included; follow the `url` field to the notice on SAM.gov for documents and amendments. This Actor is not affiliated with or endorsed by GSA or SAM.gov.

### FAQ

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

No. The Actor uses SAM.gov's public bulk extract, which needs no account or key.

#### How is this different from the SAM.gov API?

The [SAM.gov Get Opportunities API](https://open.gsa.gov/api/get-opportunities-public-api/) needs an API key, limits requests per day depending on your SAM.gov role, requires a posted-date range of at most one year, and returns at most 1,000 records per page. This Actor filters the full daily extract instead, so there is no key and no daily request quota on your side. The trade-off is freshness: data is as of the latest daily extract.

#### Is SAM.gov free? Why pay for this?

Yes, SAM.gov search and its saved-search email notifications are free. This Actor is for when you want the results as **data**: whole-word keyword matching, NAICS/PSC prefixes, several states and set-asides at once, deduplicated "only new" alerts, and export to CSV/Excel/Sheets/CRM or an API.

#### Can I get SAM.gov email or Slack alerts?

Yes. Schedule the Actor daily with *Only new since last run* on, then add Apify's email or Slack integration to the Actor or task so each run's new notices are sent to you.

#### Can I get historical or archived notices?

The Actor covers notices currently in the extract (active notices plus recent awards). Turn off *Active only* to include notices whose deadline has passed but which are still listed. Long-term archives are not included.

#### Is scraping SAM.gov legal?

This Actor downloads an official bulk file that SAM.gov publishes for public reuse; it does not crawl the SAM.gov website.

#### Can I use it from Python, JavaScript or an AI agent?

Yes. Use the Apify API or client libraries (see the **API** tab), or the Apify MCP server so AI assistants can call it as a tool.

### More from Tenderly Data

- [UK Companies House Scraper](https://apify.com/studded_columbine/uk-companies-house-scraper): lists of newly incorporated UK companies by SIC code and location, with optional directors and beneficial owners.

Questions or feature requests? Open an issue on the **Issues** tab.

# Actor input Schema

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

Words or phrases to look for in the title, solicitation number and (optionally) description. Case-insensitive. Example: "cybersecurity", "janitorial services".

## `keywordMode` (type: `string`):

Match notices containing ANY of the keywords, or ALL of them.

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

Drop notices that mention any of these words or phrases.

## `wholeWordsOnly` (type: `boolean`):

If on, "AI" will not match "maintenance". Turn off for substring matching.

## `searchInDescription` (type: `boolean`):

Also search the full notice description (not just the title and solicitation number).

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

NAICS codes or prefixes. "5415" matches 541511, 541512, etc.

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

Product Service Codes or prefixes, e.g. "D3" (IT services), "R4", "S201".

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

Keep only notices with these set-aside types. "NONE" = no set-aside (full and open).

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

Leave empty for all types.

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

Two-letter codes, e.g. "VA", "TX", "DC". See "Location match" for how this is applied.

## `countries` (type: `array`):

Three-letter ISO codes, e.g. "USA", "JPN", "DEU".

## `locationMatch` (type: `string`):

Only ~25% of notices list a place of performance. The default uses place of performance when present, otherwise the contracting office location.

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

Case-insensitive text matched against department, sub-tier and office, e.g. "veterans affairs", "army", "NASA".

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

Shortcut for a rolling posted-date window. Works well with schedules.

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

Earliest posted date (inclusive).

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

Latest posted date (inclusive).

## `deadlineWithinDays` (type: `integer`):

Only notices due between today and N days from now.

## `responseDeadlineFrom` (type: `string`):

Notices without a deadline are excluded when a deadline range is set.

## `responseDeadlineTo` (type: `string`):

Latest response deadline (inclusive).

## `activeOnly` (type: `boolean`):

Skip notices whose response deadline or archive date has already passed.

## `onlyNewSinceLastRun` (type: `boolean`):

Return only notices this saved search has not returned before. Ideal with a daily schedule. State is kept per search in a named key-value store in your account.

## `stateKey` (type: `string`):

Optional name for the 'new since last run' memory, e.g. "va-it-bids". By default it is derived from your filters, so changing filters starts a fresh search.

## `firstRunBehavior` (type: `string`):

On the first run of a monitored search: return all current matches, or just record them silently so that only genuinely new notices are returned afterwards (and charged).

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

Maximum number of opportunities to return. 0 = no limit.

## `sortBy` (type: `string`):

Order of results. Sorting scans the whole file (about 10 s); "File order" can stop early once Max results is reached.

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

Include the notice description text.

## `descriptionMaxLength` (type: `integer`):

Truncate descriptions to this many characters. 0 = full text.

## `includeContacts` (type: `boolean`):

Off by default. When on, adds the primary/secondary point-of-contact name, title, email and phone published on the notice. Use only for responding to the solicitation; do not use for unsolicited marketing.

## `sourceUrl` (type: `string`):

Leave empty to use the official SAM.gov Contract Opportunities daily extract.

## Actor input object example

```json
{
  "keywords": [
    "software"
  ],
  "keywordMode": "any",
  "wholeWordsOnly": true,
  "searchInDescription": true,
  "locationMatch": "placeOfPerformanceOrOffice",
  "postedWithinDays": 7,
  "activeOnly": true,
  "onlyNewSinceLastRun": false,
  "firstRunBehavior": "returnAll",
  "maxResults": 25,
  "sortBy": "postedDateDesc",
  "includeDescription": true,
  "descriptionMaxLength": 0,
  "includeContacts": false
}
```

# Actor output Schema

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

All matching contract opportunities returned by this run (default dataset). The 'Opportunities' view is selected by default; switch to 'Awards' for award notices.

## `runSummary` (type: `string`):

JSON summary of the run: results pushed, matched filters, already-seen notices skipped, rows scanned, filters used and source file timestamp.

# 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"
    ],
    "postedWithinDays": 7,
    "maxResults": 25
};

// Run the Actor and wait for it to finish
const run = await client.actor("studded_columbine/samgov-contract-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": ["software"],
    "postedWithinDays": 7,
    "maxResults": 25,
}

# Run the Actor and wait for it to finish
run = client.actor("studded_columbine/samgov-contract-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": [
    "software"
  ],
  "postedWithinDays": 7,
  "maxResults": 25
}' |
apify call studded_columbine/samgov-contract-opportunities --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,studded_columbine/samgov-contract-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/ovMgC5RVVZVBDUQ18/builds/0ti7jjMa4LThPOVgi/openapi.json
