# AgroLens Africa (`graciecreatives/agrolens-africa`) Actor

Transform Nigerian food-price estimates into normalized records, physical-market rankings, national comparisons, and confidence-aware analytics.

- **URL**: https://apify.com/graciecreatives/agrolens-africa.md
- **Developed by:** [Precious Egbewale](https://apify.com/graciecreatives) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.00 / 1,000 price-records

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

### What does AgroLens Africa do?

**AgroLens Africa is a food-price lookup API for Nigeria.** Choose a food such as maize, rice, or beans and get its latest available national estimate plus prices across the available Nigerian physical markets—with normalized units, source date, freshness, provenance, and market comparisons. You can optionally narrow the lookup to a market or state, or explicitly request a full multi-commodity export.

AgroLens currently integrates the **World Bank Real Time Food Prices dataset** and uses a modular source-adapter architecture designed to support additional licensed African market-data sources.

The Actor separates `physical_market`, `regional_average`, `national_average`, and `market_average` records before calculating statistics. This prevents a label such as “South West, Geopolitical Zone” from being ranked as a physical market. On Apify, results are available through four datasets and their APIs, with scheduling, integrations, monitoring, and downloadable formats built in.

The easiest first run is to leave **Operation** as **Look up a food price**, choose a **Food / commodity**, and select **Start**. The default query covers Nigeria nationwide; no state is required.

### Why use AgroLens Africa?

- **Market selection:** find the cheapest and most expensive physical markets for each commodity.
- **National comparison:** measure each physical market against the source national-average estimate.
- **Food-price monitoring:** inspect period-over-period movement, annual inflation, and inflation-confidence indicators. Changes are labelled monthly only when the source periods are exactly consecutive calendar months.
- **Auditable geography:** retain regional and national aggregates without mixing them into physical-market analytics.
- **Analysis-ready output:** receive raw prices, normalized per-kilogram prices, provenance, validation status, and source coordinates where available.
- **Developer access:** consume the same structured records through the Apify API, webhooks, schedules, and integrations.

AgroLens Africa is intended for analysts, food-security teams, researchers, procurement teams, journalists, and developers who need transparent Nigerian agricultural price records without rebuilding the ingestion and classification pipeline.

### How to use AgroLens Africa

1. Open the Actor in Apify Console.
2. In the **Input** tab, leave **Operation** on **Look up a food price** and choose a food.
3. Optionally enter a market or state; leave both blank for nationwide results.
4. Select **Start** and wait for the run to finish.
5. Open **Output → Price lookup answer** for the direct JSON response, or use the default dataset to inspect the supporting market observations. Select **Export the full dataset** only when you want a bulk export.

For local development, install and run the Actor with:

```bash
python -m venv .venv
source .venv/bin/activate
pip install -r requirements-dev.txt
apify validate-schema
ruff check .
pytest -q
APIFY_LOCAL_STORAGE_DIR="$(mktemp -d)" apify run
```

The resulting `storage/` directory is local only; it is not synchronized to Apify Console. Deploy with `apify push` only after reviewing the Actor input and licensing configuration.

The automated tests live in `tests/`, split into `tests/unit/` (services and analytics), `tests/sources/` (source adapters), and `tests/integration/` (Actor contract, dataset schemas, and full-snapshot checks). The tree deliberately contains no `__init__.py` files: the Apify CLI treats a top-level directory containing one as an Actor package and refuses to run when it finds more than one, and `pyproject.toml` adds the project root to `pythonpath` so imports still resolve. Runtime dependencies are pinned in `requirements.txt`; test and lint tooling is pinned in `requirements-dev.txt`. Install both, then run `pytest -q` and `ruff check .`. Most tests use a small 10-row CSV fixture under `tests/fixtures/`; the tests that read the full 24 MB snapshot are marked `integration` and can be skipped with `pytest -q -m "not integration"`. Validate the schemas with `apify validate-schema`. The same three checks run in CI on every push and pull request via `.github/workflows/tests.yml` (ruff, then pytest, then `apify validate-schema`).

A single-page dashboard is included under `dashboard/index.html`. It needs no build step: open it in a browser, paste a run ID (or drop exported dataset JSON files), and it renders the summaries, rankings, and national comparisons for that run. See `dashboard/README.md` for details.

### Input

| Field | Type | Default | Description |
| --- | --- | --- | --- |
| `operation` | string | `price_lookup` | `price_lookup` returns a direct food-price answer. `full_dataset` exports records for multiple commodities. |
| `commodity` | string | `maize` | Food to look up. All 12 supported commodities are selectable. Used in lookup mode. |
| `market` | string | none | Optional exact market name. Leave blank for nationwide results. |
| `state` | string | none | Optional state name; used only to narrow a lookup or disambiguate a market. |
| `commodities` | string array | all supported commodities | Used only when `operation` is `full_dataset`. |
| `country` | string | `Nigeria` | Country to process. Nigeria is currently the only supported value. |
| `sources` | string array | `world_bank` | Source adapters to run. The public Actor currently accepts only `world_bank`. |

Unit normalization (package sizes to per-kilogram prices) is always applied as a data-integrity invariant, so there is no separate `normalizeUnits` switch. Analytics are always generated alongside the price records, so there is no separate `includeAnalytics` switch either.

Example input:

```json
{
  "operation": "price_lookup",
  "commodity": "beans",
  "sources": ["world_bank"]
}
```

This returns a nationwide answer: the source national estimate, average and median of available physical-market estimates, low/high markets, and each supporting market observation. To ask for a specific market, add `"market": "Aba", "state": "Abia"`. For a state-level mean, provide `"state": "Abia"` without a market. Market matching is exact apart from letter case; the Actor does not guess a fuzzy match.

To explicitly request bulk output:

```json
{
  "operation": "full_dataset",
  "commodities": ["maize", "rice", "beans"],
  "sources": ["world_bank"]
}
```

#### Use AgroLens as a food-price API

Run the Actor with a single food query, then read the `PRICE_LOOKUP` key-value-store record from the run. With a commodity and no location filters, the answer is nationwide: it includes a national source estimate and supporting observations across all available physical markets. A state is never required.

```bash
curl --request POST \
  --url 'https://api.apify.com/v2/acts/graciecreatives~agrolens-africa/runs?waitForFinish=60' \
  --header 'Authorization: Bearer YOUR_APIFY_TOKEN' \
  --header 'Content-Type: application/json' \
  --data '{"operation":"price_lookup","commodity":"beans"}'
```

Use the run's `defaultKeyValueStoreId` from the API response to read the direct answer:

```bash
curl --header 'Authorization: Bearer YOUR_APIFY_TOKEN' \
  'https://api.apify.com/v2/key-value-stores/KEY_VALUE_STORE_ID/records/PRICE_LOOKUP'
```

The `priceLookup` link in the run output points to this same JSON record. It reports the national source average, average and median physical-market estimates, lowest/highest markets, all supporting market prices, units and divisors, source date/freshness, confidence/interpolation metadata, and citation. The default Dataset contains the matching physical-market observations so clients can process them directly.

#### Input validation and empty-result behavior

AgroLens validates the whole contract before it collects anything:

- `country` must be `Nigeria`.
- `commodities` and `sources` must be non-empty arrays of unique supported identifiers; duplicates and unsupported values are rejected.
- Unknown input fields are rejected.

If input is invalid, the run fails with an explicit error instead of finishing with zero output. After collection, every requested source is classified as `success`, `empty`, `failed`, or `disabled`:

- A source is successful only if it returns at least one relevant record that passes validation. Invalid records are rejected; if every requested source fails or returns no usable records, the run raises an error and stops.
- **Partial success** (at least one source succeeds) stays successful, but logs a warning and writes a run manifest.
- Missing national averages or derived datasets are logged as warnings rather than silently skipped, and every run writes a `RUN_SUMMARY` record to the default key-value store.
- The `RUN_SUMMARY` record is the machine-readable run manifest. Its shape is documented in *Run summary* below.

### Output

The Actor returns a direct JSON query answer in the `PRICE_LOOKUP` key-value-store record and creates run-scoped datasets for the supporting observations and intelligence:

| Dataset | Contents |
| --- | --- |
| Price records | For lookups, the selected food's nationwide physical-market records or requested market/state matches. In full dataset mode, all requested source records including retained aggregates. |
| Market summaries | Physical-market averages, medians, extrema, spreads, inflation, confidence, and separate aggregate counts. |
| Market comparisons | Each physical-market price compared with the source national-average estimate. |
| Market rankings | Physical markets ordered from cheapest to most expensive for each commodity. |

Every run also writes a `RUN_SUMMARY` record to the default key-value store. It is the run manifest: it records the run status (`success`, `partial_success`, or `failed`), per-source outcomes (including returned, relevant, usable, rejected, and emitted counts), source versions, warnings, and the run identity. Read it at `/records/RUN_SUMMARY`.

A full-dataset run over all 12 commodities is expected to produce:

| Dataset | Expected records |
| --- | ---: |
| Default price records | 888 |
| Market summaries | 12 |
| Market comparisons | 804 |
| Market rankings | 804 |

Example default-dataset record (abridged):

```json
{
  "record_id": "f3345e7a5e0fdcb2db9bd5dec58c9a0d8b13d59523f2915a5f4161f4492f4150",
  "commodity": "maize",
  "commodity_raw": "Maize (White)",
  "data_kind": "modeled_estimate",
  "geography_type": "physical_market",
  "market": "Aba",
  "state": "Abia",
  "country": "Nigeria",
  "location_id": "gid_5150000073600000",
  "latitude": 5.15,
  "longitude": 7.36,
  "normalized_price": 1230.42,
  "normalized_unit": "kg",
  "normalization_divisor": 1.0,
  "price": 1230.42,
  "unit_raw": "kg",
  "comparison_period": "previous_calendar_month",
  "percentage_change": 0.39,
  "source_period": "2026-08",
  "source_date": "2026-08-01",
  "source_date_raw": "2026-08-01",
  "published_at": "2026-08-24",
  "freshness_days": 55,
  "annual_inflation": -9.13,
  "inflation_trust_score": 9.7,
  "spatially_interpolated": false,
  "source_name": "World Bank Real Time Food Prices",
  "source_dataset_version": "2026-08-24",
  "collected_at": "2026-09-25T10:00:00+00:00",
  "validation_status": "valid"
}
```

Example values illustrate the record shape; reruns can differ by source version and collection time. You can download datasets in formats including JSON, CSV, Excel, XML, and HTML.

The dataset schemas in `.actor/` describe the emitted field types and semantics, and provide labelled Console views. Individual fields are documented in the *Data table and methodology* section.

### Data table and methodology

#### Default price-record fields

| Field | Meaning |
| --- | --- |
| `commodity` | Stable AgroLens commodity identifier. |
| `commodity_raw` | Commodity label supplied by the source. |
| `data_kind` | `modeled_estimate` or `published_market_quote`. World Bank RTFP values are `modeled_estimate`. |
| `geography_type` | `physical_market`, `regional_average`, `national_average`, or `market_average` when classified. |
| `market`, `state`, `country` | Source geography labels. |
| `location_id` | Stable source location identifier used to match the same place across periods. |
| `latitude`, `longitude` | Point coordinates for `physical_market` records. **Non-physical aggregates carry `null` coordinates** so they are never plotted as markers. |
| `normalized_price`, `normalized_unit` | Comparable price and configured unit. |
| `normalization_divisor` | Source package weight in kg; `normalized_price = price / normalization_divisor`. For a 2.5 kg beans package priced at ₦250, the normalized price is ₦100/kg. |
| `price`, `unit_raw` | Price estimate and unit as interpreted from the source. |
| `comparison_period` | `previous_calendar_month` only when the current and previous source dates are exactly consecutive months; otherwise `previous_reported_period`. |
| `percentage_change` | Change from the previous reported period's normalized price. Treat it as monthly only when `comparison_period` is `previous_calendar_month`. |
| `annual_inflation` | Source annual-inflation estimate, when available. |
| `inflation_trust_score` | World Bank confidence in the preceding-12-month inflation estimate. It is not a price-accuracy or overall-source score. |
| `spatially_interpolated` | True only when the price was derived solely through spatial interpolation. `false` does **not** mean the value was directly observed; it may still be modelled or imputed. |
| `source_name`, `source_date`, `source_date_raw`, `source_dataset_version`, `source_citation`, `collected_at` | Provenance: normalized ISO date, the original source date string, dataset version, citation, and collection timestamp. |
| `source_period`, `published_at`, `freshness_days` | Freshness: source period as `YYYY-MM`, the source dataset's publication date, and the number of days between the source period and collection. See *Freshness* below. |
| `record_id`, `deduplication_key`, `content_hash`, `run_id`, `schema_version`, `normalization_version`, `source_id`, `source_product_id`, `source_dataset_id`, `source_file_sha256`, `country_iso3`, `retrieval_method` | Stable identity and lineage. See *Stable identity and lineage* below. |
| `validation_status` | `valid`, `warning`, or `invalid` after AgroLens checks. |

#### How records and rankings are produced

For each requested commodity, the adapter selects its latest reported period and preceding reported period from the bundled snapshot. A geography missing either period can therefore have no movement comparison; missing latest-market cases are surfaced as collection warnings. Prices retain the source text separately from parsed numeric values. Configured package sizes are converted to normalized per-kilogram prices, dates are normalized to ISO while retaining `source_date_raw`, and source inflation/confidence fields are preserved. Every record is labelled with `data_kind` (`modeled_estimate` for World Bank RTFP), the source dataset version, and source citation.

The source conversion settings are explicit and versioned with the record schema:

| Commodity | Source package | Divisor | Output unit |
| --- | --- | ---: | --- |
| maize, rice, gari, beef, goat meat, sorghum | 1 kg | 1.0 | NGN/kg |
| beans, yam | 2.5 kg | 2.5 | NGN/kg |
| groundnuts | 2.2 kg | 2.2 | NGN/kg |
| maize flour | 2.1 kg | 2.1 | NGN/kg |
| millet | 2.6 kg | 2.6 | NGN/kg |
| onions | 0.5 kg | 0.5 | NGN/kg |

Change is only described as monthly when `comparison_period` is `previous_calendar_month`. Because the preceding period is the previous period present in the snapshot rather than a guaranteed calendar month, gaps are labelled `previous_reported_period`, and the derived comparison and ranking datasets leave `monthly_percentage_change` empty for those records.

Geography is assigned during ingestion from World Bank administrative metadata. Only records with `geography_type == "physical_market"` contribute to physical-market averages, inflation averages, extrema, spreads, comparisons, and rankings. Regional, national, and market-average source records remain available in the default dataset for auditability. The source `market_average` value is exposed only as `source_market_average_price`; its semantics have not been independently verified and it is not the Actor’s authoritative physical-market average.

#### Terminology and transparency

- **Records vs. observations.** AgroLens returns *price records and estimates*, not independently observed market quotations. The World Bank RTFP series mixes direct price information with modelled, imputed, and spatially interpolated values.
- **`data_kind`.** Classifies each value as a `modeled_estimate` or a `published_market_quote`. World Bank RTFP values are `modeled_estimate`.
- **`inflation_trust_score`.** The World Bank defines this as confidence in the preceding-12-month inflation estimate, not as price accuracy or overall source reliability. Scores below 6 set `confidence_flag` to `extremely_low_inflation_confidence` and are counted in `extremely_low_inflation_confidence_count`.
- **`spatially_interpolated`.** `true` means the price was derived solely through spatial interpolation. `false` means only that it was not derived solely through spatial interpolation; the value may still be modelled or imputed. AgroLens never labels `false` as "observed".
- **Comparison periods.** `comparison_period` separates `previous_calendar_month` from `previous_reported_period`, and only the former is surfaced as `monthly_percentage_change`.
- **Summary counts.** `price_record_count` counts every usable price record and estimate (physical markets plus retained aggregates); `physical_market_count` counts physical markets only. `average_monthly_percentage_change` averages consecutive-month comparisons only, and `monthly_comparison_count` reports how many records qualified.

#### Stable identity and lineage

Every price record carries stable identity and provenance fields so downstream systems can deduplicate records and detect source revisions:

| Field | Meaning |
| --- | --- |
| `record_id` | SHA-256 of the natural key. Stable when a source value is revised. |
| `deduplication_key` | Human-readable natural key: `source_id\|country_iso3\|source_product_id\|location_id\|source_date\|unit_raw`. |
| `content_hash` | SHA-256 of the value-bearing fields. A new `content_hash` under the same `record_id` signals a revised value. |
| `run_id` | Apify run that produced the record. |
| `schema_version`, `normalization_version` | Versions of the emitted record shape and of the normalization rules. |
| `source_id`, `source_product_id`, `source_dataset_id`, `source_dataset_version` | Source, product, and dataset identifiers. |
| `source_file_sha256` | SHA-256 of the exact snapshot file that was read. |
| `country_iso3`, `retrieval_method` | Country code and how the value was retrieved (for example `bundled_snapshot_file`). |

Price is deliberately excluded from `record_id`, so when the World Bank revises an estimate the record keeps its identity while `content_hash` changes. Derived summaries, comparisons, and rankings also carry `run_id` and `source_id` for traceability.

#### Freshness

Freshness fields make the age of each estimate explicit, because a fixed snapshot is not a live feed:

- `source_period` — the source period as `YYYY-MM` (for example `2026-08`).
- `published_at` — the publication/version date of the source dataset (for example `2026-08-24`).
- `freshness_days` — whole days between the source period and the collection date, so consumers can alert on stale data.

#### Geospatial handling

The source snapshot attaches coordinates to some non-physical rows (the national average uses Abuja, and each geopolitical zone uses a centroid). AgroLens only keeps coordinates for `geography_type == "physical_market"`; regional, national, and market averages are emitted with `latitude` and `longitude` set to `null`. This means a `latitude`/`longitude` pair always identifies a real market point and aggregates cannot be mis-mapped.

#### Run summary (`RUN_SUMMARY`)

Every run writes the `RUN_SUMMARY` key to the default key-value store. Its fields are:

| Field | Meaning |
| --- | --- |
| `status` | `success`, `partial_success`, or `failed`. |
| `run_id` | Apify run ID. |
| `schema_version` | Emitted record schema version. |
| `country` | Country processed. |
| `requested_commodities`, `requested_sources` | Exactly what the run was asked to produce. |
| `source_results` | Per requested source: `status`, `records_returned`, relevant `records`, `usable`, `rejected`, `emitted`, `source_version`, `source_dataset_id`, `source_file_sha256`, `error_type`, and warnings. Disabled sources are not included in this object. |
| `counts` | `records`, `summaries`, `comparisons`, and `rankings` emitted. |
| `warnings` | Human-readable warnings, including missing derived datasets. |
| `generated_at` | ISO timestamp when the manifest was written. |

#### Typed output schemas

All four dataset schemas (`.actor/dataset_schema.json` and the three derived schemas) describe emitted fields and include an `overview` view. Optional values are represented as nullable; the schemas do not mark fields as required. The output schema links the run summary through `{{links.apiDefaultKeyValueStoreUrl}}/records/RUN_SUMMARY`.

#### Multi-country design

`my_actor/config/countries.py` defines a country configuration abstraction, but Nigeria is the only registered country and the current World Bank adapter still contains Nigeria-specific aggregate/geography handling. Supporting another country will require a compatible source adapter and country-specific mapping/classification work; multi-country ingestion is not currently a configuration-only feature. The public input contract accepts only registered countries.

### Data source

AgroLens Africa currently uses the World Bank **Real Time Food Prices (RTFP)** dataset for Nigeria. The repository bundles `data/NGA_RTFP_mkt_2007_2026-08-24.csv`, a fixed snapshot; it does not automatically download a newer World Bank file during a run.

**Dataset:** *Monthly food price estimates by product and market*

**Reference:** `NGA_2021_RTFP_v02_M`

**License:** Creative Commons Attribution 4.0 International (CC BY 4.0), under the [World Bank dataset terms](https://www.worldbank.org/ext/en/legal/terms-conditions/datasets). The [World Bank Microdata Library dataset page](https://microdata.worldbank.org/catalog/4503) identifies this price-estimate dataset as open data and provides the citation below.

**Citation:**

> Andrée, B. P. J. (2021). *Monthly food price estimates by product and market* (Version 2026-08-24). NGA\_2021\_RTFP\_v02\_M. Washington, DC: World Bank Microdata Library. <https://doi.org/10.48529/2ZH0-JF55>

The [World Bank catalog record](https://microdata.worldbank.org/index.php/catalog/4503/study-description) states that the estimates are based on publicly available data and that the price-estimate dataset is published as open data. The catalog identifies World Food Programme, FAO, and selected national statistical offices as underlying sources. The MIT license in this repository covers AgroLens code; upstream data and source content remain subject to their respective terms and citation requirements.

AgroLens Africa is an independent project and is not endorsed by the World Bank.

The source dataset contains modeled/estimated price data. It combines direct price information with machine-learning estimation of missing values; historical values may be revised, outliers may be adjusted, and missing entries may be predicted from exchange rates, related products, or other markets. AgroLens preserves source attribution and confidence metadata and should not be interpreted as providing independently observed real-time market quotations, audited quotations, or financial advice.

#### API examples

Use the run ID and the dataset IDs returned by the Apify API; send credentials in the `Authorization` header rather than query parameters:

```bash
curl -H "Authorization: Bearer $APIFY_TOKEN" \
  "https://api.apify.com/v2/actor-runs/$RUN_ID"

curl -H "Authorization: Bearer $APIFY_TOKEN" \
  "https://api.apify.com/v2/datasets/$DATASET_ID/items?format=json&clean=true&limit=100"
```

The default dataset contains source records and estimates. Run-scoped named datasets contain market summaries, comparisons, and rankings; use the dataset IDs exposed by the run/API response. Filter physical-market records by `geography_type` and pin analysis to `source_dataset_version` and `source_period`.

### Pricing / Cost estimation

#### How much does it cost to process Nigerian food-price estimates?

Apify cost depends on compute time, memory, and dataset storage under your plan. The default World Bank run reads a packaged CSV and normally has no browser cost. Start with one commodity, review the run’s actual usage, and scale that measurement to your commodity count and schedule. If your Apify plan includes free platform credits, eligible usage consumes those credits first; consult [current Apify pricing](https://apify.com/pricing) for allowances.

The intended Store price is **$0.01 once per successful run**, after the output datasets and run manifest have been written. The Actor code skips local runs and skips billing until PPE is active. Store monetization is not active yet: the owner must first add payout billing details in Apify Console, then activate the PPE event and publish the Actor. Apify platform usage is not configured as a separate PPE charge and may still be subject to the user's Apify plan.

### Tips and advanced options

- Start with `world_bank` and one commodity when validating an integration.
- Filter the default dataset on `geography_type == "physical_market"` for independent physical-market calculations.
- Use `data_kind`, `spatially_interpolated`, and `inflation_trust_score` when setting analytical quality thresholds, remembering that `false` interpolation means "not solely interpolated", not "observed".
- Pin analyses to the documented source version because the World Bank live series can revise history.
- Limit commodities and run frequency to reduce storage and compute use.
- Keep regional and national aggregates for provenance, but never treat them as physical locations.

### FAQ, disclaimers, and support

#### Why are there 74 records but only 67 physical markets per commodity?

The bundled latest World Bank slice contains 67 physical markets, 5 geopolitical-zone aggregates, 1 national-average record, and 1 source market-average record. The 7 aggregates are retained but excluded from physical-market statistics and rankings.

#### Are these official market quotes?

No. The World Bank source describes them as price estimates, including modeled and potentially spatially interpolated values. Verify critical decisions against primary or official sources.

#### Is the code open source?

Yes. AgroLens Africa code is available under the [MIT License](LICENSE). That license does not relicense World Bank data.

#### Is data collection automatically legal because a page is public?

No. Public access does not override copyright, database rights, website terms, or applicable law. Users and Actor operators are responsible for obtaining permission and complying with source terms, rate limits, and relevant laws. Do not collect personal or prohibited data.

#### Where can I get help?

Use the Actor’s **Issues** tab for bugs, source changes, or feature requests. Custom countries, commodities, validation rules, outputs, and integrations can be developed as a custom solution.

# Actor input Schema

## `country` (type: `string`):

Country whose agricultural market data should be collected. Nigeria is currently the only supported value.

## `operation` (type: `string`):

Price lookup returns a direct answer for one food. Full dataset exports observations for the selected commodities.

## `commodity` (type: `string`):

Food whose latest available World Bank estimate you want to look up.

## `market` (type: `string`):

Exact market name from the source data, for example Aba. Leave blank for the Nigeria national estimate or enter a state below.

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

Exact state name. With a market, this disambiguates markets with similar names. Without a market, AgroLens averages the available physical-market estimates in that state.

## `commodities` (type: `array`):

Only used for the full dataset export operation. Select one or more commodities.

## `sources` (type: `array`):

Sources to process. AgroLens Africa currently supports the World Bank Real Time Food Prices dataset; unknown sources are rejected before collection.

## Actor input object example

```json
{
  "country": "Nigeria",
  "operation": "price_lookup",
  "commodity": "maize",
  "commodities": [
    "maize",
    "rice",
    "beans",
    "gari",
    "groundnuts",
    "maize_flour",
    "beef",
    "goat_meat",
    "millet",
    "onions",
    "sorghum",
    "yam"
  ],
  "sources": [
    "world_bank"
  ]
}
```

# Actor output Schema

## `priceLookup` (type: `string`):

Direct query result for the selected food and optional market/state, including price, unit, source date, freshness, and provenance.

## `priceRecords` (type: `string`):

Raw and normalized agricultural price records from supported sources.

## `summaries` (type: `string`):

Commodity-level market statistics, inflation and confidence metrics.

## `comparisons` (type: `string`):

Physical market prices compared with national averages.

## `rankings` (type: `string`):

Markets ranked from cheapest to most expensive for each commodity.

## `runManifest` (type: `string`):

Run status, per-source results, record counts, and warnings for the run.

# 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("graciecreatives/agrolens-africa").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("graciecreatives/agrolens-africa").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 graciecreatives/agrolens-africa --silent --output-dataset

```

## MCP server setup

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

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/e8So4OSMcDuzJ9JK9/builds/NjFIriaQwvIeNehuo/openapi.json
