Retail & Franchise Site Selection Intelligence avatar

Retail & Franchise Site Selection Intelligence

Under maintenance

Pricing

from $0.01 / 1,000 ranked opportunity zones

Go to Apify Store
Retail & Franchise Site Selection Intelligence

Retail & Franchise Site Selection Intelligence

Under maintenance

Find 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

Artashes Arakelyan

Maintained by Community

Actor 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.12 completed successfully, and the full Austin pharmacy acceptance run passed with A / good data 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 Brief worksheet 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_brief section in summary.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

  1. Geocode the requested study area.
  2. Download relevant competitors and demand-generating POIs from OpenStreetMap/Overpass.
  3. Generate regular analysis cells across the study area.
  4. Calculate competitor density, nearest-competitor distance, transport access, and demand-generator density for every cell.
  5. Rank opportunity zones using transparent, configurable weights.
  6. 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_query with 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, or support;
  • 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:

ScenarioVerified resultInterpretation
Austin pharmacy — Apify cloudBuild 1.0.12; data quality A; 24 competitors; 1,476 demand generators; 109 zones; complete rankingBuilt-in category, retries, scoring, Dataset, XLSX, GeoJSON, JSON, and interactive-map outputs work end to end in the production runtime.
Tbilisi coffee shop694 direct competitors; 3,114 demand generators; adjacent shop=coffee source failedThe Actor preserved a useful direct-only result and labels it B / moderate, partial_competitor_ranking, and direct_only_adjacent_failed.
Yerevan custom bookstoreCustom direct, adjacent, and demand definitions completed; demonstration property blocked from GO; too few eligible rent comparables for a medianCustom 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.txt
mkdir -p storage/key_value_stores/default
cp examples/austin_pharmacy_input.json storage/key_value_stores/default/INPUT.json
python -m unittest discover -s tests -v
python -m src

Inspect the first live-run outputs:

python -m json.tool storage/key_value_stores/default/summary.json
python -m json.tool storage/key_value_stores/default/source_data.json
open storage/key_value_stores/default/site_selection_report.xlsx
open storage/key_value_stores/default/interactive_map.html
open 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/default
python3 -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@latest
apify --version
apify login
python -m unittest discover -s tests -v
apify push

After the build succeeds in Apify Console, run at least the Austin example and verify:

  1. the run finishes successfully and the Dataset contains ranked opportunity zones;
  2. summary.json reports the expected status, source coverage, and reliability;
  3. site_selection_report.xlsx, interactive_map.html, source_data.json, and all three GeoJSON layers are present in the default key-value store;
  4. the run log and Data Quality worksheet disclose every failed, cached, or partial source;
  5. 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.