AI-powered business opportunity discovery tool for Toronto
GapFinder analyzes real-world data from 8 sources to find unmet needs in any Toronto neighbourhood — then generates actionable business models backed by evidence, not guesswork.
Click anywhere on the Toronto map. GapFinder scans the area and tells you:
- What's missing — business categories with zero or low presence vs. population
- Who lives there — demographics, income, languages, immigration patterns
- What people complain about — community sentiment from Reddit, forums, local blogs
- Seasonal factors — how Toronto's 6-month winter and 86cm annual snowfall shape demand
- Traffic context — whether the area is walkable, drivable, or gridlocked
Then AI analyzes all of this together and proposes 5–8 business opportunities, each with a revenue model, investment estimate, risk assessment, and competitive advantage — grounded in the actual data.
Click on map
│
▼
┌─────────────────────────────────────────┐
│ Parallel Data Collection │
│ │
│ 📍 Google Maps → 20 business types │
│ 👥 Toronto Census → demographics │
│ 💬 Community → Reddit sentiment │
│ 🌡️ Open-Meteo → climate profile │
│ 🚗 TomTom → traffic flow │
└─────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────┐
│ AI Analysis (Gemini 2.5 Flash) │
│ │
│ Real data in → Smart opportunities out │
│ "0 pet stores + 52K population │
│ + 6 months cold = pet supply store │
│ with winter gear line" │
└─────────────────────────────────────────┘
│
▼
Scored & ranked opportunities
with business models
Most "opportunity finder" tools ask AI to guess. GapFinder feeds AI real data and asks it to analyze.
The difference:
| Without data | With data |
|---|---|
| "A gym might do well here" | "3 gyms serve 52K people (1:17,000 ratio vs city avg 1:8,000). 80% university-educated, 73% condo dwellers with no home gym space. Gap confirmed." |
| Source | What It Provides | Cost |
|---|---|---|
| Google Maps Places API | Business count by category, ratings, gap signals (ZERO/LOW/MODERATE/SATURATED) | Free $200/mo credit |
| Toronto Open Data | 2016 Census neighbourhood profiles — population, income, education, immigration, languages | Free, no key |
| Statistics Canada | Census tract demographics via CensusMapper | Free key |
| Community Sentiment | Reddit/forum analysis via Gemini web search grounding — complaints, wishes, needs | Free |
| Open-Meteo | Climate profile — temperature, snowfall, precipitation, seasonal patterns | Free, no key |
| TomTom Traffic API | Real-time traffic flow — speed, congestion level, road accessibility | Free 2,500 req/day |
| Gemini AI | Opportunity analysis with web search grounding | API key required |
| OpenStreetMap + Leaflet | Interactive map with 158 Toronto neighbourhood boundaries | Free |
- Interactive map — click anywhere in Toronto, neighbourhood auto-detected from 158 official boundaries
- Address search — type an address, geocoded via Nominatim
- 6-step streaming analysis — real-time progress as each data source loads
- Neighbourhood profile — population, income, age, languages, immigration %, dwelling types
- Business density scanner — 20 categories scanned, gap signals calculated
- Climate context — seasonal business signals (winter gear, snow removal, indoor venues)
- Traffic context — congestion-aware recommendations (delivery vs. walk-in vs. drive-to)
- Scored opportunities — each rated 1-10 on urgency, market size, competition, difficulty
- Business models — revenue model, investment estimate, time to profit, target audience
- Sort & filter — by score, urgency, ease, market size, or category
- Analysis history — all past analyses saved, searchable, revisitable
- Bookmarks & notes — star opportunities, add personal notes
- Non-Toronto fallback — works globally with Google Maps data (Toronto-specific sources skipped)
Next.js 16 App Router, API Routes, Server Components
React 19 UI layer
TypeScript 5 Type safety
Tailwind CSS 4 Styling (@theme inline, no config file)
SQLite Local database via better-sqlite3
Drizzle ORM Type-safe queries (.returning().all() for mutations)
Gemini 2.5 Flash AI engine with Google Search grounding
Leaflet Interactive map with OpenStreetMap tiles
- Node.js 20+
- pnpm
| Key | Where to get it | Required |
|---|---|---|
GEMINI_API_KEY |
Google AI Studio | Yes |
GOOGLE_MAPS_API_KEY |
Google Cloud Console — enable Places API | Yes |
TOMTOM_API_KEY |
TomTom Developer Portal — enable Traffic API + Traffic Flow API | Yes |
CENSUSMAPPER_API_KEY |
CensusMapper.ca | Optional |
Note: Google Maps API key must have HTTP-referrer restrictions removed (set to "None") for server-side calls.
# Clone
git clone https://github.com/yourusername/gapfinder.git
cd gapfinder
# Install
pnpm install
# Configure
cp .env.local.example .env.local
# Edit .env.local with your API keys
# Run
pnpm devOpen http://localhost:3000 and click anywhere on the Toronto map.
gapfinder/
├── app/
│ ├── page.tsx # Home — map + search + results
│ ├── history/page.tsx # Analysis history
│ ├── analysis/[id]/page.tsx # Saved analysis detail
│ └── api/
│ ├── analyze/route.ts # POST — full analysis pipeline
│ ├── analyses/ # GET/DELETE — history
│ ├── geocode/route.ts # Address → coordinates
│ ├── neighbourhood/route.ts # Coordinates → neighbourhood
│ └── opportunities/ # PATCH — bookmarks & notes
├── components/
│ ├── Map.tsx # Leaflet map with neighbourhood polygons
│ ├── Dashboard.tsx # Stats + visual ranking
│ ├── OpportunityCard.tsx # Expandable opportunity details
│ ├── NeighbourhoodProfile.tsx # Demographics + climate + traffic
│ ├── DataSources.tsx # Data source status cards
│ └── ...
├── lib/
│ ├── data-sources/
│ │ ├── google-maps.ts # Business density scanner
│ │ ├── toronto-opendata.ts # Census neighbourhood profiles
│ │ ├── statscan.ts # Statistics Canada demographics
│ │ ├── community.ts # Reddit/forum sentiment via Gemini
│ │ ├── weather.ts # Open-Meteo climate profile
│ │ ├── traffic.ts # TomTom traffic flow
│ │ └── index.ts # Parallel data aggregator
│ ├── geo/
│ │ ├── neighbourhoods.ts # Point-in-polygon detection
│ │ └── toronto-boundaries.geojson
│ ├── gemini.ts # Gemini API client
│ ├── prompts.ts # AI prompt builder
│ ├── schema.ts # Drizzle ORM schema
│ └── db.ts # Database helpers
└── data/ # SQLite database (gitignored)
Each opportunity is scored 1–10 across four dimensions:
| Dimension | What it measures |
|---|---|
| Urgency | How pressing is the need? (community complaints, zero-presence categories) |
| Market Size | How large is the potential customer base? (population, demographics) |
| Competition | How crowded is the space? (existing business count, saturation level) |
| Difficulty | How hard is it to execute? (capital requirements, regulations, expertise) |
Overall Score = weighted combination factoring in climate suitability and traffic accessibility.
Gap signals from Google Maps:
ZERO(0 businesses) — strongest signalLOW(1–3) — significant gapMODERATE(4–8) — some roomSATURATED(9+) — highly competitive
- Phase 1 — MVP: AI analysis with web search
- Phase 2A — Real data: Google Maps, demographics, community sentiment
- Phase 2B — Context: weather + traffic integration
- Phase 2C — PDF/JSON export
- Phase 2C — Multi-location comparison
- Phase 2C — Dark mode
- Phase 3 — Google Trends integration (waiting for public API)
- Phase 3 — PWA support
- Future — Iran market version
Built in one day using Claude Code — from idea to production in 15 prompts.
Private — Personal Use Only
