AgroLens Africa
Pricing
from $1.00 / 1,000 price-records
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
Maintained by CommunityActor 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
- Open the Actor in Apify Console.
- In the Input tab, leave Operation on Look up a food price and choose a food.
- Optionally enter a market or state; leave both blank for nationwide results.
- Select Start and wait for the run to finish.
- 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 .venvsource .venv/bin/activatepip install -r requirements-dev.txtapify validate-schemaruff check .pytest -qAPIFY_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:
{"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:
countrymust beNigeria.commoditiesandsourcesmust 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_SUMMARYrecord to the default key-value store. - The
RUN_SUMMARYrecord 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):
{"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 amodeled_estimateor apublished_market_quote. World Bank RTFP values aremodeled_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 setconfidence_flagtoextremely_low_inflation_confidenceand are counted inextremely_low_inflation_confidence_count.spatially_interpolated.truemeans the price was derived solely through spatial interpolation.falsemeans only that it was not derived solely through spatial interpolation; the value may still be modelled or imputed. AgroLens never labelsfalseas "observed".- Comparison periods.
comparison_periodseparatesprevious_calendar_monthfromprevious_reported_period, and only the former is surfaced asmonthly_percentage_change. - Summary counts.
price_record_countcounts every usable price record and estimate (physical markets plus retained aggregates);physical_market_countcounts physical markets only.average_monthly_percentage_changeaverages consecutive-month comparisons only, andmonthly_comparison_countreports 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 asYYYY-MM(for example2026-08).published_at— the publication/version date of the source dataset (for example2026-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. 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_bankand 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, andinflation_trust_scorewhen setting analytical quality thresholds, remembering thatfalseinterpolation 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.
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.