Retail & Franchise Site Selection Intelligence
Under maintenancePricing
from $0.01 / 1,000 ranked opportunity zones
Retail & Franchise Site Selection Intelligence
Under maintenanceFind and rank underserved expansion zones for any retail or service business. Analyze direct and adjacent competitors, demand generators, transport access, and amenities. Export an auditable Excel report, interactive map, Dataset, JSON, and QGIS-ready GeoJSON layers.
Pricing
from $0.01 / 1,000 ranked opportunity zones
Rating
0.0
(0)
Developer
Artashes Arakelyan
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
10 days ago
Last modified
Categories
Share
Release status: v6.7 is the cloud-validated v1.0 production release. It combines the Phase 3 analysis engine with the Phase 4 client-facing outputs. Apify build
1.0.12completed successfully, and the full Austin pharmacy acceptance run passed withA / gooddata quality.
v6.7 transparent market-comparable screening
v6.7 adds configurable rules for the market-rent benchmark. The default search radius is 1,500 m around the top zone, eligible area is 50–250 sqm, and a listing must be verified, active, non-demonstration, first-floor, street-accessible, and have an address, source URL, and verification date. At least three eligible listings are required. Every rejected listing remains in the Excel report with explicit exclusion reasons, preventing large buildings, distant premises, stale listings, and incomplete records from silently distorting the median.
The production quality gate also prevents A / good from being reported when the adjacent-
competitor source fails. A direct-only ranking is capped at B / moderate and remains labelled
partial_competitor_ranking / direct_only_adjacent_failed.
v6.6 production decision guards
v6.6 prevents demonstration, inactive, or unverified listings from receiving a
GO recommendation. A market rent median is produced only when at least three
verified, active, non-demonstration first-floor street-entry comparables are
available. If the adjacent-competitor query fails while direct competitors are
available, the analysis is labeled partial_competitor_ranking and reports
direct_only_adjacent_failed competition coverage.
v6.5 property-economics extension
Optional candidate_properties can now be evaluated against the highest-ranked
opportunity zone. When properties are supplied, business_economics must include
a positive projected_monthly_sales_amd. The report calculates rent per square
metre, distance to the top zone, total occupancy cost, maximum affordable rent,
required sales, safety margin, a property-fit score, and a GO, NEGOTIATE, or
REJECT screening recommendation.
The Excel report adds Candidate Properties and Property Economics sheets.
Listing details and financial assumptions remain user-supplied and must be
verified before signing a lease.
v6.4 — Resilient interactive-map delivery
v6.4 replaces the legacy OSM subdomain template with the policy-compliant
https://tile.openstreetmap.org/{z}/{x}/{y}.png URL. The map now detects file://
viewing, explains how to serve the report through localhost, and stops repeated tile requests after
multiple failures. Opportunity zones, competitors, demand generators, popups, and layer controls
remain usable on a neutral background when a basemap is unavailable.
Advanced users can set basemap_url and basemap_attribution in Actor input. The URL must be an
absolute HTTPS raster-tile template containing {z}, {x}, and {y}. Users remain responsible
for the selected provider's licence, attribution, usage limits, and credentials.
v6.3 — Conservative competitor deduplication
v6.3 removes likely duplicate competitor POIs before spatial scoring. Two records are merged only
when they have the same normalized name, the same competitor type (direct or adjacent), and
are no more than 25 metres apart. Unnamed records, differently classified records, and same-name
outlets farther apart are retained.
The record with the richer OSM tag set is retained. JSON and XLSX outputs report the raw count, final unique count, number removed, threshold, retained OSM reference, removed references, and maximum cluster distance. The successful Yerevan bookstore dataset used during verification was reduced conservatively from 64 to 63 competitors by merging one 5.2-metre duplicate pair.
v6.2 — Persistent source-cache protection
v6.2 adds a persistent, input-specific source cache. Every successful direct-competitor,
adjacent-competitor, and demand-generator group is stored in the named
retail-site-source-cache key-value store. If a later live Overpass request still fails after
its normal retry, only that failed group is restored from its last successful snapshot.
Cached recovery is never hidden: affected sources are labelled cached_after_failure, their
snapshot timestamp and age are included in the audit trail, and an otherwise A-grade result is
capped at B / moderate reliability. A first successful run is required to seed the cache for a
specific location, radius, business category, and tag configuration.
v6.1 — GeoJSON compatibility fix
v6.1 explicitly serializes every GeoJSON deliverable as standards-compliant JSON text before it
is written with the application/geo+json content type. This prevents Python dictionary syntax
from appearing in downloaded .geojson files and makes them directly readable by QGIS, web maps,
and standard JSON parsers.
v6.0 — Phase 4 productization
v6.0 keeps the complete Phase 3 analysis engine and adds the first client-facing product layer:
- a
Decision Briefworksheet with status, data-quality limits, ranked zones, strengths, and recommended validation actions; - an interactive Leaflet HTML map with opportunity zones, direct competitors, adjacent competitors, and an optional demand-generator layer;
- a machine-readable
decision_briefsection insummary.json; - expanded Dataset and Actor output schemas for commercial review;
- publication material and a release checklist for Apify Store deployment.
v5.6 analysis-engine change
Direct and adjacent competitors are now queried independently on every run. The existing
custom_fallback_tags input name is retained for backward compatibility, but its non-primary
tags now define adjacent competitors rather than an emergency fallback query.
An Apify Actor that discovers and ranks expansion opportunity zones for a selected business category in a city. The user provides a place and a business category; the Actor collects OpenStreetMap data, builds analysis cells, measures competition and accessibility, and returns decision-ready tables and geospatial files.
Product promise
Enter a city and a configurable retail or service business category. Get ranked underserved expansion zones with an explicit data-quality grade.
This project replaces the earlier manual-input scoring prototype. Users are not expected to collect facility coordinates or invent 0–100 site scores before running the Actor.
Primary users
- retail and franchise expansion teams;
- commercial real-estate analysts;
- pharmacy, clinic, grocery, gym, café, bank, and EV-charging operators;
- location-intelligence and GIS consultants.
MVP workflow
- Geocode the requested study area.
- Download relevant competitors and demand-generating POIs from OpenStreetMap/Overpass.
- Generate regular analysis cells across the study area.
- Calculate competitor density, nearest-competitor distance, transport access, and demand-generator density for every cell.
- Rank opportunity zones using transparent, configurable weights.
- Export a Dataset, XLSX report, GeoJSON layers, and summary JSON.
Minimal input
{"location_query": "Austin, Texas, USA","business_category": "pharmacy","analysis_mode": "discover_opportunity_zones"}
See examples/austin_pharmacy_input.json for the complete MVP example.
Built-in presets currently cover pharmacy, supermarket, clinic, coffee shop, gym, EV charging,
bank, restaurant, bakery, hotel, and fuel station. Select custom to supply primary competitor
tags, broader adjacent-competitor tags, and business-specific demand-generator groups without changing code.
Example custom bookstore configuration:
{"location_query": "Yerevan, Armenia","business_category": "custom","custom_osm_tags": [{"key": "shop", "value": "books"}],"custom_fallback_tags": [{"key": "shop", "value": "books"},{"key": "shop", "value": "stationery"}],"adjacent_competitor_weight": 0.4,"custom_demand_generators": {"universities": {"role": "primary_demand","tags": [{"key": "amenity", "value": "university"}]},"schools": {"role": "primary_demand","tags": [{"key": "amenity", "value": "school"}]},"public_transport": {"role": "transport","tags": [{"key": "highway", "value": "bus_stop"}]},"libraries": {"role": "support","tags": [{"key": "amenity", "value": "library"}]},"cafes": {"role": "support","tags": [{"key": "amenity", "value": "cafe"}]}}}
MVP outputs
- Default Dataset: ranked opportunity zones.
opportunity_zones.geojson: all scored grid cells.competitors.geojson: discovered competing businesses.demand_generators.geojson: supporting POIs used in the score.site_selection_report.xlsx: recommendations, assumptions, and source-quality notes.interactive_map.html: client-facing map with layer controls and zone popups.summary.json: run metadata and key findings.
Important interpretation
The Actor produces a screening analysis, not a final lease or investment recommendation. It uses OpenStreetMap availability as a market and accessibility proxy. Optional candidate-property and rent-economics inputs are supported, but those records are user-supplied and must be verified. Census, traffic, network travel time, zoning, parcel constraints, and field inspection remain external validation inputs.
Development phases
See TECHNICAL_SPEC.md and IMPLEMENTATION_PLAN.md.
Current implementation status
Phases 3 and 4 are implemented. The current runnable build:
- validates the Actor input and score weights;
- geocodes
location_querywith Nominatim; - resolves the selected category preset;
- downloads competitors and demand-generating POIs through Overpass;
- requests each demand-generator group separately so one timeout does not erase successful results from the other groups;
- preserves partial demand data and reports success or failure for every group;
- waits after the first pass and retries only failed demand groups, without re-querying or duplicating groups that already succeeded;
- retries a failed competitor source after demand collection without repeating successful demand queries;
- persists successful source groups by location and configuration, then restores only failed groups from the latest matching snapshot on later runs;
- exposes
cached_after_failure, cache age, and cache-restored group counts in JSON and XLSX, while capping cached results at B / moderate reliability; - blocks final recommendations when competitor collection still fails: the missing competition
weight stays unallocated and the remaining result is labelled
demand_only_diagnostic; - retries rate-limited requests across three public endpoints with backoff;
- applies category-specific primary and adjacent competitor definitions for every built-in or custom business configuration;
- queries direct and adjacent competitor definitions independently on every run, removes duplicate OSM objects, and always gives the direct classification precedence;
- removes likely duplicate competitor POIs using conservative same-name, same-type, 25-metre matching and publishes a complete merge audit;
- classifies primary-tag matches as direct competitors and broader-tag-only matches as adjacent competitors; adjacent businesses use a configurable relative weight (default 0.4);
- scores competition density with weighted competitors while nearest-distance scoring uses the nearest direct competitor;
- records the endpoint, query strategy, and exact competitor tags in the XLSX Data Quality sheet;
- grades source coverage from A to D and labels sparse-data rankings as low-confidence;
- assigns every demand-generator group a scoring role:
primary_demand,transport, orsupport; - marks flat scoring components inactive and redistributes their requested weight across active components instead of assigning an artificial neutral score;
- writes a diagnostic Excel report even when a source remains unavailable;
- generates a regular grid fully contained by the circular study radius;
- calculates competitor counts at each configured catchment radius and nearest-competitor distance;
- calculates demand, public-transport, and supporting-amenity counts;
- normalizes components within the study area and ranks zones with traceable weighted scores;
- writes ranked zones to the default Dataset;
- writes
source_data.json,summary.json,site_selection_report.xlsx, and three GeoJSON layers to the key-value store. - writes a client-facing interactive HTML map and structured decision brief while preserving the full source and methodology audit trail.
Validated acceptance scenarios
The release has passed 53 automated tests, three materially different acceptance scenarios, and a complete Apify cloud-runtime acceptance run:
| Scenario | Verified result | Interpretation |
|---|---|---|
| Austin pharmacy — Apify cloud | Build 1.0.12; data quality A; 24 competitors; 1,476 demand generators; 109 zones; complete ranking | Built-in category, retries, scoring, Dataset, XLSX, GeoJSON, JSON, and interactive-map outputs work end to end in the production runtime. |
| Tbilisi coffee shop | 694 direct competitors; 3,114 demand generators; adjacent shop=coffee source failed | The Actor preserved a useful direct-only result and labels it B / moderate, partial_competitor_ranking, and direct_only_adjacent_failed. |
| Yerevan custom bookstore | Custom direct, adjacent, and demand definitions completed; demonstration property blocked from GO; too few eligible rent comparables for a median | Custom business logic and the production property-economics guards behave conservatively. |
These runs validate the software paths, not the commercial truth of every output. OSM coverage, public Overpass availability, address quality, and user-supplied property data still vary by market.
The result is a screening-level recommendation. It is not a final lease or investment decision: demographics, parcels, zoning, rent, traffic, travel time, and field verification remain external validation steps.
Local setup
python -m pip install -r requirements.txtmkdir -p storage/key_value_stores/defaultcp examples/austin_pharmacy_input.json storage/key_value_stores/default/INPUT.jsonpython -m unittest discover -s tests -vpython -m src
Inspect the first live-run outputs:
python -m json.tool storage/key_value_stores/default/summary.jsonpython -m json.tool storage/key_value_stores/default/source_data.jsonopen storage/key_value_stores/default/site_selection_report.xlsxopen storage/key_value_stores/default/interactive_map.htmlopen storage/key_value_stores/default/opportunity_zones.geojson
For reliable local basemap loading, serve the output directory over HTTP instead of opening the
map through a file:// URL:
cd storage/key_value_stores/defaultpython3 -m http.server 8000
Then open http://localhost:8000/interactive_map.html. Stop the server with Control+C.
Only one INPUT*.json file may exist inside the default key-value-store directory. Keep backups
outside storage/key_value_stores/default.
Deploy to Apify
Apify Actor versions use the MAJOR.MINOR format. Both numbers must be between 0 and 99. Do not
use a three-part value such as 1.0.0; the CLI rejects it. The release configuration should contain:
{"version": "1.0"}
Every field in .actor/input_schema.json, including properties nested inside arrays and objects,
must include the metadata required by Apify. This release provides titles and descriptions for the
analysis mode, candidate sites, custom OSM tags, candidate properties, and comparable-rule fields.
Missing field metadata causes the Apify upload to succeed but the cloud build to fail during schema
validation. The schema also uses Apify-compatible minimum constraints instead of the unsupported
JSON Schema exclusiveMinimum keyword. The output_formats array uses the select editor with
items.enum, which renders Apify's supported predefined multiselect control.
The Actor output schema uses actorOutputSchemaVersion: 1, a schema title and description, and a
properties object containing the generated Dataset and key-value-store links.
The Dataset schema keeps actorSpecification: 1, defines fields as a JSON Schema object with
type: object and nested properties, and exposes the ranked-zone overview table.
From the project root, update the CLI, confirm authentication, rerun the tests, and push:
npm install -g apify-cli@latestapify --versionapify loginpython -m unittest discover -s tests -vapify push
After the build succeeds in Apify Console, run at least the Austin example and verify:
- the run finishes successfully and the Dataset contains ranked opportunity zones;
summary.jsonreports the expected status, source coverage, and reliability;site_selection_report.xlsx,interactive_map.html,source_data.json, and all three GeoJSON layers are present in the default key-value store;- the run log and Data Quality worksheet disclose every failed, cached, or partial source;
- the interactive map opens through its Apify output URL and its layer controls work.
The Austin cloud acceptance run completed this publication gate. Store review remains subject to Apify's listing, pricing, account, and marketplace approval requirements.