# Changelog of USAspending Scraper: New Federal Contract Awards Feed (`fecklesspleb/usaspending-new-awards-feed`) Actor

- **URL**: https://apify.com/fecklesspleb/usaspending-new-awards-feed/changelog.md
- **Full Actor documentation**: https://apify.com/fecklesspleb/usaspending-new-awards-feed.md

## Changelog

### Unreleased: free status row when nothing is new - 2026-10-31 (actor.json version unchanged: 0.1)

- When only-new is on (`onlyNewSinceLastRun` 'yes', or 'auto' in the default daily mode), USAspending.gov answered, at least one award was skipped as already delivered (same `stateKey`) and 0 data rows were saved, the run now pushes one row `{recordType: "status", status: "warning", message: "Not charged: no new items since last run. ...", source, host, skippedSeen, stateKey, checkedAt}` with no charged event (only the free default dataset-item event), logs a warning, mentions it in the status message and sets `statusRow: true` in `RUN_SUMMARY`. Same shape as the source-down row (`status_row()`), with status "warning" instead of "error". Not pushed when data was saved, when the source failed (the source-down "error" row is pushed instead, unchanged), when the cost limit stopped the run, when only-new is off, or when nothing was skipped as seen. This replaces the note in the entry below that runs finding nothing new are unchanged.
- Files: `my_actor/main.py` (new `push_no_new_status()`, called after `push_status_if_empty()` at the end of `run()`), `README.md` ("Nothing new" FAQ line), tests/test_main.py (5 new tests; `test_feed_paging_and_dedupe` now counts data rows separately from the new status row). Tests: 77 (was 72) passed. Pricing, defaults and data rows unchanged.

### Unreleased: cost wording - 2026-10-31 (actor.json version unchanged: 0.1; no code or pricing change)

- `my_actor/main.py`: the stale comment "an empty-input run ... costs at most $0.15" now says at most $0.009
  (3 x $0.003, the real default), matching the `DEFAULT_MAX_ITEMS` comment.
- Comments and docs no longer say a $0.00005 platform start event is charged: pricing has only `award` and
  `award-basic` ($0.003 each) and no start event. `{}` cost math is per-item only: 3 x $0.003 = $0.009; a start event
  would apply only if one were configured. Files: `my_actor/main.py`, `pricing.json` (`nationwideDailyFeed` note),
  `NOTES.md`, `tests/test_default_cost.py` (docstring; no start event added, limit $0.010), and the wording of the
  older 2026-10-31 entry below. Pricing logic, defaults and charging are unchanged.
- Checked, no change: the sample SBIR award 80NSSC27C0044 shows `numberOfOffersReceived: 500`; the public award API
  (`/api/v2/awards/CONT_AWD_80NSSC27C0044_8000_-NONE-_-NONE-/`) returns `number_of_offers_received: "500"` on
  2026-10-31, so the field is the source value, mapped as-is.

### Unreleased: free status row when the source is down - 2026-10-31 (actor.json version unchanged: 0.1)

#### Added: one free `recordType: "status"` row when nothing could be read

A local run with the network down used to finish SUCCEEDED with 0 items and nothing in the dataset, so the user saw an
empty result with no explanation. Now, when USAspending.gov cannot be reached or keeps answering with errors (robots.txt
unreadable, DNS/connection failure, repeated 5xx, 403, a non-JSON answer) and 0 data rows were saved, the run pushes one
row `{recordType: "status", status: "error", message: "Not charged: ..."}` with no charged event (only the
free default dataset-item event), logs a warning, mentions it in the status message and sets `statusRow: true` in
`RUN_SUMMARY`. The run still exits 0 / SUCCEEDED. Runs that saved data, and runs that simply found nothing new, are unchanged.

Files changed:

- `my_actor/main.py`: new `status_row()` and `push_status_if_empty()`, called at the end of `run()`; the early-stop
  handler also catches `httpx.HTTPError` and `ValueError` (non-JSON answer) so they stop cleanly instead of crashing.
- `README.md`: new "Source down" FAQ line.
- `tests/test_netdown_status.py`: new (DNS down, HTTP 500, non-JSON; helper is quiet when data was saved or nothing failed).

### Unreleased (2026-10-31, actor.json version unchanged: 0.1)

- Default `maxItems` lowered from 25 to 3 (code `DEFAULT_MAX_ITEMS`, input-schema default and prefill, and a new
  `actor.json` `exampleRunInput` = the prefill with `maxItems` 3). Ranger's cloud `{}` run of build 0.1.3 stopped at the
  $0.02 max-charge cap (6 awards x $0.003); `{}` now costs at most 3 x $0.003 = $0.009 (per-item events only; a start event
  would apply only if one is configured). README Price section, `pricing.json` note, NOTES.md and tests updated (new `tests/test_default_cost.py`).

### Unreleased (2026-10-29)

- Default `maxItems` lowered from 50 to 25, so an empty-input run finishes in under a minute (measured 48.8 s; it was 61.2 s at 50) and costs at most $0.075. Flat price confirmed: $0.003 for both `award` and `award-basic`.

### 0.1 (2026-10-29, not published)

- First version: new U.S. federal contract, IDV and grant awards from the official USAspending.gov API (`spending_by_award` with `new_awards_only`, plus `/awards/{id}/` detail).
- Modes: `daily_feed` (rolling `lookbackDays` window, default 7, with cross-run dedupe in a named state store, `stateKey`) and `date_range` (`startDate`/`endDate`).
- Filters: award types, place-of-performance and recipient states, NAICS, PSC, Assistance Listings, awarding agencies (name or abbreviation, resolved live), keywords, min/max amount.
- Detail enrichment (`includeDetail`): set-aside, extent competed, offers received, pricing type, parent recipient, solicitation ID. Awards whose detail is not yet published are held back in the daily feed and delivered with detail later; after three "not published" answers, newer awards are held back without a request.
- Privacy: individual and masked ("REDACTED DUE TO PII", "INDIVIDUAL RECIPIENT") recipients skipped; no executive names; no street address for sole proprietorships.
- Pay-per-event charging (`award`, `award-basic`), stops at the run's maximum cost; RUN_SUMMARY in the default key-value store.
