# SAM.gov Contract Opportunities API — Federal RFPs & Bids (`samgov-tools/samgov-opportunities`) Actor

Export SAM.gov RFPs, solicitations, sources sought and award notices to JSON/CSV. No API key needed: official public daily extract (up to ~1 day old), or live API with your own free key. Filter NAICS, set-aside, type, state, date. $2 per 1,000 results.

- **URL**: https://apify.com/samgov-tools/samgov-opportunities.md
- **Developed by:** [AgentReady Tools](https://apify.com/samgov-tools) (community)
- **Categories:** Lead generation, Business
- **Features:** Limited permissions
- **Stats:** 1 total users, 0 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$2.00 / 1,000 opportunity returneds

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 Contract Opportunities API — Federal RFPs & Bids (No API Key Needed)

Export US federal contract opportunities from SAM.gov — RFPs, solicitations, combined synopsis/solicitations, sources-sought and pre-solicitation notices, special notices and award notices — to JSON, CSV or Excel. Every run returns one normalized record per notice with NAICS, PSC, set-aside, agency path, place of performance, response deadline, points of contact and the SAM.gov link.

**No API key needed.** By default the Actor streams the **official public SAM.gov daily extract** of active contract opportunities (`ContractOpportunitiesFullCSV`, published by SAM.gov Data Services) and filters it for you. That data is **up to about one day old**. If you need live, up-to-the-minute data, add your own free SAM.gov API key and the Actor uses the official Get Opportunities API instead.

No page scraping, no shared keys, no unofficial endpoints.

### For AI agents

Returns one normalized dataset item per SAM.gov federal contract opportunity, with title, dates, industry codes, contacts and source metadata.

**MCP:** connect your client to [the Actor's Apify MCP endpoint](https://mcp.apify.com?tools=samgov-tools/samgov-opportunities). Example prompt: “Find 5 active federal solicitations for renovation work in Virginia using samgov-tools/samgov-opportunities.” Call with `{}` for recent active records without a SAM.gov key, or use 1–3 filters such as `keywords`, `state` and `noticeType`. Add `maxItems` to cap results. Read `noticeId` for identity, `title` and `responseDeadline` for triage, `uiLink` for the notice, and `dataSource`/`extractDate` for freshness. `description` is text in daily-extract mode and a URL in API mode; unavailable fields are `null`.

**REST:** set `APIFY_TOKEN` to your Apify token, then run with a small input:

```sh
curl --request POST \
  'https://api.apify.com/v2/acts/samgov-tools~samgov-opportunities/run-sync-get-dataset-items' \
  --header "Authorization: Bearer $APIFY_TOKEN" \
  --header 'Content-Type: application/json' \
  --data '{"keywords":"renovation","maxItems":5}'
```

**JavaScript:** install `apify-client` and set `APIFY_TOKEN` in your environment:

```js
import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('samgov-tools/samgov-opportunities').call({
  keywords: 'renovation',
  maxItems: 5,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items);
```

Example JSON item excerpt (illustrative daily-extract data; the complete field list and item appear below):

```json
{
  "noticeId": "example-notice-id",
  "title": "Office renovation",
  "responseDeadline": null,
  "naicsCode": "236220",
  "uiLink": "https://sam.gov/workspace/contract/opp/example-notice-id/view",
  "description": "Renovation of office space.",
  "dataSource": "sam.gov-daily-extract",
  "extractDate": "2026-11-14T04:30:20.000Z"
}
```

The REST response is an array of items; the JavaScript example reads the same dataset. See the complete example JSON item and all 26 field names in [Output](#output) below. Filters can return no matches; `{}` requests up to 10 active notices from the last 30 days.

#### Paying agents without an Apify account (x402 / MPP)

An agent without an Apify account can buy a prepaid Apify API token from Apify AGI (`agi.apify.com`) with x402 (USDC on Base or Solana), MPP (Tempo or Solana) or Skyfire, then use it as the Apify API token in this README's REST example (Actor `samgov-tools~samgov-opportunities`) or as the bearer token for the Apify MCP server. This Actor uses limited permissions, which prepaid tokens require. Apify's current terms: minimum $1, the balance is a hard spending cap, the token expires 14 days after purchase, unused balance is non-refundable, and the feature is experimental. Check `agi.apify.com` for current terms before paying.

The token pays this Actor's normal pay-per-event prices: `opportunity`: $0.002 per stored opportunity. See [Let your AI agent pay for Actor runs](https://docs.apify.com/agents/payments), [x402](https://docs.apify.com/platform/integrations/x402) and [MPP](https://docs.apify.com/integrations/mpp).

### Who it's for

Government contractors, business-development and capture teams, researchers, and builders who want structured US federal contract opportunity data in JSON, CSV, or Excel — for lead lists, daily monitoring, or their own pipelines.

### Two official data sources

| | Daily extract (default, no key) | Live API (your own key) |
| --- | --- | --- |
| Source | Official public SAM.gov Data Services daily CSV extract | Official SAM.gov Get Opportunities Public API |
| Freshness | Up to ~1 day old (each item has `extractDate`) | Live |
| Key needed | No | Your own free SAM.gov public API key |
| Coverage | Active notices only | Active and inactive notices, up to one-year posting window |
| `description` field | Notice description text | Description URL (needs your key to fetch) |
| Quotas | None from SAM.gov | Your key's daily request quota (a new personal key can be very limited) |

`source` = `auto` (default) picks the live API when you supply `samApiKey`, otherwise the daily extract. Force either with `bulk` or `api`. Each item says where it came from in `dataSource` (`sam.gov-daily-extract` or `sam.gov-api`).

### Optional: get a free SAM.gov API key for live data

1. Create a free SAM.gov individual account at [sam.gov](https://sam.gov/).
2. Sign in, then open **[Account Details](https://sam.gov/workspace/profile/account-details)**.
3. Under **Public API Key**, generate or view your key. You may need to re-enter your password or an emailed one-time code.
4. Paste the key into this Actor's `samApiKey` field. It is a secret input.

Never share individual keys or use a shared personal key. Follow the [SAM.gov Terms of Use](https://sam.gov/about/terms-of-use). The Actor uses your key only during the run, never logs it, and never copies it to dataset items. Apify controls encrypted secret-input retention. This project contains no credentials.

### Pricing

**$2 per 1,000 opportunities** ($0.002 each), pay-per-event, from either source. Apify platform usage for the run is included in that price.

- You are charged only for opportunities that are successfully stored. The charge event name is `opportunity`.
- Notices filtered out, duplicates within a run, rejected records, and download/API errors do not generate events.
- The run stops as soon as `maxItems` or your maximum charge is reached; the budget is checked before every charged item.
- Runs are independent: running the same query again can store and charge for the same opportunity again.
- If a run fails or stops early, items already stored may already have been charged. There is no cross-run checkpoint or deduplication.

### Input

| Field | Meaning | Default |
| --- | --- | --- |
| `source` | `auto`, `bulk` (daily extract, no key) or `api` (live, needs key) | `auto` |
| `samApiKey` | Optional secret public API key from your own SAM.gov account | None (daily extract) |
| `keywords` | Title search. API: official `title` parameter. Extract: every word must appear in the title (case-insensitive). Not a full-text search | None |
| `naics` | 2–6 digits. API: `ncode`. Extract: prefix match (`236` matches `236220`) | None |
| `setAside` | Official `typeOfSetAside` code; select from the input form | None |
| `noticeType` | Official `ptype`: u, p, a, r, s, o, g, k, i | None |
| `state` | 2-letter place-of-performance state, e.g. `VA` | None |
| `activeOnly` | Boolean; extract mode requires `Active: Yes`. API mode excludes only string `active: No` (case-insensitive), retaining boolean false or missing status. Use `false` to disable the filter | `true` |
| `postedFrom` | Inclusive start, `MM/dd/yyyy` or `YYYY-MM-DD`; range at most one calendar year | 29 days before `postedTo` (or today UTC) |
| `postedTo` | Inclusive end, `MM/dd/yyyy` or `YYYY-MM-DD`; range at most one calendar year | Today UTC |
| `maxItems` | Maximum stored (and charged) opportunities, 1–10,000 | 10 |
| `pageSize` | API mode only: page size, 1–1,000 | 100 |

If only `postedTo` is supplied, the start defaults to 29 days before it. If only `postedFrom` is supplied, the end defaults to today. Ranges must be ordered and no longer than one calendar year. Raise `maxItems` to get more results; with an empty input the Actor returns the 10 newest active notices in a few seconds.

Example without a key (daily extract):

```json
{
  "keywords": "renovation",
  "naics": "236",
  "setAside": "SBA",
  "noticeType": "o",
  "state": "CA",
  "maxItems": 100
}
```

Example with your own key (live API; the placeholder is not a real key):

```json
{
  "samApiKey": "YOUR_OWN_PUBLIC_API_KEY",
  "keywords": "renovation",
  "naics": "236220",
  "postedFrom": "10/16/2026",
  "postedTo": "11/14/2026",
  "maxItems": 100
}
```

### Output

Each dataset item is one opportunity. Stable fields from both sources: `noticeId`, `title`, `solicitationNumber`, `type`, `baseType`, `postedDate`, `responseDeadline`, `naicsCode`, `classificationCode`, `setAside`, `setAsideCode`, `fullParentPathName`, `fullParentPathCode`, `organizationType`, `officeAddress`, `placeOfPerformance`, `pointOfContact`, `award`, `active`, `uiLink`, `description`, `resourceLinks`, `additionalInfoLink`, `archiveDate`, `dataSource`, `extractDate`.

Missing fields are null. API mode preserves nested addresses, contacts, place of performance and award details; extract mode maps CSV columns into nested objects.

- **Daily extract:** `description` is the notice text from the extract; `extractDate` is when SAM.gov generated the file (from its `Last-Modified` header); `resourceLinks` is null (the extract has no attachment list). `postedDate` keeps the extract's `YYYY-MM-DD HH:MM:SS` format.
- **Live API:** `description` is the returned URL, not text. Any `api_key` query parameter is removed from stored links; fetching a description URL later requires your own key. `extractDate` is null. `responseDeadline` accepts the API's `reponseDeadLine` typo and `responseDeadLine` spelling.

Attachments and external resources may have separate access or use restrictions. The Actor does not download attachments.

Example JSON output item (API mode), reduced from the official API's Example 1 (not a live capture):

```json
{
  "noticeId": "5b345bbb7127b91a3ad577b203fc6f68",
  "title": "Historic Office Renovation ",
  "solicitationNumber": " 47PF0018R0023 ",
  "type": "Award Notice",
  "baseType": "Combined Synopsis/Solicitation",
  "postedDate": "2018-05-04",
  "responseDeadline": null,
  "naicsCode": "236220",
  "classificationCode": "Z",
  "setAside": null,
  "setAsideCode": null,
  "fullParentPathName": null,
  "fullParentPathCode": null,
  "organizationType": "OFFICE",
  "officeAddress": {
    "zipcode": "60604",
    "city": "CHICAGO",
    "countryCode": "USA",
    "state": "IL"
  },
  "placeOfPerformance": {
    "streetAddress": "517 E Wisconsin Ave",
    "city": { "code": "53000", "name": "Milwaukee" },
    "state": { "code": "WI" },
    "zip": "53202",
    "country": { "code": "USA" }
  },
  "pointOfContact": [
    { "type": "primary", "title": "Contracting Officer " }
  ],
  "award": {
    "date": "2018-05-04",
    "number": "47PF0018C0066",
    "amount": "800620"
  },
  "active": "Yes",
  "uiLink": "https://beta.sam.gov/opp/5b345bbb7127b91a3ad577b203fc6f68/view",
  "description": "https://api.sam.gov/prod/opportunities/v1/noticedesc?noticeid=5b345bbb7127b91a3ad577b203fc6f68",
  "resourceLinks": null,
  "additionalInfoLink": null,
  "archiveDate": null,
  "dataSource": "sam.gov-api",
  "extractDate": null
}
```

### Limits and behavior

**Daily extract:** the official file (~200 MB, active notices, newest first) is streamed and parsed on the fly — never fully loaded into memory — and the download is aborted as soon as `maxItems` matches are stored, so typical runs finish in seconds. A query with few matches scans the whole file (usually well under a minute). Network failures are retried; a mid-download failure restarts the download, and already stored notices are not stored or charged again.

**Live API:** requires a public key and a posting range of at most one year. Page size is at most 1,000. Daily request quotas depend on your account role; a basic personal key may be very limited. HTTP 429, server errors, and network or timeout failures receive up to four total attempts with backoff, jitter, and Retry-After support. Other HTTP errors fail clearly. Requests time out after 30 seconds, with 250 ms between pages. The API returns the latest version of each opportunity; it is not a historical version archive.

### FAQ

**Do I need a SAM.gov API key?**\
No. Without a key the Actor uses the official public SAM.gov daily extract (data up to ~1 day old). Add your own free key only if you need live data or inactive/archived notices.

**How fresh is the no-key data?**\
SAM.gov regenerates the public extract daily. Each item carries `extractDate` so you can see exactly which file it came from. Notices posted after that time appear in the next day's extract, or immediately in API mode.

**Does this Actor include a SAM.gov key?**\
No. Supply your own for live data. The Actor never logs the key or writes it to dataset items.

**Does this scrape SAM.gov?**\
No. It uses only the official public Data Services extract and the official Get Opportunities Public API. It does not scrape SAM.gov pages or call the website's internal search.

**Can I search the full notice text?**\
No. `keywords` matches opportunity titles only, in both modes. In daily-extract mode the description text is included in the output, so you can filter it further yourself.

### Data use and sources

This Actor uses only official SAM.gov sources: the public Data Services daily extract and the Get Opportunities Public API. It does not scrape SAM.gov pages or use the website's internal search endpoints. Public contract opportunity records may be copied and distributed. Most SAM.gov material is not copyrighted, and US government works are generally public domain in the US; citation is appreciated. Check any third-party material separately. The Terms' D\&B commercial-use restrictions concern D\&B entity data, rather than public contract opportunity records; this Actor does not query entity data. Follow all applicable access and API-key terms.

- [SAM.gov Data Services (public extracts)](https://sam.gov/data-services/Contract%20Opportunities/datagov?privacy=Public)
- [Official API documentation and role-based limits](https://open.gsa.gov/api/get-opportunities-public-api/)
- [SAM.gov Terms of Use, key sharing, and data restrictions](https://sam.gov/about/terms-of-use)
- [SAM.gov Account Details (Public API Key)](https://sam.gov/workspace/profile/account-details)
- [Apify SDK Actor reference](https://docs.apify.com/sdk/js/reference/class/Actor)

### Run locally

Requires Node.js 22+.

```sh
npm install
npm run build
npm test
mkdir -p storage/key_value_stores/default
```

Create `storage/key_value_stores/default/INPUT.json` (an empty `{}` uses the daily extract; for API mode enter your key in a local editor). The entire `storage/` directory is gitignored. Avoid putting your key in shell commands or tracked files. Then run either:

```sh
npm start
## Or, if the Apify CLI is installed:
apify run
```

Dataset output is in local Apify storage. Delete the secret input file after use. Docker uses the Apify Node.js 22 Actor image and installs production dependencies in its final stage. Generate a lockfile with `npm install` when registry access is available, then use `npm ci` for reproducible builds. Tests use mocked fetch, in-memory CSV and fixtures only; they make no SAM.gov calls and require no credentials. The reduced fixture in `test/fixtures/official-example.json` is based on the official API's Example 1 (not a live capture).

# Actor input Schema

## `source` (type: `string`):

Choose the data source using the string auto, bulk or api. Default: auto uses the live API when samApiKey is supplied, otherwise the public daily extract (up to about one day old). Use bulk to force the no-key extract or api to require live data and your own key. Example: "bulk".

## `samApiKey` (type: `string`):

Your own SAM.gov public API key as a string; omitted by default, so no key is needed for the daily extract. Use for live API data; source api requires it, while auto selects the API when it is present. Generate it in SAM.gov Account Details (https://sam.gov/workspace/profile/account-details). It is secret input, never logged or stored in dataset items; no shared key is provided. Example placeholder: "YOUR_OWN_PUBLIC_API_KEY".

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

Filter opportunity titles with a space-separated string, not full notice text. Default: omitted, with no title filter. Use to find a topic; API mode sends the official title parameter, while the daily extract requires every word in the title, case-insensitively. Example: "renovation".

## `naics` (type: `string`):

Filter by a NAICS code supplied as a string of 2–6 digits. Default: omitted, with no NAICS filter. Use to target an industry; the API receives ncode, and the daily extract uses a prefix match (236 matches 236220). Example: "236220".

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

Filter by an official typeOfSetAside code string from the enum. Default: omitted, including all set-asides. Use to target a procurement eligibility category, such as total small business set-asides. Example: "SBA".

## `noticeType` (type: `string`):

Filter by an official ptype code string from the enum: u, p, a, r, s, o, g, k or i. Default: omitted, including all notice types. Use to target a procurement stage, such as solicitations (o) or sources sought (r). Example: "o".

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

Filter place of performance by a two-letter US state code string; lowercase is normalized to uppercase. Default: omitted, with no state filter. Use for work in a particular state; API mode sends state and the daily extract matches PopState. Example: "VA".

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

Filter active status with a boolean. Default: true. The daily extract keeps only rows marked Yes; API mode excludes records whose active value is the string No (case-insensitive), but retains false or missing values. Use false to disable this filter; the daily extract still covers active notices only. Example: false.

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

Inclusive start of the posting-date range as a string in MM/dd/yyyy or YYYY-MM-DD format. Default: 29 days before postedTo, or 29 days before today UTC when both dates are omitted. Use to bound the search start; the ordered range must be at most one calendar year. Example: "10/16/2026".

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

Inclusive end of the posting-date range as a string in MM/dd/yyyy or YYYY-MM-DD format. Default: today UTC, including when only postedFrom is supplied. Use to bound the search end; the ordered range must be at most one calendar year. Example: "11/14/2026".

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

Limit the opportunities stored and charged for in this run with an integer from 1 to 10000. Default: 10. Use a small value for previews or raise it for larger exports; the run stops and aborts an open extract download when the limit is reached. Example: 25.

## `pageSize` (type: `integer`):

Set the official API request page size with an integer from 1 to 1000. Default: 100. Use to tune API pagination when using your own key; ignored in daily-extract mode. This does not replace the maxItems output limit. Example: 100.

## Actor input object example

```json
{
  "source": "auto",
  "samApiKey": "YOUR_OWN_PUBLIC_API_KEY",
  "keywords": "renovation",
  "naics": "236220",
  "setAside": "SBA",
  "noticeType": "o",
  "state": "VA",
  "activeOnly": true,
  "postedFrom": "10/16/2026",
  "postedTo": "11/14/2026",
  "maxItems": 10,
  "pageSize": 100
}
```

# Actor output Schema

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

Dataset items URL: each item is one normalized opportunity. Read noticeId, title, responseDeadline and uiLink for triage, and dataSource/extractDate for freshness. description is notice text in daily-extract mode or a URL in API mode; unavailable values are null.

# 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 = {
    "source": "auto",
    "maxItems": 10
};

// Run the Actor and wait for it to finish
const run = await client.actor("samgov-tools/samgov-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 = {
    "source": "auto",
    "maxItems": 10,
}

# Run the Actor and wait for it to finish
run = client.actor("samgov-tools/samgov-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 '{
  "source": "auto",
  "maxItems": 10
}' |
apify call samgov-tools/samgov-opportunities --silent --output-dataset

```

## MCP server setup

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