A German apprenticeship (Ausbildung) job aggregator and market analyzer platform that combines web scraping, AI-powered data enrichment, and real-time market analytics.
AusbildungScout is an intelligent platform designed to help job seekers discover and analyze apprenticeship opportunities across German job platforms. The system automates job data collection, enriches job listings with AI-generated insights, and provides comprehensive market statistics.
- Automated Job Scraping: Intelligent crawling of German job platforms (ausbildung.de)
- AI-Powered Analysis: Uses Google Gemini API to extract structured data from unstructured job postings
- Salary Intelligence: Multi-source salary resolution combining web scraping, tariff agreements, and contextual analysis
- Real-time Market Analytics: Aggregated statistics on market trends, salary ranges, and hiring patterns
- Advanced Filtering: Comprehensive search filters including language requirements, visa sponsorship, and benefits
- Historical Tracking: Preserves job history and vacancy changes over time
- Salary Standardization: Converts various salary formats to standardized monthly EUR amounts
- Tariff Agreement Mapping: Identifies collective bargaining agreements and corresponding salary standards
- Language Level Classification: Categorizes German and English proficiency requirements
- Benefit Normalization: Standardizes diverse benefit descriptions into comparable tags
- Tech Stack Detection: Identifies required programming languages and technologies
- Relocation Support Tracking: Documents housing and mobility assistance programs
βββββββββββββββββββββββββββββββββββββββββββββββββββ
β Frontend (React + Vite) β
β β’ Job listing & filtering β
β β’ Detailed job views β
β β’ Market statistics dashboard β
ββββββββββββββββ¬βββββββββββββββββββββββββββββββββββ
β REST API
ββββββββββββββββΌβββββββββββββββββββββββββββββββββββ
β Backend (Node.js + Express) β
β β’ REST API endpoints β
β β’ MongoDB queries & aggregations β
β β’ Response formatting & pagination β
ββββββββββββββββ¬βββββββββββββββββββββββββββββββββββ
β
ββββββββββββββββΌβββββββββββββββββββββββββββββββββββ
β Data Pipeline (Background Worker) β
β β
β βββββββββββββββββββββββββββββββββββββββββββ β
β β Phase 1: Scraping β β
β β β’ Puppeteer-based web scraping β β
β β β’ Cookie consent handling β β
β β β’ Rate limiting & user-agent rotation β β
β βββββββββββββββββββββββββββββββββββββββββββ β
β β β
β βββββββββββββββββββββββββββββββββββββββββββ β
β β Phase 2: AI Enrichment (Gemini) β β
β β β’ Structured data extraction β β
β β β’ API key rotation & rate limiting β β
β β β’ Fallback HTML parsing β β
β βββββββββββββββββββββββββββββββββββββββββββ β
β β β
β βββββββββββββββββββββββββββββββββββββββββββ β
β β Phase 3: Salary Enrichment β β
β β β’ Tariff mapping β β
β β β’ Description parsing β β
β β β’ Company website lookup (optional) β β
β βββββββββββββββββββββββββββββββββββββββββββ β
β β β
β βββββββββββββββββββββββββββββββββββββββββββ β
β β Phase 4: Database Persistence β β
β β β’ MongoDB storage β β
β β β’ Vacancy change tracking β β
β β β’ Inactive job marking β β
β βββββββββββββββββββββββββββββββββββββββββββ β
ββββββββββββββββ¬βββββββββββββββββββββββββββββββββββ
β
ββββββββββββββββΌβββββββββββββββββββββββββββββββββββ
β MongoDB Database β
β β’ Job documents with full enrichment β
β β’ Historical snapshots β
β β’ Market statistics β
βββββββββββββββββββββββββββββββββββββββββββββββββββ
- Runtime: Node.js 18+
- Framework: Express.js 5.x
- Database: MongoDB 9.x
- Language: TypeScript 5.x
- Web Scraping: Puppeteer 24.x (for browser automation)
- HTML Parsing: Cheerio 1.x
- API: Google GenAI SDK (Gemini 3.1 Flash Lite)
- HTTP Client: Axios 1.x
- Framework: React 19.x
- Build Tool: Vite 7.x
- Language: TypeScript 5.x
- Styling: Tailwind CSS 4.x
- Routing: React Router v7
- UI Notifications: React Toastify 11.x
- HTTP Client: Axios 1.x
- Task Runner: tsx (for TypeScript execution)
- Dev Server: Nodemon (hot reload)
- Linting: ESLint 9.x + TypeScript ESLint
- Package Manager: npm or yarn
- Node.js 18.0.0 or higher
- npm 9.0.0 or higher (or yarn)
- MongoDB 5.0 or higher (cloud via MongoDB Atlas or local installation)
- Google Gemini API key (for AI enrichment)
- Modern web browser (Chrome, Firefox, Safari, Edge)
git clone https://github.com/hemanthkumar-21/AusbildungScout.git
cd AusbildungScoutCreate a .env file in the backend directory:
cd backend
cat > .env << EOF
# Server Configuration
PORT=5000
NODE_ENV=production
# Database
MONGO_URI=mongodb+srv://user:password@cluster.mongodb.net/ausbildungscout
# Frontend Configuration
FRONTEND_URL=http://localhost:3000
# Google Gemini API
GEMINI_API_KEY=your_gemini_api_key_here
# Optional: Multiple API keys (comma-separated) for rate limiting
GEMINI_API_KEYS=key1,key2,key3
# Scraper Configuration
SCRAPER_MIN_DELAY_MS=2000
SCRAPER_MAX_DELAY_MS=5000
# Demo Mode
DEMO_MODE=false
EOFcd backend
# Install dependencies
npm install
# Build TypeScript
npm run buildcd frontend
# Install dependencies
npm install
# Create .env file
cat > .env << EOF
VITE_API_URL=http://localhost:5000/api
EOFTerminal 1 - Backend Server:
cd backend
npm run dev
# Server runs on http://localhost:5000Terminal 2 - Frontend Development Server:
cd frontend
npm run dev
# Frontend runs on http://localhost:5173Terminal 3 - Job Miner (Background Worker):
cd backend
npm run mine
# Runs the job scraping and enrichment pipeline# Backend
cd backend
npm run build
npm start
# Frontend
cd frontend
npm run build
npm previewIf using MongoDB Atlas:
- Create a cluster at https://www.mongodb.com/cloud/atlas
- Create a database user with appropriate permissions
- Add your IP to the network whitelist
- Copy the connection string to
MONGO_URIin.env
For local MongoDB:
# macOS (via Homebrew)
brew services start mongodb-community
# Linux (Ubuntu/Debian)
sudo systemctl start mongod
# Windows
# Start from Services or run: mongodhttp://localhost:5000/api
GET /jobs
Query Parameters:
page(number): Page number for pagination (default: 1)limit(number): Items per page (default: 20)germanLevel(string): Filter by German language requirementvisaNeed(boolean): Filter jobs offering visa sponsorshipminSalary(number): Minimum salary in EUR/monthmaxSalary(number): Maximum salary in EUR/monthstartDate(string): Filter by start dateeducationLevel(string): Required education levelsearch(string): Full-text search termtariffTypes(string): Comma-separated tariff agreementsrelocationSupport(boolean): Filter jobs with relocation supportsortBy(string): Sort field (salary, date, relevance)
Response Example:
{
"success": true,
"data": [
{
"_id": "65abc123...",
"job_title": "Fachinformatiker/in Systemintegration",
"company_name": "TechCorp GmbH",
"locations": [{"city": "Berlin", "state": "Germany"}],
"salary": {"firstYearSalary": 950, "thirdYearSalary": 1200, "average": 1050},
"german_level_requirement": "B2",
"visa_sponsorship": true,
"benefits_tags": ["laptop", "home_office", "health_insurance"]
}
],
"pagination": {
"total": 1250,
"page": 1,
"limit": 20,
"pages": 63
}
}GET /jobs/:id
Response Example:
{
"success": true,
"data": {
"_id": "65abc123...",
"job_title": "Fachinformatiker/in",
"company_name": "TechCorp GmbH",
"description": "Full job description...",
"salary": {...},
"benefits": [...],
"created_at": "2024-01-15T10:30:00Z"
}
}GET /jobs/search?q=keyword
Performs full-text search across job titles, descriptions, and company names.
GET /stats
Response Example:
{
"success": true,
"data": {
"totalJobs": 5420,
"salaryStats": {
"avgSalary": 1050,
"minSalary": 450,
"maxSalary": 2500
},
"languageStats": [
{"_id": "B2", "count": 2150},
{"_id": "B1", "count": 1800}
],
"visaSponsorshipJobs": 1250,
"visaSponsorshipPercentage": "23.07%"
}
}GET /health
Response:
{
"status": "ok",
"timestamp": "2024-01-15T10:30:00Z",
"mode": "production"
}AusbildungScout/
βββ backend/
β βββ src/
β β βββ index.ts # Express server entry point
β β βββ miner.ts # Background worker for job mining
β β βββ db/
β β β βββ index.ts # MongoDB connection setup
β β βββ model/
β β β βββ job.ts # Job schema & model
β β β βββ index.ts # Model exports
β β βββ routes/
β β β βββ jobs.ts # Route definitions
β β β βββ jobs.controller.ts # Route handlers
β β βββ types/
β β β βββ job.types.ts # TypeScript interfaces
β β β βββ index.ts # Type exports
β β βββ utils/
β β β βββ scraper.ts # Puppeteer-based web scraper
β β β βββ gemini-pipeline.ts # AI data enrichment pipeline
β β β βββ salary-resolver.ts # Salary extraction & standardization
β β β βββ tariff-salary-mapper.ts # Tariff agreement mappings
β β β βββ normalizer.ts # Benefit & tech stack normalization
β β β βββ filters.ts # Query filter builders
β β β βββ company-benefits-fetcher.ts # Company website lookup
β β βββ miner_logs/ # Mining operation logs
β βββ package.json
β βββ tsconfig.json
β βββ .env.example
β
βββ frontend/
β βββ src/
β β βββ main.tsx # React entry point
β β βββ App.tsx # Root component
β β βββ api.ts # API client configuration
β β βββ types.ts # Shared TypeScript types
β β βββ index.css # Global styles
β β βββ components/
β β β βββ JobCard.tsx # Job listing card
β β β βββ JobDetails.tsx # Job details view
β β β βββ FilterSidebar.tsx # Filter panel
β β β βββ Pagination.tsx # Pagination component
β β βββ pages/
β β β βββ JobListPage.tsx # Main job listing page
β β β βββ JobDetailsPage.tsx # Individual job detail page
β β βββ assets/ # Static assets
β βββ package.json
β βββ tsconfig.json
β βββ vite.config.ts
β βββ tailwind.config.js
β βββ .env.example
β
βββ README.md
βββ .gitignore
- TypeScript: All code must be properly typed
- Naming: Use camelCase for variables/functions, PascalCase for classes/types
- Error Handling: Always handle errors gracefully with try-catch or error middleware
- Comments: Keep comments minimal; code should be self-documenting
- Imports: Organize imports alphabetically; use absolute paths with @ alias
The Job model includes:
- Core fields: title, company, location, description
- Salary data: first/third year salary, average, source, confidence
- Language requirements: German and English proficiency levels
- Tariff agreement information for salary standards
- Benefits categorization and tech stack
- Relocation support details
- Hiring process information
- Timestamps for tracking updates and inactivity
- RESTful endpoints following standard conventions
- Pagination for large result sets (default 20 items/page)
- Error responses with consistent
successanderrorfields - Query parameter-based filtering for complex searches
- Rate limiting on external API calls (Gemini, company websites)
| Variable | Required | Example |
|---|---|---|
| MONGO_URI | Yes | mongodb+srv://user:pass@cluster.mongodb.net/db |
| GEMINI_API_KEY | Yes | AIzaSy... |
| PORT | No | 5000 |
| FRONTEND_URL | No | http://localhost:3000 |
| SCRAPER_MIN_DELAY_MS | No | 2000 |
| SCRAPER_MAX_DELAY_MS | No | 5000 |
| NODE_ENV | No | development |
| DEMO_MODE | No | false |
- Scraping Phase: Puppeteer crawls ausbildung.de with realistic browsing patterns
- AI Analysis: Gemini API extracts structured data from HTML
- Salary Resolution: Multi-level priority system for salary extraction:
- Priority 1: Scraped salary data
- Priority 2: Description parsing
- Priority 3: Company website lookup
- Priority 4: Tariff standard fallback
- Persistence: Data saved to MongoDB with vacancy tracking
- API key rotation for Gemini (max 15 calls/minute per key)
- Automatic fallback to HTML parsing when rate limited
- Scraper delays (2-5 seconds) mimicking human behavior
- Company website lookups throttled to 1 request/second
- Duplicate detection via original_link
- Vacancy count change tracking
- Historical data preservation (jobs marked inactive, not deleted)
- Confidence scores for enriched data
- Invalid entry filtering
MongoDB Connection Error
Error: connect ECONNREFUSED
Solution: Ensure MongoDB is running and MONGO_URI is correct
Gemini API Rate Limit
Error: 429 Too Many Requests
Solution: Add multiple API keys to GEMINI_API_KEYS (comma-separated)
Scraper Cookie Modal Issues
Cookie consent modal blocking content
Solution: Handled automatically; if persistent, check ausbildung.de website changes
Port Already in Use
Error: listen EADDRINUSE :::5000
Solution: Change PORT environment variable or kill process on port 5000
Contributions are welcome! Please follow these guidelines:
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
This project is licensed under the MIT License - see LICENSE.md for details.
- Google Gemini API for AI-powered data enrichment
- Puppeteer for reliable web automation
- MongoDB for flexible data storage
- React community for excellent libraries
- All job platforms for data sources
For questions or issues:
- Create an issue on GitHub
- Check existing documentation
- Review troubleshooting guides
Last Updated: April 2026
Version: 1.0.0