# USDA Global Commodity Forecast Revision Signals (`starshaped_bullsnake/usda-global-commodity-forecast-revision-signals`) Actor

Monitor official USDA FAS PSD commodity forecasts and emit stateful revision bundles after a silent baseline.

- **URL**: https://apify.com/starshaped\_bullsnake/usda-global-commodity-forecast-revision-signals.md
- **Developed by:** [Starshape Tools](https://apify.com/starshaped_bullsnake) (community)
- **Categories:** Other
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $50.00 / 1,000 commodity forecast revision bundles

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

## USDA Global Commodity Forecast Revision Signals

Monitor official USDA Foreign Agricultural Service (FAS) Production, Supply and Distribution (PSD) data and emit structured revision bundles when previously accepted forecast values change.

This Actor is intended for agricultural commodity analysts, procurement and supply-chain teams, market-intelligence workflows, banks/insurers, researchers, and agents that need machine-readable USDA forecast revisions. It is **not** a price-prediction service, trading recommendation, WASDE text scraper, historical-release-vintage reconstruction service, or financial advice.

### Official source

Production acquisition uses only the official credential-free USDA FAS PSD SOAP service:

`https://apps.fas.usda.gov/PSDExternalAPIService/svcPSD_AMIS.asmx`

V1 uses `getDatabyCommodityPerYear`, which returns the available countries and attributes for one requested commodity code and market year.

The legacy PSD Online REST lookup endpoints are **not** a production dependency. Empirical runtime testing returned `403 "Bad API Key"`, so V1 does not require those endpoints, an API.Data.Gov key, private lookup caches, or third-party mirrors.

### What one paid signal means

V1 has exactly one business signal type:

`FORECAST_REVISION`

One paid Dataset item represents one changed:

`commodity × country × marketYear`

bundle for one observed semantic transition.

If Production, Exports, and Ending Stocks all change in the same bundle in one accepted source cycle, they appear together in one `changes[]` array and still produce **one** Dataset item. There is no additional charge per changed attribute.

### Stable source identity

The raw PSD row identity is:

`CommodityCode + CountryCode + MarketYear + AttributeId`

`Value` is the mutable monitored field and is not part of identity.

`UnitId`, `CalendarYear`, and `Month` are semantic guard fields, not identity fields. Empirical preflight profiling covered Corn, Wheat, and Soybeans across three populated market years each: 9 commodity-year datasets and 15,381 source rows with zero stable-key duplicates, zero null key fields, and zero null/non-finite values.

### First live run is a silent baseline

The first valid run for a unique monitor scope:

1. acquires every requested commodity/year SOAP slice;
2. validates and normalizes the complete source cycle;
3. validates optional country/attribute filters against that acquired scope;
4. stores the accepted monitor snapshot;
5. emits **zero** business signals.

Existing PSD values are not replayed as historical revisions. This Actor only guarantees revisions observed after that monitor's first accepted baseline.

### Explicit monitor scope

Live inputs are:

- `commodityCodes[]` — required, strings, maximum 10; leading zeros are preserved;
- `marketYears[]` — required, explicit 4-digit years, maximum 6;
- `countryCodes[]` — optional;
- `attributeIds[]` — optional integer IDs;
- `maxItems` — output cap only.

Market years never roll automatically with the calendar. Changing commodity/year/country/attribute scope creates a new monitor fingerprint and therefore a new silent baseline. Changing only `maxItems` keeps the same monitor identity.

### Country and attribute filter validation

Optional country and attribute filters are validated against the official PSD rows returned for the commodity/year scope of the current live run. A requested filter value that is absent from that scope causes the run to fail rather than being silently ignored.

This is **scope-membership validation**. The Actor does not claim to maintain a global USDA country-code or attribute registry.

All requested commodity/year slices are acquired successfully **before** country/attribute filter validation begins. Partial source success is never used to generate revisions.

### Revision semantics

For an existing raw row, only a `value` change can create a forecast revision.

For every changed attribute, the signal includes:

- `rowKey`
- `attributeId`
- `attributeName`
- `unitId`
- `unitDescription`
- `before`
- `after`
- `delta`
- `direction` (`RAISED` or `CUT`)

The signal ID is deterministic from the monitor fingerprint, bundle identity, sorted changed row keys, and before/after values. Detection time is not part of the signal ID.

### New rows are not paid events

A raw PSD row that appears after baseline is accepted silently into the next snapshot.

V1 is a **revision monitor**, not a new-series monitor. A newly appearing row therefore creates no paid Dataset item. Later value changes to that accepted row can produce normal `FORECAST_REVISION` signals.

### Fail-closed anomaly handling

#### Previously accepted row disappears

If a previously accepted raw row is missing from the next fully acquired monitor scope, the Actor does **not** interpret that as forecast removal.

It fails closed as:

`SOURCE_ROW_DISAPPEARANCE_ANOMALY`

with:

- business signals = 0
- accepted snapshot mutation = 0

This protects against partial or structurally changed official responses.

#### Semantic guard field changes

For the same raw row key, a change to any of:

- `UnitId`
- `CalendarYear`
- `Month`

fails closed as:

`SOURCE_SEMANTIC_ANOMALY`

The old and new numeric values are not compared as a forecast revision, and accepted state remains unchanged.

Description-only changes (`commodityName`, `countryName`, `attributeName`, `unitDescription`) do not create paid signals. Updated descriptions can be accepted silently when IDs and semantic guard fields remain stable.

### Atomic source cycle

A live run must acquire and validate **every** requested commodity/year SOAP slice before comparison.

The Actor fails closed without accepted-state mutation on, among other conditions:

- official HTTP/network failure;
- unexpected authentication requirement;
- empty/unsupported requested commodity-year;
- SOAP fault or parse failure;
- unexpected source host/path or content type;
- missing canonical field;
- source commodity/year mismatch;
- duplicate stable key;
- null/non-finite value;
- previously accepted row disappearance;
- semantic guard-field change;
- partial acquisition.

No revisions are emitted from a partially acquired source cycle.

### Pending queue and `maxItems`

`maxItems` limits Dataset emission only.

If 80 revision bundles are detected and `maxItems` is 30, the first 30 deterministic queued events are emitted and the remaining 50 stay pending in monitor state. Pending events drain before newly detected events.

Pending identity is the deterministic `signalId`, not merely the commodity/country/year bundle. If the same bundle is revised again before an older pending transition is delivered, both distinct transitions are preserved when their signal IDs differ.

The accepted source snapshot may advance after successful Dataset + `OUTPUT` writes while undelivered signals remain pending.

### State-commit safety

State is committed last:

1. Dataset signals;
2. `OUTPUT` summary;
3. compressed monitor state.

If Dataset or `OUTPUT` persistence fails, accepted monitor state is not committed.

### Example live input

```json
{
  "mode": "live",
  "commodityCodes": ["0440000"],
  "marketYears": [2026],
  "countryCodes": ["BR", "US"],
  "attributeIds": [28, 88],
  "maxItems": 30
}
```

The specific codes and IDs above are an input-format example; users should select the official PSD scope relevant to their monitoring task.

### Example output shape

```json
{
  "signalType": "FORECAST_REVISION",
  "commodityCode": "0440000",
  "commodityName": "Example commodity",
  "countryCode": "BR",
  "countryName": "Example country",
  "marketYear": 2026,
  "changes": [
    {
      "rowKey": "0440000|BR|2026|28",
      "attributeId": 28,
      "attributeName": "Example attribute",
      "unitId": 8,
      "unitDescription": "Example unit",
      "before": 130000,
      "after": 132000,
      "delta": 2000,
      "direction": "RAISED"
    }
  ],
  "changedAttributeCount": 1
}
```

The values in this documentation example are illustrative and are not represented as current USDA observations.

### Deterministic safe sample

`mode: "sample"` runs a self-contained synthetic demonstration through the production normalization, diff, bundling, signal-identity, and pending-queue logic.

It demonstrates:

1. one Corn/Brazil-like synthetic bundle with two changed attributes in one Dataset item;
2. a second synthetic commodity/country/year bundle with an upward revision;
3. a third synthetic bundle with a downward revision.

Hard sample guarantees:

- USDA HTTP requests: 0
- production named KVS open/read/write: 0
- production snapshot updated: false
- signal type: `FORECAST_REVISION` only
- synthetic values are **not USDA observations**

### Update cadence

USDA PSD update frequency varies by commodity. USDA describes monthly review/update for WASDE-covered commodities, while some horticultural products are typically updated less frequently (for example, around twice per year).

The Actor is therefore positioned as a **commodity-specific revision monitor**, not as a claim that every PSD commodity is revised monthly. Schedule it according to the commodity scope and operational need.

### Legal reuse and attribution

Dataset-specific evidence in the USDA Ag Data Commons record for the Foreign Agricultural Service Production, Supply and Distribution Database identifies FAS as publisher, Public Access Level as Public, and the licence as **U.S. Public Domain**. A separate USDA catalog-derived record may state CC BY 4.0; both permit commercial reuse.

This Actor uses the more conservative attribution posture:

> Source: USDA Foreign Agricultural Service, Production, Supply and Distribution (PSD) Database.

No USDA endorsement is implied. USDA seals or logos are not used to imply endorsement.

### Privacy

V1 processes commodity, country, market-year, attribute, unit, and numeric forecast data. It does not enrich or monitor personal data.

### Important limitations

This Actor:

- detects snapshot-to-snapshot changes observed after baseline;
- does **not** reconstruct historical release vintages;
- does **not** predict commodity prices;
- does **not** provide trading or financial advice;
- does **not** claim every numeric revision is economically material;
- does **not** claim every commodity updates monthly.

# Actor input Schema

## `mode` (type: `string`):

Live acquires official USDA FAS PSD SOAP rows. Sample uses deterministic synthetic fixtures with zero USDA requests and zero production-state access.

## `commodityCodes` (type: `array`):

Required in live mode. USDA PSD commodity codes as strings; leading zeros are preserved. Maximum 10.

## `marketYears` (type: `array`):

Required in live mode. Explicit 4-digit PSD market years. Maximum 6; no rolling relative window is applied.

## `countryCodes` (type: `array`):

Optional. Values are validated against official PSD rows returned for the requested commodity/year scope; absent values fail the run instead of being silently ignored.

## `attributeIds` (type: `array`):

Optional integer PSD attribute IDs. Values are validated against official PSD rows returned for the requested commodity/year scope; absent values fail the run.

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

Caps Dataset output only. Undelivered revision transitions remain in the monitor pending queue.

## Actor input object example

```json
{
  "mode": "sample",
  "maxItems": 30
}
```

# Actor output Schema

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

No description

## `OUTPUT` (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 = {
    "mode": "sample",
    "maxItems": 30
};

// Run the Actor and wait for it to finish
const run = await client.actor("starshaped_bullsnake/usda-global-commodity-forecast-revision-signals").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 = {
    "mode": "sample",
    "maxItems": 30,
}

# Run the Actor and wait for it to finish
run = client.actor("starshaped_bullsnake/usda-global-commodity-forecast-revision-signals").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 '{
  "mode": "sample",
  "maxItems": 30
}' |
apify call starshaped_bullsnake/usda-global-commodity-forecast-revision-signals --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,starshaped_bullsnake/usda-global-commodity-forecast-revision-signals"
        }
    }
}
```

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/c5EAD4Ls1ebrMwbfN/builds/WPmPHRIOP3Z8ZMFMU/openapi.json
