# Federal Contract Awards – New Winners Feed (`cce_partners/federal-contract-award-winners-feed`) Actor

Who just won a federal contract in your NAICS? Newest USAspending.gov awards with set-aside, competition, parent company. Only new ones with a monitor name.

- **URL**: https://apify.com/cce_partners/federal-contract-award-winners-feed.md
- **Developed by:** [Chris Grant](https://apify.com/cce_partners) (community)
- **Categories:** Lead generation, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $5.00 / 1,000 award rows

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

## Federal Contract Awards – New Winners Feed

Who just won a federal contract in your NAICS? This Actor returns **federal contract awards** from the official USAspending.gov API, newest first; with a monitor name, only awards that are new since the last run.
Each row has the winner, its **current parent company**, obligated and potential value, end dates,
**set-aside, extent competed, number of offers and solicitation procedures**, and a plain **DoD 90-day delay flag**.

> **Data delay, plainly:** civilian awards appear on USAspending within days. **DoD and USACE awards are published about
> 90 days after award** by DoD policy (FPDS/USAspending). We flag them (`dataDelay: "dod_90_day"`) and deliver them on the
> first run after they become public (`changeType: "newly_visible_dod"`). This feed can't show DoD awards earlier than that.
> USAspending doesn't say *when* a DoD award became public, so `newly_visible_dod` is an estimate; see "How new works" below.
> For earlier signals, **[SAM.gov Scraper – New Contract Opportunity Alerts](https://apify.com/cce_partners/samgov-opportunity-radar)** returns SAM.gov award notices (notice type Award Notice), which can appear before
> USAspending has the award (most of all for DoD).

### What you get per row

- Award: `id`, `piid`, `parentIdvPiid`, `awardType`, `baseObligationDate` (award date), `lastModifiedDate`, `usaspendingUrl`.
- Winner: `recipientName`, `recipientUei` (null for individuals, `recipientRedacted: true`), `currentParentName` / `currentParentBasis`
  (`alias-map` = a small cited map of recent acquisitions, `usaspending-parent` = USAspending recipient profile), `usaspendingParentName`.
- Money and time: `awardAmount` (obligated), `potentialValue` (base + all options), `startDate`, `endDate`, `potentialEndDate`, `monthsToEnd`.
- Who and what: `awardingAgency`, `awardingSubAgency`, `fundingAgency`, `naicsCode`, `pscCode`, `popState`, `popCity`, `description`.
- Competition: `setAsideType`, `extentCompeted`, `numberOfOffers`, `solicitationProcedures`, `otherThanFullAndOpen`,
  `fairOpportunityLimited`, `solicitationIdentifier`, `contractPricingType` (codes plus `...Description`).
- Feed: `changeType` (`new` | `newly_visible_dod` | `amended` | `unchanged`), `firstSeenAt`, `dataDelay`, `watchMatch`.

Competition fields, potential value and potential end date come from USAspending's award-detail endpoint, one request per
award, so they are filled for the first `maxDetailLookups` rows (default 60, newest first) that are reached within
`enrichmentDeadlineSecs` (default 90 s; lookups also stop after 3 failures in a row). Other rows have them `null` and
`detailLookedUp: false`. Values are never guessed, and a slow USAspending never holds back the rows themselves.

### Input

At least one of `naicsCodes`, `pscCodes`, `awardingAgencies`, `recipients`. With none (for example an API call with `{}`),
the Actor uses NAICS 541512 and $25,000 minimum and says so in the status message.

- `naicsCodes`: 2, 4 or 6 digits (USAspending rejects other lengths; the run stops before any row is charged).
- `awardingAgencies`: USAspending top-tier names such as `Department of Veterans Affairs` (case, `Dept.` and abbreviations
  like `VA`, `DOD`, `GSA` are accepted). Names are checked against USAspending's agency list at the start of the run: an unknown
  name (e.g. `Dept of Veteran Affairs`, or a sub-agency such as `Department of the Army`) stops the run before any search, with
  suggestions. Only Apify's start event is charged for such a run. If USAspending's agency list can't be read (after 3
  retries), the run also stops before any search and says so, rather than run with names it couldn't check; try again later.
- `setAsideTypes` (e.g. `SBA`, `8A`, `SDVOSBC`, `WOSB`, `HZC`) and `extentCompetedTypes` (e.g. `A`, `C`, `CDO`) are filtered by USAspending itself.
- `recipients`: competitor UEIs (exact) or names (contained). One search per entry (up to 25; more are ignored with a
  warning), each with its own saved position.
- `monitorName`: see below; letters, digits, `-` and `_` only. A name can't contain `-SEEN-` or end in `-PENDING`,
  `-PENDING-<number>` or `-JOURNAL` (those are the names of another monitor's saved records); such a run stops at once. `lookbackDays` (default 14), `includeDodDelayed` (default on),
  `includeAmended` (default off), `resolveParents`, `maxDetailLookups`, `maxParentLookups`, `enrichmentDeadlineSecs`,
  `emitUnchanged`, `resetState`, `maxItems`.
- `searchTimeBudgetSecs` (default 600): overall time for all searches of one run. When it runs out, the search stops
  cleanly, the monitor saves its position, the status message says so, and the next run continues. If USAspending fails on
  one search (timeouts, HTTP 429 or 5xx after retries), the run's remaining searches are skipped instead of each waiting out
  the same outage; they keep their position.

### Monitor or one-off

- **Without `monitorName`** (stateless, the default): the newest awards in the last `lookbackDays`, 25 rows unless you set
  `maxItems`. Nothing is saved, so the same awards can come again on the next run.
- **With `monitorName`**: only awards that are new since the last run of that monitor (500 rows per run unless you set
  `maxItems`). Rows over the cap come on the next run, judged by the same rules as when they were found (including the first
  run's 30-day allowance and the age limits), and that includes amended rows when `includeAmended` is on. Schedule it daily
  or weekly. Reusing a monitor name with different filters keeps the old position and seen awards; use a new name or
  `resetState` for a fresh start.
- **Two runs of one monitor:** a second concurrent run stops with 0 rows (it still pays the Actor start event, see
  Pricing). Apify runs this Actor with limited permissions, so a run can't check whether the run holding the monitor's lock
  is still alive. A lock that hasn't been updated for 10 minutes counts as abandoned and another run may take it over (where
  a run's status can be read, a run that is still being aborted or timing out keeps its lock). A run re-checks its lock
  before every single write to the monitor's saved state and before every batch of 10 rows it delivers, and it watches its
  own lock: if the lock hasn't been refreshed for 9 minutes (one minute before another run could take it over), it stops
  saving, pushing and charging and says so in its status ("this run's monitor lock expired"). Once a run sees the lock is
  gone or expired it stops: no more rows are pushed or charged, it saves nothing more and leaves a new holder's lock alone.
  The next run delivers the rest. The only overlap left is the batch in flight at that moment: the next run re-sends it (at
  most 10 rows) free of charge, so some rows can come twice, but none is charged twice. That needs a run whose lock went
  unrefreshed for 9 minutes while it was still running, which should not happen in normal operation.
- **How long a running holder blocks a monitor:** a run that is still running keeps the lock until it ends, at the latest at
  its run timeout (1 hour by default, longer if you raise the timeout), because it refreshes the lock every minute for as long
  as its process is alive, even while it waits on a slow USAspending.gov answer. Until then each other run of that monitor
  (for example a schedule) stops with 0 rows, pays only its start event, and its status names the run that holds the lock
  and when that run last refreshed it. A run that crashed or froze stops refreshing, so its lock is free 10 minutes after
  its last refresh. To free a monitor at once, abort the run that holds it (a graceful abort releases the lock).
  The status says "can't check that run's status under limited permissions" only when Apify refused the status read; any
  other cause (an unknown run status, a failed request) is named as such.
- **Abort and timeout:** a graceful abort (the Console's default) releases the monitor's lock at once, so the next run can
  start right away. A monitor run also stops itself shortly before its run timeout (15 s early, or a third of the time for
  very short timeouts), releases the lock and ends as FAILED with a message saying so. Nothing is charged twice: rows it had
  already selected come first on the next run, otherwise the next run searches again from the saved position. A **forced abort** (abort without "gracefully") or an out-of-memory crash ends the run instantly, with no
  chance to clean up: the monitor's lock then stays until it expires, so the monitor is **blocked for up to 10 minutes**, and a
  run started in that time stops with 0 rows (and still pays its start event).
- **Many recipients:** in a monitor, recipient searches run in the order you list them (up to 25). Once the rows found so far
  reach `maxItems`, the remaining recipient searches are not run in this run; they keep their saved position and are searched
  on the next run. The status message and `RUN_STATS` name the skipped searches. If the first recipients keep filling
  `maxItems`, raise `maxItems` or split the list into several monitors.
  (A run without `monitorName` searches every recipient and returns the newest `maxItems` rows.)
- **Crash or abort during delivery:** rows are delivered in batches of 10. Each batch is marked as delivered in the monitor's
  state *before* its rows are pushed and charged, so no row is ever charged twice. A graceful abort or the run's own
  timeout stop first finishes the batch in progress (it waits up to 20 s for it, and never past the run's timeout). If the
  run is stopped in the middle of a batch anyway (out of memory, a forced abort, a crash, or a batch that needs longer), the
  next run of the monitor re-sends that one batch, **at most 10 rows, free of charge**, first and on top of its `maxItems`.
  Re-sent rows have `resentFree: true` in the dataset (normal rows don't have the field) and the status headline counts them
  separately ("N re-sent free"). Because they go first, even a re-send run that is itself stopped leaves at most 10 rows to
  re-send. Rows you already got from that batch can come again; nothing is lost and nothing is charged twice. A stop at any other point loses nothing: the selected rows are saved before
  enrichment, and the next run delivers them first. **That next run only delivers those saved rows (up to its `maxItems`; the
  rest stay saved for the run after) and doesn't search;** the search resumes once all saved rows are delivered.
- **Spending limit:** a run never pushes a row its remaining spending limit can't pay for. Rows it couldn't pay for stay saved
  and come first on the next run.
- **Large `maxItems` and the run timeout:** delivering takes about 0.15 s per row on Apify, so the default 3,600 s run timeout
  fits the largest `maxItems` (10,000) with time to spare for the search and enrichment. A monitor stops delivering shortly
  before the run's timeout (60 s before it, or 20% of the timeout for timeouts under 5 minutes, at least 5 s) and ends normally; the rows it
  didn't deliver stay saved (not charged) and come first on the next run, and the status message says so. If the search or
  enrichment itself runs into the timeout, the run stops itself just before it (see above). If you lower the run timeout a lot,
  a run delivers fewer rows; a run without `monitorName` just returns fewer rows.
- **Short run timeouts:** the delivery reserve shrinks with the timeout, so a 60 s timeout still returns rows; a `{}` run takes about 30 s, so don't go below 60 s.

### How "new" works

Each monitor run asks USAspending for contract records updated since that search's saved position (2-day overlap) and keeps:

- civilian awards first obligated in the last 150 days and on or after the monitor's start (the first run's window start).
  On the **first run** awards obligated up to 30 days before the window also count, so the first run may include a few
  recently obligated awards that were only modified, not newly published;
- DoD/USACE awards first obligated in the last 180 days, whenever they first appear (flagged). **Exception, a monitor's first
  window:** a DoD award obligated more than 60 days ago that shows up in the monitor's first window (its first run, and
  the runs that finish that window) may have been public long before the monitor started. It is **not delivered or charged**;
  the monitor just remembers it. DoD awards obligated in the last 60 days are delivered as `new`.
- **Known limit (DoD):** after the first window, an unseen DoD award obligated up to 180 days ago counts as
  `newly_visible_dod` when it is modified. Some of these were already public before the monitor started (published about 90
  days after award, but modified only now), so early runs of a new monitor can include and charge a few previously published
  DoD awards. This fades out within about 6 months of the monitor's start. Turn off `includeDodDelayed` if you don't want them.
  If a search returns more records than `maxPagesPerQuery` pages in one run, the monitor saves its exact position in that search
  and the next run continues from there; the status message says so only when that is the case.
  State (seen award ids, the position per search, the monitor's start) lives in your own Apify key-value store
  `govcon-award-feed-state`, per monitor. Seen ids are forgotten only once an award is too old to ever count as new again.

### Pricing

Pay per event: Actor start $0.01, plus **$0.005 per award row**. A weekly watch on 3 NAICS codes is about 50-150 rows.
The start event is Apify's built-in `apify-actor-start` event: the platform charges it when the run starts, before any
Actor code runs, so the Actor can't skip it. Apify counts it per GB of run memory (one event up to 1 GB, then one per extra GB),
so a run with more than 1 GB can count more than one start event. The Actor itself charges only award rows (it never charges
the start event; Apify's SDK refuses a manual charge of an `apify-` event, see Apify's pay-per-event docs on synthetic events).
A monitor run that stops because another run holds the monitor's lock returns and charges no rows, but **still pays its start
event** ($0.01 at the default 512 MB). Avoid starting the same monitor twice at once (for example two overlapping schedules).

### Data and privacy

Source: api.usaspending.gov (official, keyless, US-government data), polite rate (<= 2.5 requests/s per run; contact in the HTTP
`From` header). Apify runs share outbound IPs, so USAspending may rate-limit (HTTP 429); the Actor honors `Retry-After`
and uses per-request timeouts. Recipients that look like individuals have name and UEI removed and no parent lookup. Descriptions have
emails and phone numbers removed. No contact fields are requested.

### Limits

- No SAM.gov notice join and no SAM.gov award-notice rows in this version (use the [SAM.gov Scraper – New Contract Opportunity Alerts](https://apify.com/cce_partners/samgov-opportunity-radar) Actor for those).
- A very broad monitor (for example a whole agency at the start of a fiscal year) may need several runs to catch up; each run
  continues exactly where the previous one stopped.
- Set-aside/competition values are as reported to FPDS for the latest transaction of the award.

### GovCon suite by CCE Partners

- **[SAM.gov Scraper – New Contract Opportunity Alerts](https://apify.com/cce_partners/samgov-opportunity-radar)**: open bids before award, and SAM.gov award notices (often earlier than USAspending for DoD).
- **[Expiring Federal Contracts – Recompete Radar](https://apify.com/cce_partners/govcon-recompete-radar)**: the other end of the contract: awards whose period of performance ends in 3–18 months, with the incumbent.
- **[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).

Not affiliated with or endorsed by USAspending.gov, the U.S. Department of the Treasury or any government agency.

# Changelog

This Actor's version history is a separate document: https://apify.com/cce_partners/federal-contract-award-winners-feed/changelog.md

# Actor input Schema

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

NAICS codes with 2, 4 or 6 digits (USAspending accepts only these lengths; 2 and 4 digits match as a prefix). Give at least one of NAICS, PSC, awarding agency or recipient. If none is given, NAICS 541512 is used.

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

Product/Service codes, e.g. DA01, R499.

## `awardingAgencies` (type: `array`):

Top-tier agency names as on USAspending, e.g. Department of Veterans Affairs, Department of Defense (includes USACE and the military departments). Case, "Dept." and abbreviations such as VA, DOD or GSA are accepted. Names are checked against USAspending's agency list when the run starts: an unknown name stops the run before any search, so no award rows are charged.

## `setAsideTypes` (type: `array`):

FPDS set-aside codes, filtered by USAspending itself (exact): e.g. SBA (total small business), SBP, 8A, 8AN, HZC, HZS, SDVOSBC, SDVOSBS, WOSB, WOSBSS, EDWOSB, EDWOSBSS, VSA, VSS, NONE.

## `extentCompetedTypes` (type: `array`):

FPDS extent-competed codes, filtered by USAspending (exact): A full and open, B not available for competition, C not competed, D full and open after exclusion of sources, E follow-on, F competed under SAP, G not competed under SAP, CDO competitive delivery order, NDO non-competitive delivery order.

## `recipients` (type: `array`):

Competitor UEIs (exact match) or names (contained in the recipient name). One search per entry, max 25.

## `placeOfPerformanceStates` (type: `array`):

Two-letter state codes, e.g. VA, MD.

## `minAwardAmount` (type: `integer`):

Only awards with at least this much obligated.

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

Stateless runs and the first run of a monitor: records updated in the last N days. Later monitor runs continue from a saved cursor per search (2-day overlap).

## `includeDodDelayed` (type: `boolean`):

DoD and USACE awards appear on USAspending about 90 days after award by DoD policy. Included by default and flagged dataDelay=dod_90_day; changeType=newly_visible_dod when the award is more than 60 days old. DoD awards up to 180 days old are kept (civilian: 150). Turn off to exclude them.

## `includeAmended` (type: `boolean`):

Also return awards you already received whose amount, end date or recipient changed (changeType=amended). Each is a charged row.

## `resolveParents` (type: `boolean`):

Current parent from USAspending's recipient profile plus a small cited alias map for recent acquisitions.

## `maxDetailLookups` (type: `integer`):

Set-aside, extent competed, number of offers, solicitation procedures, potential value and potential end date need one USAspending request per award (~0.7 s each). Rows over the cap, or not reached before the enrichment deadline, have those fields null and detailLookedUp=false.

## `maxParentLookups` (type: `integer`):

New recipient profiles fetched per run (cached 30 days).

## `enrichmentDeadlineSecs` (type: `integer`):

Detail and parent lookups stop after this many seconds (and after 3 failures in a row); rows are still delivered, with the missing fields null. Keeps a slow USAspending from stalling the run.

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

Name of this watch. With a name, each run returns only awards that are new since the last run (saved state per name). Without a name the run is stateless: the newest awards in the lookback window (25 rows unless maxItems is set). Reusing a name with different filters keeps the old cursor and seen awards; use a new name or resetState. Letters, digits, "-" and "\_" only (1-100 characters), e.g. it-services-va. An empty value is the same as no name (stateless).

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

Also return awards already delivered and unchanged (changeType=unchanged) and amended ones. Each is a charged row.

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

Forget what this monitor has seen and start again from lookbackDays.

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

Cost cap. Default: 25 for a stateless run, 500 for a monitor. In a monitor, rows over the cap are delivered on the next run.

## `maxPagesPerQuery` (type: `integer`):

100 records per page. If reached, the monitor saves its position in the search and the next run continues from exactly there.

## `maxRequests` (type: `integer`):

Hard per-run budget for search requests (detail and parent lookups have their own caps above).

## `searchTimeBudgetSecs` (type: `integer`):

Overall time for all USAspending searches of one run (on top of the per-request timeouts). When it runs out the search stops cleanly, the monitor saves its position and the next run continues; the status message says so. If USAspending fails on one search (timeouts, 429, 5xx), the remaining searches of that run are skipped.

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

Legacy alias of monitorName, kept for older API calls. Use monitorName instead; monitorName wins when both are set. An empty value is the same as no name.

## Actor input object example

```json
{
  "naicsCodes": [
    "541512"
  ],
  "minAwardAmount": 25000,
  "lookbackDays": 14,
  "includeDodDelayed": true,
  "includeAmended": false,
  "resolveParents": true,
  "maxDetailLookups": 60,
  "maxParentLookups": 50,
  "enrichmentDeadlineSecs": 90,
  "emitUnchanged": false,
  "resetState": false,
  "maxPagesPerQuery": 30,
  "maxRequests": 400,
  "searchTimeBudgetSecs": 600
}
```

# Actor output Schema

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

No description

## `runStats` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

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

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "naicsCodes": [
        "541512"
    ],
    "minAwardAmount": 25000,
    "lookbackDays": 14
};

// Run the Actor and wait for it to finish
const run = await client.actor("cce_partners/federal-contract-award-winners-feed").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 = {
    "naicsCodes": ["541512"],
    "minAwardAmount": 25000,
    "lookbackDays": 14,
}

# Run the Actor and wait for it to finish
run = client.actor("cce_partners/federal-contract-award-winners-feed").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 '{
  "naicsCodes": [
    "541512"
  ],
  "minAwardAmount": 25000,
  "lookbackDays": 14
}' |
apify call cce_partners/federal-contract-award-winners-feed --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,cce_partners/federal-contract-award-winners-feed"
        }
    }
}
```

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/rimNmG6SdE5W90q6N/builds/bkE6UbfFSZos5srpO/openapi.json
