A production-ready Flask application that discovers and connects businesses using Google Places API. Find leads by location, business type, and website availability with real-time streaming results.
Deployment: Automated CI/CD via GitHub Actions to Hetzner Kubernetes | Last updated: 2025-01-08
Lead Finder is a web-based tool that helps businesses and marketers discover potential leads by searching Google Places API. It provides real-time business discovery with filtering capabilities, map-based location selection, and CSV export functionality.
Live URL: https://leadfinder.janisrael.com
- Location-Based Search - Search businesses by geographic coordinates (latitude, longitude) with configurable radius up to 50km
- Map-Based Location Picker - Interactive Google Maps interface to visually select search locations
- Business Type Filtering - Filter results by multiple business types (restaurant, hotel, store, etc.) with preset suggestions
- Website Availability Filter - Filter businesses based on website presence (has website, no website, or both)
- Real-Time Streaming Results - Results appear in real-time as they are discovered, without page refresh
- CSV Export - Export discovered leads to CSV format for further analysis
- Asynchronous Processing - Background crawling ensures responsive user experience
- SQLite Database - Local storage of discovered businesses with timestamps
- RESTful API - Clean API endpoints for programmatic access
- Health Check Endpoint - Kubernetes-ready health monitoring (
/api/health) - Error Handling - Comprehensive error handling for API failures and rate limits
- Responsive Design - Mobile-friendly interface with neumorphism UI design
- Production Ready - Docker containerization and Kubernetes deployment support
- Python 3.11 - Programming language
- Flask 3.1.1 - Web framework
- SQLite3 - Database for local storage
- Gunicorn - Production WSGI server
- python-dotenv - Environment variable management
- requests - HTTP client for Google Places API
- HTML5/CSS3 - Modern web standards
- JavaScript (ES6+) - Client-side interactivity
- Google Maps JavaScript API - Map integration and location picker
- Roboto Slab Font - Typography
- Docker - Containerization
- Kubernetes (k3s) - Container orchestration
- Nginx Ingress Controller - HTTP/HTTPS routing
- GitHub Actions - CI/CD automation
- Hetzner Cloud - Cloud hosting
- Python 3.11 or higher
- pip (Python package manager)
- Google Places API key (Get one here)
-
Clone the repository:
git clone git@github.com:janisrael/lead-finder.git cd lead-finder -
Create a virtual environment (recommended):
python3 -m venv venv source venv/bin/activate # On Linux/Mac # or venv\Scripts\activate # On Windows
-
Install dependencies:
pip install -r requirements.txt
-
Set up environment variables:
# Create .env file cat > .env << EOF GOOGLE_PLACES_API_KEY=your-api-key-here PORT=6005 DEBUG=True SECRET_KEY=your-secret-key-here EOF
-
Run the application:
python app.py
-
Access the application: Open your browser and navigate to:
http://localhost:6005
| Variable | Description | Default | Required |
|---|---|---|---|
GOOGLE_PLACES_API_KEY |
Google Places API key | None | Yes |
PORT |
Application port | 6005 (local), 5000 (Docker) |
No |
DEBUG |
Flask debug mode | False |
No |
SECRET_KEY |
Flask secret key | None | Yes (production) |
FLASK_ENV |
Flask environment | production |
No |
- Go to Google Cloud Console
- Create a new project or select existing project
- Enable Places API and Maps JavaScript API
- Create credentials (API Key)
- Restrict the API key to Places API and Maps JavaScript API only
- Add the key to your
.envfile or Kubernetes secrets
Note: Google Places API has usage limits and billing. Monitor your usage in Google Cloud Console.
-
Set Location
- Enter coordinates manually (lat,lng format) or
- Click "Pick from Map" to use the interactive map
-
Configure Search Parameters
- Set radius (maximum 50km)
- Select business types (type and press Enter, or choose from suggestions)
- Choose website filter (Yes, No, or Both)
-
Start Search
- Click "Search" button
- Results will stream in real-time to the table below
-
Export Results
- Click "Export as CSV" to download all discovered leads
Health check endpoint for monitoring.
Response:
{
"status": "ok",
"service": "lead-finder",
"version": "1.0"
}Start a new business discovery crawl.
Parameters:
location(string, required) - Location coordinates in "lat,lng" formatradius(number, required) - Search radius in kilometers (max 50)types(string, required) - Comma-separated business typeshas_website(string, optional) - Filter: "yes", "no", or "both" (default: "both")keyword(string, optional) - Additional keyword filter
Example:
curl "http://localhost:6005/crawl?location=50.0405,-110.6766&radius=20&types=restaurant,hotel&has_website=yes"Response:
{
"status": "started"
}Stream discovered businesses (polling endpoint).
Parameters:
last_id(number, optional) - Last received business ID (default: 0)
Example:
curl "http://localhost:6005/stream?last_id=0"Response:
{
"last_id": 150,
"results": [
{
"id": 1,
"name": "Restaurant Name",
"address": "123 Main St",
"phone": "+1-555-1234",
"website": "https://example.com",
"rating": 4.5,
"types": ["restaurant", "food"],
"status": "OPERATIONAL"
}
]
}lead-finder/
├── app.py # Main Flask application
├── requirements.txt # Python dependencies
├── Dockerfile # Docker container definition
├── .dockerignore # Docker build exclusions
├── .env.example # Environment variables template
├── CI_CD_SECRETS.md # CI/CD secrets documentation
├── README.md # This file
├── k8s/ # Kubernetes manifests
│ ├── namespace.yaml # Kubernetes namespace
│ ├── secret.yaml # Kubernetes secrets template
│ ├── deployment.yaml # Application deployment
│ ├── service.yaml # Kubernetes service
│ └── ingress.yaml # Nginx ingress configuration
├── .github/
│ └── workflows/
│ └── deploy.yml # GitHub Actions CI/CD workflow
├── index.html # Main web interface
├── app.js # Frontend JavaScript
├── style.css # Stylesheet (neumorphism design)
├── templates/ # Flask templates (if needed)
└── static/ # Static assets
| Column | Type | Description |
|---|---|---|
id |
INTEGER PRIMARY KEY | Auto-incrementing ID |
name |
TEXT | Business name |
address |
TEXT | Business address |
phone |
TEXT | Formatted phone number |
website |
TEXT | Business website URL |
rating |
REAL | Google rating (0.0-5.0) |
types |
TEXT | Comma-separated business types |
status |
TEXT | Business status (OPERATIONAL, etc.) |
fetched_at |
TIMESTAMP | Timestamp of discovery |
The application is deployed to Hetzner Kubernetes (k3s) using GitHub Actions CI/CD.
Prerequisites:
- Kubernetes cluster (k3s)
- Nginx Ingress Controller
- PersistentVolumeClaim for SQLite database
Deployment Process:
- Push to
mainbranch triggers CI/CD - Tests run automatically
- Docker image is built on Hetzner server
- Kubernetes manifests are applied
- Application is available at
https://leadfinder.janisrael.com
Kubernetes Resources:
- Namespace:
leadfinder - Deployment:
leadfinder-app(2 replicas) - Service:
leadfinder-service - Ingress:
leadfinder-ingress - PersistentVolumeClaim:
leadfinder-pvc(1Gi for SQLite database)
Build image:
docker build -t leadfinder:latest .Run container:
docker run -d \
-p 5000:5000 \
-e GOOGLE_PLACES_API_KEY=your-api-key \
-e SECRET_KEY=your-secret-key \
-v leadfinder-data:/app/data \
leadfinder:latestGitHub Actions automatically deploys to Hetzner Kubernetes on push to main branch.
Required GitHub Secrets:
HETZNER_SSH_PRIVATE_KEY- SSH key for Hetzner server accessHETZNER_HOST- Hetzner server IP addressGOOGLE_PLACES_API_KEY- Google Places API key
See CI_CD_SECRETS.md for detailed setup instructions.
Google Places API has the following limits:
- Nearby Search: 1,000 requests per day (free tier)
- Place Details: 150,000 requests per day (free tier)
- Next Page Token: Requires 2-second delay between requests
The application handles rate limits gracefully and displays appropriate error messages.
Issue: Google Places API key not configured
Solution: Ensure GOOGLE_PLACES_API_KEY is set in environment variables or Kubernetes secrets.
Issue: API Request Denied
Solution: Verify API key restrictions in Google Cloud Console. Ensure Places API and Maps JavaScript API are enabled.
Issue: Database locked
Solution: SQLite may have locking issues with multiple workers. Consider using PostgreSQL for production with high concurrency.
Issue: No results found
Solution: Verify location coordinates are correct. Check radius is not too small. Ensure business types are valid Google Places types.
Issue: Connection timeout
Solution: Check network connectivity. Verify Google Places API is accessible from your server.
- API Key Security: Never commit API keys to version control. Use environment variables or Kubernetes secrets.
- Input Validation: All user inputs are validated before API calls.
- Rate Limiting: Consider implementing rate limiting for production use.
- HTTPS: Always use HTTPS in production (handled by Cloudflare and Nginx Ingress).
- Database Security: SQLite database files should have restricted permissions.
- Email extraction from websites
- Lead scoring and prioritization
- Export to multiple formats (JSON, Excel)
- Saved search profiles
- Batch processing for multiple locations
- Integration with CRM systems
- Advanced filtering (rating, price level, etc.)
- Historical data tracking
- Analytics dashboard
Improvements welcome! Consider adding:
- Additional export formats
- Advanced filtering options
- User authentication
- Search history
- Favorite businesses
- Email notifications
- API documentation (OpenAPI/Swagger)
This tool uses Google Places API, which is subject to Google's Terms of Service. Users are responsible for:
- Complying with Google Places API Terms of Service
- Respecting rate limits and quotas
- Properly attributing Google data
- Ensuring they have permission to use business data
Always:
- Use API keys responsibly
- Monitor API usage and costs
- Respect business privacy
- Comply with applicable data protection regulations
Created By: Jan Francis Israel
Website: https://janisrael.com
HuggingFace: https://huggingface.co/swordfish7412
GitHub: https://github.com/janisrael/lead-finder
Project: Lead Finder
Version: 1.0
Status: Production Ready
Last Updated: January 2025
Note: This application migrated from PHP to Python Flask for better maintainability and deployment flexibility. Legacy PHP files may still exist in the repository but are not actively used.