# SAM.gov Scraper – New Contract Opportunity Alerts (`cce_partners/samgov-opportunity-radar`) Actor

SAM.gov scraper for federal contract opportunities by NAICS, set-aside, agency and state. Newest 25 per run, or only new/amended with a saved monitor name.

- **URL**: https://apify.com/cce_partners/samgov-opportunity-radar.md
- **Developed by:** [Chris Grant](https://apify.com/cce_partners) (community)
- **Categories:** Lead generation, Automation
- **Stats:** 1 total users, 0 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $4.00 / 1,000 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

## SAM.gov Scraper & Federal Contract Alerts (Opportunity Radar)

**SAM.gov scraper and RFP alert tool** for **federal contract opportunities** (solicitations, presolicitations, sources sought, combined synopses and awards) that match your NAICS codes, set-asides, agency and state. **Set a monitor name and it returns only new or amended notices**; without one, each run returns the newest notices in your lookback window (25 by default), with no change tracking. **No API key needed**: it reads SAM.gov's official public data. Add your free SAM.gov key for real-time results. Each run gives you clean, flat rows for Google Sheets, Slack, Zapier, Make or your CRM, and with a monitor name you only pay for what's new.

### GovCon suite by CCE Partners

- **SAM.gov Scraper – New Contract Opportunity Alerts** (this Actor): new and amended SAM.gov opportunities by NAICS, set-aside, agency and state.
- **[Expiring Federal Contracts – Recompete Radar](https://apify.com/cce_partners/govcon-recompete-radar)**: federal contracts ending in 3–18 months (a recompete pipeline from USAspending.gov); only some are matched to an open SAM.gov notice.
- **[SEC Form D Scraper – New Startup Funding Raises](https://apify.com/cce_partners/sec-form-d-new-raises-feed)**: newly funded private companies (prospective teaming partners and commercial buyers).
- **[Federal Contract Awards – New Winners Feed](https://apify.com/cce_partners/federal-contract-award-winners-feed)**: who won the contract, from USAspending.gov: winner, current parent company, amount, set-aside and competition details; only new awards with a monitor name.

### SAM.gov saved search alerts vs this Actor

SAM.gov saved searches email you links, and you still have to log in and click through to see each notice. This Actor returns the notice itself as a row (title, notice type, deadline, agency, NAICS, set-aside, place of performance, description and `samUrl`) that you can filter, sort and send anywhere.

- **Only new or changed.** Set a monitor name and it remembers every notice it has seen, returning a notice again only when it's amended (title, response deadline, set-aside, NAICS/PSC, award).
- **No key required.** Keyless mode uses SAM.gov's official public nightly extract (up to 24h old). Add a key for real-time results.
- **No personal contact data.** No names, emails or phone numbers, so the output is safe to share and store.
- **Pay per result, no platform usage fees.** With a monitor name you pay only for new or changed notices; unchanged ones are free, even when you output them with `emitUnchanged`. Without a monitor name every notice a run returns is charged (up to 25 by default), including notices an earlier run already returned (unless you have saved state from before 0.2.8; see "Monitor names").

### Use cases

#### 1. 8(a) IT services firm: daily new solicitations

```json
{
  "naicsCodes": ["541511", "541512", "541519"],
  "setAside": "8A",
  "noticeTypes": ["o", "k"],
  "lookbackDays": 7,
  "monitorName": "8a-it-daily"
}
```

Schedule it daily and connect Google Sheets. Each morning you get only the new or amended IT solicitations.

#### 2. SDVOSB contractor: Texas construction bids and sources sought

```json
{
  "naicsCodes": ["236220", "238210"],
  "setAside": "SDVOSBC",
  "state": "TX",
  "noticeTypes": ["o", "p", "r"],
  "lookbackDays": 14,
  "monitorName": "sdvosb-tx-construction",
  "aiTagging": true
}
```

Sources sought (`r`) and presolicitations (`p`) give you early warning before the RFP drops. Tags add the sector, set-aside category and days to respond.

#### 3. Capture team: cybersecurity across all agencies

```json
{
  "keywords": "cybersecurity",
  "noticeTypes": ["o", "k", "r"],
  "lookbackDays": 30,
  "maxItems": 2000,
  "monitorName": "cyber-capture"
}
```

### Monitor names: get only new results

- **With a monitor name** (`monitorName`, e.g. `"8a-it-daily"`): the Actor saves a fingerprint of every notice this monitor returned, in a key-value store in your own Apify account. Later runs with the same name return only new or amended notices (`changeType` new or changed). Every matching notice in the window is compared (keyless: up to 10,000 newest; API key: the first 1,000, or `maxItems` if larger); `maxItems` (default 1,000) caps how many new or changed notices one run returns, and the rest come on the next run. Use one name per filter set; a new name starts fresh. Use this for schedules, n8n, Make, Zapier and Sheets.
- **Without a monitor name** (the default): no records are written (one exception for older users, below). Each run returns the newest notices posted in the last `lookbackDays` days, up to 25 (set `maxItems` to change it), with `changeType` empty. Good for a first look or a one-off search. The run's status message reminds you: "Set a monitor name to get only new results."
- **Existing users without a monitor name keep their tracking.** If you ran this Actor without a monitor name before stateless became the default (version 0.2.7 or earlier), your saved state for that filter set is still used: those runs keep returning, and charging, only new or changed notices (default `maxItems` 1,000, as before). The log names the monitor name that already holds that state, for example: "Stateless (latest rows) is now the default for runs without a monitor name; your existing saved state is kept. Set monitorName "07f8910e3b79" to keep it explicitly." (the status message has a short form). Set exactly that `monitorName` (case-sensitive) and the run uses the same saved state: nothing is re-sent or re-charged. Any other new name starts fresh (its first run returns and charges all matching notices once). Only runs with no saved state for their filters are stateless; they write no records (on a brand-new account an empty storage may still be created).
- **Exception: runs with no filters at all, and the Store's example input** (an empty input, an empty `monitorName` with no filters, or the example input NAICS 236220) are always stateless, even if you have saved state from 0.2.7 or earlier for them. They return the newest notices as above and never open, read or write the saved-state store at all (since 0.2.19 they don't even check whether such state exists, so the log no longer names it). If you used this Actor that way in 0.2.7 or earlier, your saved state is kept under a fixed monitor name: `"07f8910e3b79"` for runs with no filters, `"c2a2d3548358"` for NAICS 236220 only. Set that `monitorName` to pick it up with nothing re-sent or re-charged.
- You're charged per new or changed notice returned (and per notice in stateless runs), so a scheduled run without a monitor name pays again for notices it already returned. With a monitor name and `emitUnchanged` on, notices returned only because they're unchanged (`changeType: "unchanged"`) are free; the status message shows how many.

### What is saved (state keys)

Only runs with a monitor name (or an older no-name run that still uses its saved state, see above) write anything. They use one key-value store in your own account, `samgov-radar-state`, with these records per monitor (`<name>` = the monitor name; for older no-name state, a 12-character code such as `07f8910e3b79`):

- `FP2-STATE_<name>`: the monitor's saved state (fingerprints of the notices it has seen).
- `FP2B-STATE_<name>` (since 0.2.19): a second copy of the same record, written right after it, so a late write from a run that lost its monitor can't undo newer progress. Same size as `FP2-STATE_<name>`. Older versions ignore it.
- `STATE_<name>`: the same state in the format of versions 0.2.7 and older (kept so you can switch back).
- `PENDING-STATE_<name>!<run ID>` (since 0.2.21; `PENDING-STATE_<name>` in older versions): each run's copy of the batch it is delivering, cleared once it's pushed (left behind only by an interrupted run; the next run re-sends it free of charge and deletes it). A monitor can have several of these after interrupted runs.
- `LOCK-STATE_<name>`: the monitor's lock while a run works (removed when the run ends; expires after 10 minutes without a heartbeat).
  Monitor names with leading or trailing spaces also keep their pre-0.2.8 `STATE_` record. Deleting a monitor's records makes its next run start fresh (all matching notices are returned and charged once); delete all of them together, and never while a run on that monitor is active.

### How change detection works (and its limits)

With a monitor name, each notice gets a fingerprint of these fields: notice ID, title, solicitation number, notice type, posted date, response deadline date, set-aside code, primary NAICS code, PSC code, and award number, date and amount. Values are normalized so keyless mode and API-key mode give the same fingerprint for the same notice (checked on 999 live notices: 999 identical). A notice comes back as `changed` (and is charged) only when one of these fields changes. Archiving, archive-date edits, agency or office renames, link formats, descriptions and attachments are not changes.

Known limits:

- **Only date-level deadline changes and wording changes count.** A response deadline moved to another time on the same date, or a title edit that only changes capitalization, punctuation or accented/special characters, is not reported as changed.
- **Upgrade or downgrade gap.** When a monitor first runs on a new fingerprint version (or after going back to an older build and returning), notices it already knew are re-baselined silently: not returned, not charged. A real amendment published exactly in that gap is absorbed once and not reported.
- **Interrupted runs.** Results are saved, pushed and charged in batches, and the monitor's memory is saved before each batch is pushed and charged, so no notice is ever charged twice. Before each batch is saved, the Actor also keeps a copy of it as "pending" and clears it once the batch is pushed. A graceful stop lets the batch in progress finish its push and charge before the run ends: a graceful abort (the Abort run API with `gracefully=true`; Apify gives the run 30 seconds), a stop signal or the run-timeout guard (below). The run waits for that batch until just before its run timeout. The **default Abort button in Apify Console is a forced, immediate abort**, just like an out-of-memory crash or a run timeout. Such a stop gives the run no time to finish anything. If it lands between a batch's save and its push, the next run on that monitor re-sends that batch **free of charge**, and so does any batch a graceful abort cuts off after its 30 seconds. A forced stop right after the push can make that free re-send a duplicate of rows you already have, and a stop before the charge leaves the batch uncharged. Either way, no notice is ever charged twice.
  What to expect from re-sent rows:
  - **They can be shortened.** A re-sent row is the pending copy, and that copy has to fit in one 8 MB record. For a very large batch, long descriptions in the copy are cut short (they end in "…"). In extreme cases other long fields are cut too, or the row keeps only its key fields (notice ID, title, dates, agency, NAICS, set-aside, link) and gets `pendingShortened: true`. The rows delivered by the original run, when it got that far, are always complete.
  - **They can come late.** The pending copy doesn't expire. It's re-sent by the next run on that monitor, whenever that is, for example days later, or after you switch to an older version and back.
  - **They can fall outside your current filters.** They were picked with the filters of the interrupted run. If you've changed the monitor's filters since, the re-sent rows are still delivered once, because they were already recorded as delivered.
- **One run per monitor at a time.** A run with a monitor name locks that monitor while it works. So does a run without a monitor name that still uses saved state from 0.2.7 or earlier for its filters (see "Monitor names"); all other runs without a monitor name never lock anything. If you start a second run on the same monitor (for example a manual run during a scheduled one), the second run stops right away with a "Locked" status message and returns and charges no notices, so nothing is delivered twice. It still pays the Actor start charge ($0.01 per GB of run memory, at least $0.01), like every run. A monitor run also stops itself 15 seconds before its run timeout (a third of the time left for very short timeouts), releases the lock and ends as failed with a clear message; notices already returned are saved and the next run continues. A graceful abort (the Abort run API with `gracefully=true`) or a stop signal releases the lock before the run ends. The default **forced** abort, an out-of-memory crash or a container kill gives the run no time to release it, so it can block the monitor for up to 10 minutes; runs started in that time stop as "Locked" and charge no notices.
- **Switching between keyless and API-key mode.** The keyless nightly extract can be up to ~24 h behind the API. An amendment API-key mode already returned is not returned or charged again when a keyless run still sees the older version, or when the extract catches up (for 7 days after API-key mode returned it). One exception: a notice whose first-ever appearance was in API-key mode, already amended, can come back once as `changed` (showing the older text) if you switch to keyless before the extract has the amendment. The reverse also applies: if a notice is reverted, within those 7 days, to the content it had before the amendment API-key mode returned, and only keyless runs see the revert, the revert is not reported.
- **Memory cleanup.** To keep saved state small, a monitor forgets notices posted more than a year ago, and notices whose response deadline passed more than 90 days ago that it hasn't seen for 90 days. Above a hard cap of 50,000 notices it forgets closed notices (deadline passed) first, then notices without a deadline, and open notices only as a last resort (the run log warns when that happens). If a forgotten notice shows up again later (for example after you widen your filters), it is returned and charged once as new.

### SAM.gov API rate limits: how this avoids them

- **Keyless mode (default) makes no SAM.gov API calls.** It downloads SAM.gov's public nightly extract once per run, so there is no daily quota to hit.
- **With your own key**, search results come 1,000 per page, so a typical daily alert uses only a few requests. Description lookups are off by default (`maxDescriptionFetches` = 0) because SAM.gov gives non-federal keys without a role only 10 requests/day (1,000/day with a role).
- Failed requests are retried with exponential backoff instead of hammering the API.

### Export SAM.gov opportunities to Google Sheets, Slack or n8n

Schedule the Actor (for example daily) in Apify Console with a `monitorName`, then add an integration: Apify's built-in Google Sheets, Slack, Zapier and Make integrations, or the Apify node in n8n. With a monitor name, each run sends only new or amended notices, so the sheet or channel gets no duplicates.

### Notice type codes

o = Solicitation, p = Presolicitation, k = Combined Synopsis/Solicitation, r = Sources Sought, s = Special Notice, a = Award, u = J\&A, g = Sale of Surplus, i = Intent to Bundle.

### Set-aside codes

SBA (Total Small Business), SBP (Partial SB), 8A / 8AN (8(a)), HZC / HZS (HUBZone), SDVOSBC / SDVOSBS (Service-Disabled Veteran-Owned), WOSB / WOSBSS / EDWOSB / EDWOSBSS (Women-Owned), VSA / VSS (Veteran).

### FAQ

**Do I need a SAM.gov API key?** No. Without a key the Actor uses SAM.gov's official public nightly extract, which can be up to 24 hours old. A free key (sam.gov → Account Details → Public API Key) gives you real-time results.

**How does "only new or changed" work?** Each `monitorName` keeps a fingerprint of every notice it has seen. A notice is returned again only if it's amended: its title, solicitation number, notice type, posted date, response deadline date, set-aside, NAICS, PSC or award number/date/amount changed. Archiving, agency or office renames, links, and switching between keyless and API-key mode don't count as changes and aren't charged (an amendment is charged once, even though the keyless extract shows it up to ~24 h after the API; see the one rare exception under "Known limits"). Turn on `resetState` to start fresh. Without a monitor name no records are written and there is no change tracking (except for filter sets that already had saved state from version 0.2.7 or earlier; see "Monitor names"): each run returns the newest notices in the lookback window (25 by default), and every returned notice is charged; see "Monitor names" above.

**Can I run several searches?** Yes. Give each filter set its own `monitorName`.

**Do I have to log in to SAM.gov to see the details?** No. Each row already has the title, type, agency, deadlines, NAICS, set-aside, place of performance and the description (in keyless mode; with an API key, opt in with `maxDescriptionFetches`). `samUrl` links to the notice if you want the attachments.

**Does it include contracting officer emails or phone numbers?** No, by design. Use the `samUrl` link to see contacts on SAM.gov.

**How much does it cost?** $0.01 per run plus $4 per 1,000 opportunities returned, and $2 per 1,000 AI tags if you turn them on. With a monitor name only new or changed notices are returned and charged, so a daily alert with 20 new notices costs about $0.09. Without a monitor name every returned notice is charged: a default run (25 notices) costs about $0.11, also when it repeats notices from earlier runs.

**Can I use it from an AI agent?** Yes. Call it through the Apify MCP server or API. The output is flat JSON with stable fields such as `noticeId`, `title`, `responseDeadline` and `samUrl`.

**Is this affiliated with SAM.gov or GSA?** No. It's an independent tool that uses SAM.gov's official public data.

### Two data modes

| | **Keyless (default when no key)** | **API key (recommended for real-time)** |
|---|---|---|
| Source | Official SAM.gov public nightly extract ([Data Services](https://sam.gov/data-services/Contract%20Opportunities/datagov?privacy=Public)) | SAM.gov Get Opportunities Public API |
| Freshness | **Up to 24h old** (file regenerated nightly) | Real-time |
| Setup | None | Free key: sam.gov > Account Details > Public API Key |
| Daily quota | None from SAM.gov | Your key's daily limit |

If you supply `samApiKey`, the Actor always uses the API. **Official public nightly extract, up to 24h old; add a free SAM.gov API key for real-time results.**

We never scrape SAM.gov web pages. Keyless mode uses only the public bulk extract that SAM.gov publishes for download.

#### Field differences in keyless mode

- `awardeeUei` is always empty (not in the extract).
- `naicsCodes` (secondary NAICS list) is empty; `naicsCode` (primary) is present.
- `popCity` has the name only, no city code.
- `agencyPathCode` is built from CGAC / FPDS / AAC codes and may differ from the API's code path; `organizationCode` filtering is approximate.
- `keywords` is a local case-insensitive title match (the API runs its own title search), so counts can differ slightly.
- Covers notices currently in the extract (active notices, roughly the last year), latest version only.

### Output

Same columns in both modes: noticeId, title, solicitationNumber, noticeType, baseType, active, postedDate, responseDeadline, archiveDate, department, subTier, office, agencyPathCode, naicsCode, pscCode, setAsideCode, setAside, office/place-of-performance location, award fields, samUrl, additionalInfoUrl, changeType, optional tag\_\* columns, plus **`description`**: the full notice text, so you don't need to log in to read it. No contact names, emails or phones, in either mode (emails, phone numbers and names after labels like "Point of Contact:" are removed from the description).

### Full details and attachment keywords

- `includeDetails` (default on). Keyless: the description comes from the nightly extract at no extra cost. With an API key: one SAM.gov request per returned notice, only if you opt in with `maxDescriptionFetches` (default 0, so no descriptions in key mode unless you set it). Non-federal keys without a SAM.gov role get only 10 requests/day; with a role, 1,000/day.
- `attachmentKeywords` (optional, API key required). Downloads each returned notice's public attachments through the official API `resourceLinks` with your key, searches PDF and text files, and returns `attachmentKeywordHits` (which keywords matched), `attachmentsScanned` and `attachmentsSkipped`. Attachment text is never returned. Capped by `maxAttachmentDownloads` (default 10); each download counts toward your daily SAM.gov limit. Word/Excel files are skipped. Not available keyless: the public extract has no attachments, and we don't scrape SAM.gov pages.

### Cost

A keyless test run with 512 MB memory and maxItems 10 took about 6 s and cost about $0.0003 in platform usage.

# Changelog

This Actor's version history is a separate document: https://apify.com/cce_partners/samgov-opportunity-radar/changelog.md

# Actor input Schema

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

Optional. Free SAM.gov public API key (sam.gov > Account Details) for real-time results. Leave empty for keyless mode: official SAM.gov public nightly extract, up to 24h old.

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

Search within opportunity titles, e.g. 'cybersecurity'.

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

e.g. 541512, 238210

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

Optional SAM.gov set-aside filter.

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

SAM.gov ptype codes: o=Solicitation, p=Presolicitation, k=Combined Synopsis, r=Sources Sought, s=Special Notice, a=Award, u=J\&A, g=Sale of Surplus, i=Intent to Bundle.

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

Two-letter code, e.g. VA.

## `organizationCode` (type: `string`):

Optional SAM.gov organization code.

## `lookbackDays` (type: `integer`):

Only notices posted within this many days (1–364). Without a monitor name, each run returns the newest notices from this window.

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

Maximum number of opportunities returned (and charged) per run, newest first. Leave empty for the default: 25 without a monitor name, 1,000 with one. Without a monitor name there is no change tracking (unless saved state from version 0.2.7 or earlier exists for these filters): each run returns the newest notices in the lookback window, including ones earlier runs returned. With a monitor name, the newest matching notices are compared with the saved state first (up to 10,000 in keyless mode; with an API key the first 1,000, or this value if larger), and new or changed ones beyond this limit come on the next run.

## `monitorName` (type: `string`):

Set a name (e.g. '8a-it-daily') to get only NEW or AMENDED notices: the Actor saves a fingerprint of every notice this monitor returned, and later runs with the same name return only notices that are new or changed. Use one name per filter set. Leave empty to get the newest notices in the lookback window each run (nothing saved, up to 25 by default).

## `emitUnchanged` (type: `boolean`):

Monitor name only: also output notices this monitor already returned with no changes (marked unchanged; not charged).

## `resetState` (type: `boolean`):

Monitor name only: forget this monitor's saved state so all matching notices are treated as new.

## `aiTagging` (type: `boolean`):

Free rule-based tags. Add an AI key below to also get a plain-English summary.

## `aiApiKey` (type: `string`):

OpenAI-compatible key for the one-line 'what they're buying' summary. Only title and codes are sent; never contact data.

## `aiBaseUrl` (type: `string`):

OpenAI-compatible API base URL used for optional AI summaries.

## `aiModel` (type: `string`):

e.g. a small, cheap chat model.

## `includeDetails` (type: `boolean`):

Adds the notice description text to each row (emails, phone numbers and labeled contact names removed). Keyless: from the nightly extract, free. With an API key: only up to maxDescriptionFetches (default 0), since each costs a SAM.gov API request.

## `maxDescriptionChars` (type: `integer`):

Longer descriptions are cut.

## `maxDescriptionFetches` (type: `integer`):

API key mode only. Default 0 = no descriptions in key mode, because each one is a SAM.gov API request against your daily limit (non-federal keys without a SAM.gov role: 10/day; with a role: 1,000/day). Set e.g. 25 to opt in. Keyless mode always includes descriptions for free.

## `attachmentKeywords` (type: `array`):

Optional. Downloads each returned notice's public attachments via the official API resourceLinks (with your key), and reports which of these keywords appear in PDF/text attachments (attachmentKeywordHits). Attachment text itself is not returned. Each download counts toward your SAM.gov daily limit.

## `maxAttachmentDownloads` (type: `integer`):

Hard cap on attachment downloads (API key mode).

## Actor input object example

```json
{
  "lookbackDays": 7,
  "emitUnchanged": false,
  "resetState": false,
  "aiTagging": false,
  "aiBaseUrl": "https://api.openai.com/v1",
  "includeDetails": true,
  "maxDescriptionChars": 10000,
  "maxDescriptionFetches": 0,
  "maxAttachmentDownloads": 10
}
```

# Actor output Schema

## `dataset` (type: `string`):

Dataset with all emitted opportunities

# 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 = {};

// Run the Actor and wait for it to finish
const run = await client.actor("cce_partners/samgov-opportunity-radar").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 = {}

# Run the Actor and wait for it to finish
run = client.actor("cce_partners/samgov-opportunity-radar").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 '{}' |
apify call cce_partners/samgov-opportunity-radar --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,cce_partners/samgov-opportunity-radar"
        }
    }
}
```

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/jHWTaoToruEWHuUJX/builds/dH44qMT15YbsTOY7a/openapi.json
