AI-Powered Job Application Assistant for South African Job Seekers
Automate your job search with intelligent resume tailoring, ATS optimization, live job listings, career coaching, job market insights and personalised cover letters.
JobApplier AI is an end-to-end job application assistant that reduces the time you spend on repetitive job search tasks from hours to minutes. Upload your CV once, set your preferences, and let AI handle the tailoring, scoring, cover letter writing, and motivation scripting for every application.
Current status: Beta (v3) — core features working, some agents still in active development.
| Feature | Description | Status |
|---|---|---|
| Live Job Listings | Real jobs from Indeed & LinkedIn, filtered to South Africa (last 30 days) | ✅ Working |
| CV Intelligence | Upload PDF/DOCX — AI auto-extracts skills, experience, education, and certifications | ✅ Working |
| ATS Score | Match score + missing keyword recommendations for any job posting | 95% Complete |
| Cover Letter Generator | Tailored cover letters in seconds from your profile + job description | 80% Complete |
| CV Tailoring | AI rewrites your CV to align with a specific job | 90% Complete |
| AI Coach (Neilwe) | Personal AI career coach for job search strategy | 80% Complete |
| Dashboard Analytics | Application tracking and match score history | 40% Complete |
| Application Tracker | Track applications with status, notes, and outcomes | 30% Complete |
| Career Insights | AI-powered personalised career news using live web search — hiring trends, salary insights, interview tips, in-demand skills tailored to your role, seniority, and location | ✅ Working |
| Auto-Application | Automatically submit applications on your behalf | 📋 Planned |
| Mobile App | React Native companion app | 📋 Planned |
'Status' is based on production level readiness. All features work in development phase apart from features with "Planned" status
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Frontend │────▶│ Backend │────▶│ AI Service │
│ React 18 │◀────│ Spring Boot 3 │◀────│ FastAPI │
│ :5173 │ │ :8080 │ │ :8001 │
└─────────────────┘ └────────┬────────┘ └─────────────────┘
│
┌────────────┼────────────┐
│ │ │
┌──────▼──┐ ┌─────▼─────┐ ┌──▼──────┐
│ JobSpy │ │PostgreSQL │ │ Redis │
│ :8002 │ │ :5432 │ │ :6379 │
└─────────┘ └───────────┘ └─────────┘
| Layer | Technology |
|---|---|
| Frontend | React 18 + TypeScript + Vite + Tailwind CSS + shadcn/ui |
| Backend | Spring Boot 3.2 + Java 21 + Spring Security (Argon2) + JPA/Hibernate |
| AI Service | Python 3.11 + FastAPI + OpenAI GPT-5 + Sentence Transformers + PDFMiner |
| Job Scraping | JobSpy (Indeed primary, LinkedIn backup) — South Africa focused |
| Career Insights | OpenAI WebSearchTool — live web search for personalised career news (no third-party news API) |
| Database | PostgreSQL 15 (production) / H2 (development) |
| Cache | Redis 7 (session management) |
| Deployment | Docker Compose |
Future stack include Kubernetes for load management, Google Cloud Platform for cloud deployment, Cloudflare for security
The dashboard's Career Insights panel runs 4 concurrent web searches via OpenAI's WebSearchTool inside a FastAPI agent. Queries are generated from the user's profile:
- Seniority — inferred from experience count + most recent job title (Executive / Senior / Mid-level / Junior / Graduate) ^
- Career field — matched from role title and skills against 12 domains (Software Engineering, Finance, Marketing, etc.)
- Location — from the user's saved preferences
Java's NewsController reads the profile from PostgreSQL, derives the context, and POSTs it to Python. Python runs the 4 searches concurrently (asyncio.gather) so total latency equals the cost of one search, not four. Results are deduplicated by URL and tagged with categories (Hiring Trends, In-Demand Skills, Interview Tips, Salary Insights).^
Job cards on the homepage show AI-computed match scores from the match-score agent, not just keyword overlap from JobSpy. After the job list loads, the top 20 jobs are scored in parallel batches of 5 against the user's live profile. resume_text is synthesised from the profile if no CV has been uploaded — meaning scores update dynamically whenever the user updates their profile.
Neilwe's chat is routed through AgentController.java, which:
- Fetches the user's full profile fresh from PostgreSQL on every message
- Fetches all applications from the DB
- Forwards both to Python with the backend's own base URL
- Gives the Python agent function tools to POST/PUT/DELETE back to Java endpoints for live DB writes (adding applications, updating status, etc.)
This ensures the agent always has current data, not stale frontend state.
- Docker & Docker Compose (recommended)
OR: Java 21 + Maven, Python 3.11, Node.js 18+, OpenAI API key
# 1. Clone
git clone https://github.com/yourusername/jobapplier-ai.git
cd jobapplier-ai
# 2. Set environment variables
cp .env.example .env
# Edit .env with your values:
# OPENAI_API_KEY=sk-your-key-here
# DB_PASSWORD=yourpassword
# JWT_SECRET=yoursecret
# 3. Start all services
docker-compose up --build
# 4. Open the app
open http://localhost:5173| Service | URL |
|---|---|
| Frontend | http://localhost:5173 |
| Backend API | http://localhost:8080 |
| AI Service | http://localhost:8001 |
| JobSpy | http://localhost:8002 |
| Endpoint | Method | Description |
|---|---|---|
/agents/neilwe-chat |
POST | Chat with AI career coach |
/agents/match-score |
POST | Calculate ATS match score |
/agents/tailor-resume |
POST | Tailor resume for job posting |
/agents/generate-cover-letter |
POST | Generate personalised cover letter |
/agents/generate-email |
POST | Generate outreach email |
/agents/extract-cv |
POST | Parse and extract CV data (PDF/DOCX) |
/agents/autofill |
POST | Autofill from CV text |
/agents/extract-job-titles |
POST | Extract job titles from CV |
/agents/career-insights |
POST | Personalised career news via live web search (4 concurrent queries: hiring trends, skills, interview tips, salary) |
/health |
GET | Health check |
| Endpoint | Method | Description |
|---|---|---|
/search |
POST | Search Indeed/LinkedIn jobs |
/search-by-profile |
POST | Search using full user profile |
/health |
GET | Health check |
| Endpoint | Method | Description |
|---|---|---|
/api/auth/register |
POST | Register new user |
/api/auth/login |
POST | Login |
/api/profile/{userId} |
GET/PUT | User profile management |
/api/jobs/recommendations/{userId} |
GET | Personalised job recommendations |
/api/jobs/search-by-profile |
POST | Search jobs by profile |
/api/applications |
POST | Create application |
/api/applications/user/{userId} |
GET | Get user applications |
/api/dashboard/** |
Various | Dashboard analytics |
/api/ai/** |
Various | Proxy to AI service |
/api/agent/chat |
POST | DB-aware AI agent chat (AgentController fetches fresh profile + applications, forwards to Python with function tools for live DB writes) |
/api/agent/context/{userId} |
GET | Debug: view exact context the agent receives |
/api/news/career-resources |
GET | Personalised career insights — fetches profile from DB, calls Python /agents/career-insights |
Copy .env.example and fill in your values:
OPENAI_API_KEY=sk-your-key-here
DB_PASSWORD=your-postgres-password
JWT_SECRET=your-jwt-secret-key
AI_SERVICE_URL=http://ai-service:8001 # default
JOBSPY_SERVICE_URL=http://jobspy-service:8002 # default
CORS_ALLOWED_ORIGINS=http://localhost:5173 # add production domainTo switch to PostgreSQL for local dev, set:
SPRING_PROFILES_ACTIVE=prod
# AI Service
cd ai-service && pytest tests/
# Backend
cd backend && ./mvnw test
# Frontend
cd frontend && npm test
# Manual: Test job scraper
curl -X POST http://localhost:8002/search \
-H "Content-Type: application/json" \
-d '{"keyword":"software engineer","location":"Johannesburg","max_results":5,"days_old":30}'jobapplier-ai/
├── frontend/ # React + TypeScript + Vite
│ ├── src/
│ │ ├── components/ # UI components (shadcn/ui)
│ │ ├── context/ # React context (AppContext)
│ │ ├── lib/ # API clients, utilities
│ │ ├── pages/ # Page components
│ │ └── types/ # TypeScript types
│ └── package.json
| └── Dockerfile
├── backend/ # Spring Boot 3.2
│ ├── src/main/java/
│ │ └── jobapplier/
│ │ ├── api/ # REST controllers
│ │ ├── config/ # Security, App config
│ │ ├── model/ # JPA entities
│ │ ├── repository/ # Spring Data JPA
│ │ └── service/ # Business logic
│ └── pom.xml
| └── Dockerfile
├── ai-service/ # Python FastAPI
│ ├── app.py # Main FastAPI app
│ ├── requirements.txt
│ └── Dockerfile
├── jobspy-service/ # Job scraping service
│ ├── jobspy_service.py
│ ├── requirements.txt
│ └── Dockerfile
├── docs/ # Documentation
│ ├── Architecture-Overview.md
│ ├── Product-Specification.md
│ ├── Testing-Strategy.md
│ └── Design-Decisions.md
├── docker-compose.yml
└── README.md
- User authentication & profiles
- CV upload & AI extraction
- ATS match scoring
- Cover letter generation
- Live job listings (Indeed)
- Resume tailoring
- AI Coach (Neilwe)
- Application tracking
- Dashboard analytics
- Career insights (AI-powered, profile-personalised, live web search)
- LinkedIn job integration
- Email notifications
- Interview preparation module
- Automated application submission
- Mobile app (React Native)
- POPIA compliance audit
- Architecture Overview - System architecture and design (Still in progress)
- Product Specification - Feature requirements and specifications
- Testing Strategy - Testing approach and test plans
- Design Decisions - Key technical decisions and rationale (Still in progress)
- Fork the repository
- Create a feature branch:
git checkout -b feature/your-feature - Commit your changes:
git commit -m 'Add your feature' - Push to the branch:
git push origin feature/your-feature - Open a Pull Request
Please read CONTRIBUTING.md for more details.
MIT License — see LICENSE for details.
- OpenAI for GPT-5 API
- JobSpy for job scraping
- shadcn/ui for UI components
- Spring Boot for backend framework
Made with ❤️ for job seekers everywhere · Join the community









