AgroLens Africa avatar

AgroLens Africa

Pricing

from $1.00 / 1,000 price-records

Go to Apify Store
AgroLens Africa

AgroLens Africa

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

Pricing

from $1.00 / 1,000 price-records

Rating

0.0

(0)

Developer

Precious Egbewale

Precious Egbewale

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

4 days ago

Last modified

Categories

Share

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:

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

FieldTypeDefaultDescription
operationstringprice_lookupprice_lookup returns a direct food-price answer. full_dataset exports records for multiple commodities.
commoditystringmaizeFood to look up. All 12 supported commodities are selectable. Used in lookup mode.
marketstringnoneOptional exact market name. Leave blank for nationwide results.
statestringnoneOptional state name; used only to narrow a lookup or disambiguate a market.
commoditiesstring arrayall supported commoditiesUsed only when operation is full_dataset.
countrystringNigeriaCountry to process. Nigeria is currently the only supported value.
sourcesstring arrayworld_bankSource 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:

{
"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:

{
"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.

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:

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:

DatasetContents
Price recordsFor 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 summariesPhysical-market averages, medians, extrema, spreads, inflation, confidence, and separate aggregate counts.
Market comparisonsEach physical-market price compared with the source national-average estimate.
Market rankingsPhysical 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:

DatasetExpected records
Default price records888
Market summaries12
Market comparisons804
Market rankings804

Example default-dataset record (abridged):

{
"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

FieldMeaning
commodityStable AgroLens commodity identifier.
commodity_rawCommodity label supplied by the source.
data_kindmodeled_estimate or published_market_quote. World Bank RTFP values are modeled_estimate.
geography_typephysical_market, regional_average, national_average, or market_average when classified.
market, state, countrySource geography labels.
location_idStable source location identifier used to match the same place across periods.
latitude, longitudePoint coordinates for physical_market records. Non-physical aggregates carry null coordinates so they are never plotted as markers.
normalized_price, normalized_unitComparable price and configured unit.
normalization_divisorSource 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_rawPrice estimate and unit as interpreted from the source.
comparison_periodprevious_calendar_month only when the current and previous source dates are exactly consecutive months; otherwise previous_reported_period.
percentage_changeChange from the previous reported period's normalized price. Treat it as monthly only when comparison_period is previous_calendar_month.
annual_inflationSource annual-inflation estimate, when available.
inflation_trust_scoreWorld Bank confidence in the preceding-12-month inflation estimate. It is not a price-accuracy or overall-source score.
spatially_interpolatedTrue 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_atProvenance: normalized ISO date, the original source date string, dataset version, citation, and collection timestamp.
source_period, published_at, freshness_daysFreshness: 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_methodStable identity and lineage. See Stable identity and lineage below.
validation_statusvalid, 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:

CommoditySource packageDivisorOutput unit
maize, rice, gari, beef, goat meat, sorghum1 kg1.0NGN/kg
beans, yam2.5 kg2.5NGN/kg
groundnuts2.2 kg2.2NGN/kg
maize flour2.1 kg2.1NGN/kg
millet2.6 kg2.6NGN/kg
onions0.5 kg0.5NGN/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:

FieldMeaning
record_idSHA-256 of the natural key. Stable when a source value is revised.
deduplication_keyHuman-readable natural key: source_id|country_iso3|source_product_id|location_id|source_date|unit_raw.
content_hashSHA-256 of the value-bearing fields. A new content_hash under the same record_id signals a revised value.
run_idApify run that produced the record.
schema_version, normalization_versionVersions of the emitted record shape and of the normalization rules.
source_id, source_product_id, source_dataset_id, source_dataset_versionSource, product, and dataset identifiers.
source_file_sha256SHA-256 of the exact snapshot file that was read.
country_iso3, retrieval_methodCountry 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:

FieldMeaning
statussuccess, partial_success, or failed.
run_idApify run ID.
schema_versionEmitted record schema version.
countryCountry processed.
requested_commodities, requested_sourcesExactly what the run was asked to produce.
source_resultsPer 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.
countsrecords, summaries, comparisons, and rankings emitted.
warningsHuman-readable warnings, including missing derived datasets.
generated_atISO 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. The World Bank Microdata Library dataset page 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 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:

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 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 LICENSE. That license does not relicense World Bank data.

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.