# USAspending Federal Award Search (`gubidonius/usaspending`) Actor

Searches US federal contract, grant and loan awards on USAspending.gov. Its paging flag turns false at 10,000 records while tens of thousands more are still there, so this Actor stops on a short page instead and uses the cursor to read past 50,000. No key and no login.

- **URL**: https://apify.com/gubidonius/usaspending.md
- **Developed by:** [Gregory Bolshakov](https://apify.com/gubidonius) (community)
- **Categories:** Business, Lead generation, MCP servers
- **Stats:** 1 total users, 0 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per event

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?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## USAspending Federal Award Search

Searches US federal contracts, grants, loans and direct payments on USAspending.gov. You get
the recipient, the agency, the obligated amount, the period of performance, the industry codes
and the recipient's registered address, as JSON, CSV or Excel.

No API key and no login. The data is public and this Actor reads it directly.

### The problem this one solves

USAspending tells you when to stop paging, and it is wrong.

Its `hasNext` flag is true up to record 10,000 and false after that. But the awards keep
coming. I paged the same search to record 50,000 and checked: pages 100, 101, 150, 300, 499
and 500 each returned 100 real awards, and no award appeared on two of them. So a scraper that
pages while `hasNext` is true stops at 10,000 and leaves 40,000 awards behind.

There is nothing to catch this with either. The API publishes no total at all. `total` and
`last_page` come back empty on every response, so there is no number to compare your row count
against.

This Actor ignores the flag. It stops when a page comes back short, which is the only honest
signal, and it reports how many times the flag was wrong during your run.

Past 50,000 awards, page paging is refused outright. There is a cursor for going further and
this Actor uses it, so a wide search is not capped at 50,000 either.

### One thing worth knowing about sorting

The cursor only accepts a text sort value. If you sort by award amount, the cursor stops
working and you are capped at 50,000. So this Actor sorts by award ID. It is a real trade and
you should know it is being made rather than find out later.

### Fields that are not where you would look

Three things I measured rather than assumed, on 200 awards across four families:

The award type lives in a different field depending on what kind of award it is. Contracts
fill `Contract Award Type` and leave `Award Type` empty. Grants and loans do the opposite. Use
`awardTypeLabel`, which was filled on all 200 where each single field was filled on 100.

Industry codes come back as objects, not text. Read as text they are empty on every contract
while being present on every contract.

The recipient's address is in an object. Asking for the flat field name is accepted by the API
and returns nothing at all, on every award family.

### Obligations are not outlays

`awardAmount` is what the agency committed. `totalOutlays` is what has actually been paid, and
it is often a real zero. A zero and an empty are different facts here and both are kept.

### Completeness

Every row carries `searchComplete` and `searchLimitedBy`. Asking for 1,000 awards and getting
1,000 is reported as `maxResults`, never as complete, because the run never saw the end.

### Cost

Pay per event, priced on the Actor page. A search matching nothing is free, because nothing is
charged until rows are written to your dataset.

# Actor input Schema

## `startDate` (type: `string`):

Beginning of the award action period, as YYYY-MM-DD. Required.

## `endDate` (type: `string`):

End of the award action period, as YYYY-MM-DD. Required.

## `awardTypes` (type: `array`):

Which families of award to include. USAspending will not accept more than one family in a single request, so picking two runs two searches and shares the row limit between them. The families also fill different fields: Contract Award Type is empty on grants and loans, and industry codes are empty on everything except contracts and IDVs.

## `agencyName` (type: `string`):

Full name of the top-tier awarding agency, for example National Science Foundation. Leave empty for all agencies.

## `recipientSearch` (type: `string`):

Free text matched against recipient names.

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

Free text matched across the award, including the description.

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

Two-letter US state codes where the work is performed, for example CA. This is where the work happens, which is often not where the recipient is registered.

## `naics` (type: `array`):

Industry codes to filter on.

## `psc` (type: `array`):

Product and service codes to filter on.

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

Stop after this many. Getting exactly this many is reported as maxResults and never as complete, because the run never saw the end of the results.

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

Ceiling on requests to USAspending. Each request returns 100 awards, so 200 requests reaches 20,000.

## Actor input object example

```json
{
  "startDate": "2026-01-01",
  "endDate": "2026-03-31",
  "awardTypes": [
    "contracts"
  ],
  "maxResults": 1000,
  "maxRequests": 200
}
```

# Actor output Schema

## `results` (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 = {
    "startDate": "2026-01-01",
    "endDate": "2026-03-31"
};

// Run the Actor and wait for it to finish
const run = await client.actor("gubidonius/usaspending").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 = {
    "startDate": "2026-01-01",
    "endDate": "2026-03-31",
}

# Run the Actor and wait for it to finish
run = client.actor("gubidonius/usaspending").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 '{
  "startDate": "2026-01-01",
  "endDate": "2026-03-31"
}' |
apify call gubidonius/usaspending --silent --output-dataset

```

## MCP server setup

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

```

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/tylgTeZItkcOcRM5f/builds/vhMYEWf27RGKid3JV/openapi.json
