# SAM.gov Contract Opportunities, No API Key Needed (`datawharf-tools/sam-gov-opportunities`) Actor

Search US federal contract opportunities by keyword, NAICS, set-aside, notice type, state, agency and date. Works with no key (daily public SAM.gov file) or add your own free SAM.gov API key for the live API. Flat rows with deadlines, contacts, links. Not affiliated with GSA or SAM.gov.

- **URL**: https://apify.com/datawharf-tools/sam-gov-opportunities.md
- **Developed by:** [Datawharf Tools](https://apify.com/datawharf-tools) (community)
- **Categories:** Business, Developer tools, Automation
- **Security:** Limited permissions
- **Stats:** 1 total users, 0 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.40 / 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 Contract Opportunities: Search by Keyword, NAICS, Set-Aside, State

Search **US federal contract opportunities** from SAM.gov by keyword, NAICS, set-aside, notice type, state, agency and date, and get flat rows you can export to JSON, CSV or Excel. **No API key is needed to start.** You pay Apify for the results returned.

### Two data sources

| | Keyless (default with no key) | Live API (you enter your own key) |
|---|---|---|
| Source | SAM.gov's public Contract Opportunities data extract (a CSV that SAM.gov rebuilds about once a day) | Official Get Opportunities Public API |
| Freshness | Up to about a day old. The run report shows the file's last-modified time. | Live |
| Notices | Active notices only (about 70,000 at the time of writing) | Latest active version of each notice |
| Key / quota | None | Your own free key; requests count against your own daily quota |
| Extra | Full notice text with `includeDescription` at no extra cost | `awardeeUei`, `resourceLinks`, full agency code path |

Choose with `source`: `auto` (default) uses the live API when you enter a key and the daily file otherwise; `api` and `bulk` force one. If the daily file cannot be downloaded and you gave no key, the run returns one `setup-required` row and charges no results.

**Field differences in keyless mode:** same field names as the live API. `resourceLinks` is empty and `awardeeUei` is null (not in the file). `agency` is the department, sub-tier and office names joined by dots; `agencyCode` holds the CGAC code only. `postedDate` is the date part. Contact details are the primary and secondary contacts the file lists.

### Who is this for?

- **Small contractors and bid teams** who want a daily table of new solicitations matching their NAICS codes.
- **Set-aside hunters** (small business, 8(a), HUBZone, SDVOSB, WOSB) who filter by set-aside type and state.
- **Capture managers and analysts** tracking an agency, sources-sought notices, presolicitations or awards.
- **Developers** who want the SAM.gov Opportunities API behind a simple form, schedule and dataset instead of writing a client that handles paging, the 1-year date limit and retries.

### What you get

One row per notice: `noticeId`, `title`, `solicitationNumber`, `agency` (full path), `postedDate`, `responseDeadline`, `type`, `baseType`, `setAside`, `setAsideCode`, `naicsCode`, `classificationCode`, `placeOfPerformance` (plus city, state, country, zip), `pointOfContactName`, `pointOfContactEmail`, `pointOfContactPhone` (and a `pointOfContacts` list), `uiLink`, `samUrl`, `resourceLinks`, award fields when present (`awardNumber`, `awardAmount`, `awardDate`, `awardeeName`, `awardeeUei`), optional `description` text, and `fetchedAt`.

The contact names and emails are exactly what the government published on the notice for bidders to use. `samUrl` is built from the notice ID; `uiLink` is the link the API returns (SAM.gov may ask you to sign in to open it).

Every run also saves a `RUN_REPORT` (key-value store) with the API calls used, the date windows, retries, and whether the run stopped at your call limit.

### Optional: live API key (free)

Only needed for the live API source.

1. Create or sign in to your account at SAM.gov (sign-in is through Login.gov).
2. Open **Account Details**.
3. Request a **Public API Key**, then enter your account password to view it. The key is shown only while you stay on that page, so copy it right away.
4. Paste it into the `apiKey` field (stored as a secret). SAM.gov asks you to renew keys about every 90 days.

### Your quota

SAM.gov sets a daily request limit per key by account type, and publishes it on open.gsa.gov. Accounts without a SAM.gov role get a small limit (documented as 10 requests per day at the time of writing); accounts with a role get far more. One request returns up to 1,000 notices, so small runs are cheap. The Actor has a **Max API calls** setting (default 10) so it never spends more of your quota than you intend, and it ends cleanly when it reaches it. If SAM.gov reports your quota is used up, the run stops with a clear message and keeps what it already fetched.

### Quick test

```json
{ "apiKey": "YOUR_KEY", "keywords": ["cybersecurity"], "maxResults": 10 }
```

That uses one API request.

### How to use it

1. Paste your SAM.gov API key, set keywords, NAICS codes or dates, and click **Start**.
2. Open the **Overview** table or download the dataset.

With no key, the run streams the daily file, applies your filters and stops reading as soon as `maxResults` is reached. Apify's start event is charged by the platform; result events are charged only for rows returned.

### Example inputs

Note on filters: SAM.gov's own `state` filter is loose, so this Actor also checks each notice and keeps only those whose place-of-performance state equals your `state` (notices with a different or missing state are dropped and not charged; the count is in RUN_REPORT as `filteredOutByState`). In keyless mode `agency` is a case-insensitive substring match on the department, sub-tier and office names (for example `ARMY` finds DEPT OF THE ARMY; covered by tests). In API mode it is passed to SAM.gov's `organizationName` search, whose matching rules are not documented in detail (an earlier live check of `state` plus `agency=ARMY` returned nothing), so try a small run or use the keyless source. NAICS code and set-aside filters were confirmed to match exactly.

Small-business IT solicitations in Virginia, last 30 days (default window):

```json
{ "apiKey": "YOUR_KEY", "naicsCodes": ["541512", "541519"], "setAside": "SBA", "noticeTypes": ["o", "k"], "state": "VA", "maxResults": 200 }
```

Sources-sought notices from the Army, one quarter:

```json
{ "apiKey": "YOUR_KEY", "agency": "ARMY", "noticeTypes": ["r"], "postedFrom": "2026-07-01", "postedTo": "2026-09-30", "maxResults": 500 }
```

Contract awards for a keyword over two years (split automatically into windows of at most one year):

```json
{ "apiKey": "YOUR_KEY", "keywords": ["janitorial"], "noticeTypes": ["a"], "postedFrom": "2024-11-01", "postedTo": "2026-10-31", "maxResults": 2000, "maxApiCalls": 50 }
```

### Use it from code (JavaScript and Python)

JavaScript (`npm install apify-client`):

```js
import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('datawharf-tools/sam-gov-opportunities').call({ naicsCodes: ["541512"], setAside: "SBA", noticeTypes: ["o", "k"], maxResults: 50 });
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items.length, 'rows');
```

Python (`pip install "apify-client>=3"`):

```python
import os
from apify_client import ApifyClient
client = ApifyClient(os.environ["APIFY_TOKEN"])
run = client.actor("datawharf-tools/sam-gov-opportunities").call(run_input={"naicsCodes": ["541512"], "setAside": "SBA", "noticeTypes": ["o", "k"], "maxResults": 50})
items = list(client.dataset(run.default_dataset_id).iterate_items())
print(len(items), "rows")
```

More examples: https://github.com/datawharf-tools/examples

### Pricing

Pay per result: **$0.0004 per notice, about $0.40 per 1,000 notices**, plus a tiny per-run start fee. A keyless run downloads about 220 MB and takes a few seconds (stops sooner when `maxResults` is reached). Set the **maximum charge per run** to cap spend. An invalid key or invalid input stops the run before any item is returned or charged.

### Limits and behaviour

- The API only accepts a posted-date range of up to one year per request. Longer ranges are split into windows automatically (newest first); each window costs at least one request.
- The API searches **titles** for keywords (not the full text). Each keyword, NAICS code and notice type you list is a separate request; duplicates are removed by notice ID.
- Set-aside accepts one value per run.
- Keyless mode matches keywords as case-insensitive text inside the title (any keyword), NAICS by prefix, and only sees active notices; use the API for archived or older notices.
- The API returns only the latest active version of a notice. For full version history use SAM.gov Data Services.
- `includeDescription` costs one extra request per notice against your quota, so it is off by default.
- Failed requests are retried with backoff (HTTP 429 without a quota message and 5xx). Quota-used-up and invalid-key errors stop the run immediately.
- If a run stops part-way, the status message starts with "PARTIAL RESULT" and the rows already saved (and charged) are kept.

### FAQ

**How do I find small-business set-aside contracts by NAICS code?** Set `naicsCodes` (for example `541512`), `setAside` (for example `SBA`) and `noticeTypes` (`o` solicitations, `k` combined synopsis/solicitation, `r` sources sought). No key is needed in keyless mode.

**Can I get new SAM.gov opportunities by email or in a spreadsheet every day?** Yes. Save the input as a Task, add a daily Schedule, and use the email, Slack or Google Sheets integration.

**Do I need a key?** No. Without one the Actor uses SAM.gov's public daily file. A key is optional and gives live data. Quota and terms of a key belong to its owner, so the Actor never provides, shares or pools keys.

**Is this scraping?** No web pages are scraped. The Actor reads SAM.gov's published public data extract (listed on data.gov as public) or the documented Get Opportunities Public API.

**Does it use the restricted or sensitive versions of the API?** No. It uses the public API only.

**Can I schedule it?** Yes. Use Apify schedules, the Apify API and webhooks; the key is a secret input.

### Your key, your terms, your data

- **Your own key only.** Each user must use their OWN SAM.gov API key. We never provide or share keys, and you must not share yours with anyone outside your organization, as the SAM.gov Terms of Use require.
- **You are responsible for following the [SAM.gov Terms of Use](https://sam.gov/about/terms-of-use)** and the API documentation on [open.gsa.gov](https://open.gsa.gov/api/get-opportunities-public-api/).
- **The Actor keeps no data of its own.** Results live in your own run dataset and the `RUN_REPORT` record in your Apify account, under your account's retention settings. The key is a secret input and is never written to the dataset, the key-value store or the logs; error messages have it removed.
- This Actor is not affiliated with, endorsed by, or sponsored by the U.S. General Services Administration (GSA) or SAM.gov. "SAM.gov" is a service of the U.S. government and is named here only to describe the data source.

**Sources: SAM.gov Data Services public Contract Opportunities extract** (https://sam.gov/data-services/Contract%20Opportunities?privacy=Public, listed at https://catalog.data.gov/dataset/contract-opportunities-from-sam-gov) and **SAM.gov Get Opportunities Public API** (https://open.gsa.gov/api/get-opportunities-public-api/). Data is published by the US government; no license is stated on the extract, so check the SAM.gov terms for your use.

### Related Actors

- [EU TED Tenders](https://apify.com/datawharf-tools/eu-ted-tenders) for the same kind of table on the EU side.
- [SEC EDGAR Filings & XBRL Data](https://apify.com/datawharf-tools/sec-edgar-api) for company filings and financials.

### Privacy policy

*Publisher: datawharf-tools. Contact: datawharf@gmail.com. Last updated 2026-11-07.*

- **What we process.** (1) If you use the API source, your own SAM.gov API key: Apify stores it encrypted as a secret input; the Actor uses it in memory for that run only, sends it only to api.sam.gov, and never logs, stores or copies it anywhere else. (2) Public opportunity notices that the API returns, including the contact names, emails and phone numbers the government publishes on them. They go straight into your own run's dataset.
- **What we keep.** Nothing. We keep no copy of your key, your inputs or the data returned, and the Actor has no analytics, tracking or third-party sharing.
- **Where your data lives.** Your run datasets, key-value records and logs live in your own Apify account under Apify's retention rules and [Apify's privacy policy](https://apify.com/privacy-policy). You control and can delete them at any time. If you use published contact details for outreach, you are responsible for complying with the laws that apply to you (for example CAN-SPAM).
- **Questions or deletion requests.** Write to datawharf@gmail.com.

# Actor input Schema

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

auto (default): live API if you enter an API key, otherwise the keyless daily file. api: always the live API (key required, freshest, includes UEI and resource links). bulk: always the keyless daily file, even if a key is entered.

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

Optional. Leave empty to use the keyless daily file. For the live API: sign in at SAM.gov, open Account Details, request a Public API Key (shown only while you stay on that page). Your OWN key only; do not share it. Stored as a secret and never written to the dataset or logs.

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

Words searched in the notice title. Several keywords match any of them. In API mode each keyword is a separate request. Leave empty for all notices.

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

Start of the posted-date range, YYYY-MM-DD. Default: 30 days before 'Posted to'. API mode splits ranges over 1 year automatically; the daily file holds active notices only, so old posted dates may return nothing.

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

End of the posted-date range, YYYY-MM-DD. Default: today (UTC).

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

2 to 6 digit NAICS codes (a shorter code matches all codes starting with it). Several codes match any of them.

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

Only one value per run. SBA = Total Small Business, SBP = Partial Small Business, 8A / 8AN = 8(a), HZC / HZS = HUBZone, SDVOSBC / SDVOSBS = Service-Disabled Veteran-Owned, WOSB / WOSBSS = Women-Owned, EDWOSB / EDWOSBSS = Economically Disadvantaged WOSB, VSA / VSS = Veteran-Owned.

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

Notice type codes: o = Solicitation, k = Combined Synopsis/Solicitation, p = Presolicitation, r = Sources Sought, s = Special Notice, a = Award Notice, u = Justification, g = Sale of Surplus Property, i = Intent to Bundle. Empty = all types.

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

2-letter US state code, e.g. VA. Only notices whose place-of-performance state equals it are returned (checked by this Actor in both sources).

## `agency` (type: `string`):

Text to find in the agency, sub-agency or office name, e.g. ARMY, NAVY, VETERANS. Daily-file mode: case-insensitive substring match (tested). API mode: passed to SAM.gov's organizationName search, whose exact matching is not documented, so a short word may return nothing; if so use the keyless daily file.

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

Daily-file mode: adds the notice text at no extra cost. API mode: costs one extra API request per notice against your SAM.gov daily quota, so raise 'Max API calls' first.

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

Maximum number of notices to return. You are charged per item returned.

## `maxApiCalls` (type: `integer`):

API mode only. Safety limit on requests to SAM.gov in this run (each returns up to 1000 notices). Default 10 matches the small daily quota SAM.gov documents for keys without a role.

## Actor input object example

```json
{
  "source": "auto",
  "keywords": [
    "cybersecurity"
  ],
  "includeDescription": false,
  "maxResults": 20,
  "maxApiCalls": 10
}
```

# Actor output Schema

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

All notices in the default dataset (table, CSV, JSON, Excel).

## `report` (type: `string`):

Summary of the run written to the default key-value store.

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

// Run the Actor and wait for it to finish
const run = await client.actor("datawharf-tools/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": ["cybersecurity"],
    "maxResults": 20,
}

# Run the Actor and wait for it to finish
run = client.actor("datawharf-tools/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": [
    "cybersecurity"
  ],
  "maxResults": 20
}' |
apify call datawharf-tools/sam-gov-opportunities --silent --output-dataset

```

## MCP server setup

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