Introducing AI Job Apply: An Autonomous LLM-Powered Job Application Assistant
Applying for jobs is arguably the most tedious part of a career search. Between copy-pasting the same profile details, writing customized cover letters for every description, and filling out multi-page application forms, candidates spend hours on repetitive data entry.
To solve this, I built AI Job Apply—an open-source, fully dockerized monorepo application designed to automate the entire job application lifecycle. It uses a semantic Retrieval-Augmented Generation (RAG) knowledge base, a zero-dependency local embeddings system, high-speed Cerebras LLM inference, and an iframe-safe Chrome extension to auto-fill forms on LinkedIn, Indeed, and ZipRecruiter.
In this post, we’ll dive into the project’s architecture, detail how the automation engine handles complex forms, and explain how the system auto-learns from manual corrections.
💡 The Core Problems & Our Solutions
- Repetitive Form Answering: Standard auto-fillers use rigid class selectors. When a webpage updates or asks custom questions (e.g., “How many years of experience do you have with React?”), they break. AI Job Apply solves this by storing answers in a PostgreSQL vector database and resolving questions dynamically using an LLM.
- Expensive API Dependencies: Generating embeddings via OpenAI or Cohere for simple text-matching is expensive and requires an internet connection. AI Job Apply uses a deterministic token-hashing embedding service that runs entirely locally with zero external API calls.
- Sandbox Iframe Isolation: Many job boards (particularly LinkedIn) render application forms inside nested
<iframe>elements to isolate the DOM. Traditional browser extensions cannot access these frames without causing security alerts. Our Chrome extension implements recursive iframe traversal and cross-world click injection to bypass this. - Knowledge Gaps & Stalls: Auto-fill bots often get stuck on unanswered fields, resulting in infinite loops or broken submissions. AI Job Apply features Stall Detection and a Knowledge Graph UI; it flags unanswered questions, skips the job, and lets the user resolve the gap. Once resolved, the system retries the job.
🏗️ System Architecture
AI Job Apply is structured as a decoupled monorepo containing a FastAPI backend, a React 19 frontend, and a Chrome browser extension.
graph TD
subgraph Client ["Client Layer (Browser & Extension)"]
FE[React 19 Frontend App]
EXT[Chrome Extension]
inject[inject.js - MAIN World]
extContent[content.js - Content Script]
extBG[background.js - Service Worker]
end
subgraph Backend ["FastAPI Monolith Backend"]
API[FastAPI Router & Endpoints]
Auth[JWT Auth & Google SSO]
RAG[Semantic RAG Cache]
Embed[Deterministic Embedding Service]
LLM[Cerebras Completion Service]
Stripe[Billing & Free Trial Bypass]
end
subgraph Database ["Data Store Layer"]
PG[(PostgreSQL + pgvector)]
Redis[(Redis Cache & Queue)]
end
subgraph External ["External Services"]
CerAPI[Cerebras Cloud - gpt-oss-120b]
Mail[Mailpit SMTP Sandbox]
end
%% Client Interactions
FE -->|Profile, RAG & Kanban Sync| API
EXT -->|Proxy API Requests| extBG
extBG -->|POST /api/solve-screen| API
extContent -->|DOM Parsing| inject
inject -->|Automate Clicks| extContent
%% Backend Interactions
API --> Auth
API --> RAG
API --> LLM
API --> Stripe
RAG -->|Semantic Query <=>| PG
Embed -->|Hash-based BOW Vectorizer| RAG
LLM -->|High-speed completions| CerAPI
Stripe --> Mail
API -->|Write Application State| PG
API -->|Cache & Session Management| Redis
Decoupled Components:
- FastAPI Backend (
/backend): Exposes Swagger-documented endpoints for profile management, vector database operations, and Cerebras integration. - React 19 Frontend (
/frontend): An Obsidian Glassmorphic themed interface. It includes an interactive Kanban board for application tracking and a Knowledge Graph UI to visualize and update RAG database entries. - PostgreSQL &
pgvector: Stores relational data alongside user knowledge base embeddings. A cosine-distance operator (<=>) matches candidate profile answers to dynamically scraped web forms. - Deterministic Token Hashing: Instead of downloading a heavy model (like BERT) or calling an external API, our backend normalizes words, hashes them deterministically using SHA-256 into indices between
0and1535, builds a bag-of-words vector histogram, and normalizes it to a unit vector. - Cerebras Cloud Completions: Connects to Cerebras’ high-speed inference engine using the
gpt-oss-120bmodel. It handles form-solving payloads and generates tailored cover letters dynamically based on candidate resumes and scraped job descriptions.
🤖 The Turbo Apply Automation Flow
The Chrome Extension runs a state machine called Turbo Apply. When active, it manages job searches, parses job descriptions, fills pages, handles verification, and tracks progress.
flowchart TD
Start([Start Turbo Apply Loop]) --> GetJob[Fetch / Select Next Job Card]
GetJob --> Scrape[Scrape Job details & Apply Status]
Scrape --> IsApplied{Already Applied?}
IsApplied -- Yes --> CooldownSkip[Skip Job & Cooldown 3s] --> GetJob
IsApplied -- No --> CheckButton{Detect Easy Apply?}
CheckButton -- External Link / None --> SkipExternal[Mark Skip - Easy Apply Only] --> GetJob
CheckButton -- Easy Apply --> ClickApply[Trigger Click in MAIN world via inject.js]
ClickApply --> OpenModal[Wait for Easy Apply Modal]
OpenModal --> CheckModal{Modal Loaded?}
CheckModal -- Timeout / Closed --> GetJob
CheckModal -- Yes --> AutoFillLoop[Auto-Form Filling Loop]
%% Loop details
AutoFillLoop --> CheckStall{HTML Changed since last check?}
CheckStall -- No (Stalled / Gaps) --> StallHandler[Dismiss Modal & Mark Status as 'needs-knowledge-graph']
StallHandler --> SaveGaps[Flag missing RAG fields in DB]
SaveGaps --> CooldownSkip
CheckStall -- Yes --> ParseFields[Parse Page Fields & Inputs]
ParseFields --> RAGSearch[Fetch Local Heuristics + Backend RAG Context]
RAGSearch --> QueryLLM[Query Cerebras AI Solver /api/solve-screen]
QueryLLM --> FillFields[Populate Inputs, Dropdowns, Radios & Checkboxes]
FillFields --> AutoLearnTrigger[User Clicked 'Continue' / 'Next'?]
AutoLearnTrigger -- Yes --> LearnAPI[Send fields to /api/profiles/active/learn]
LearnAPI --> CheckNext{Next Action?}
AutoLearnTrigger -- No --> CheckNext
CheckNext -- Continue / Next --> ClickNext[Click Next / Review Button]
ClickNext --> Sleep[Sleep 1.5s] --> AutoFillLoop
CheckNext -- Submit Application --> ClickSubmit[Click Submit Button]
ClickSubmit --> SyncDB[Sync Application details to Kanban Board]
SyncDB --> Cool5[Cooldown 5s - 10s] --> GetJob
Key Elements of the Flow:
- Stall Detection: The extension monitors the modal’s HTML state. If it remains unchanged across 6 consecutive checks (~9 seconds of inactivity), the engine assumes it has encountered an unresolved required question or a form lock. Rather than crashing, it closes the modal, marks the job as
needs-knowledge-graph, and logs the missing question. - Auto-Learning Pipeline: Whenever a user manually edits a field and clicks “Continue”, the extension intercepts the values. It sends the question-answer pair to
/api/profiles/active/learn, updating the RAG database dynamically so the AI gets smarter with each application. - Turbo Mode Gap Warning: If the extension detects that the user profile contains unresolved knowledge base questions, it prevents execution and shows a
#kb-warning-alertbanner inside the drawer, protecting the candidate’s application quality.
🧬 Connector Anatomy & Iframe Traversal
A Connector is a platform-specific adapter (e.g., LinkedIn, Indeed, ZipRecruiter) loaded by the extension content script. Here is how a connector accesses and interacts with a page:
graph TD
subgraph PageContext ["Page Execution Context"]
subgraph MainWorld ["MAIN World (Page Context)"]
mainEvent[Event Listeners & React/DOM Event Dispatchers]
end
subgraph ExtWorld ["ISOLATED World (Extension Context)"]
conn[LinkedIn Connector Object]
trav[Iframe-Safe DOM Traversal]
scra[DOM Scraper]
auto[DOM Form Automator]
end
end
subgraph DOM ["Webpage DOM Hierarchy"]
doc[Parent Document]
subgraph Iframe ["Same-Origin Iframe (LinkedIn Preload)"]
ifdoc[Iframe Content Document]
modal[Easy Apply Modal]
inputs[Text Inputs, Selects, Radios]
end
end
%% DOM Traversal
conn --> trav
trav -->|Query selector| doc
trav -->|Recursive traverse contentDocument| ifdoc
ifdoc -->|Locate modal| modal
%% Scraping
conn --> scra
scra -->|Read attributes & text| modal
%% Filling & Clicking
conn --> auto
auto -->|Inject values & trigger 'input' events| inputs
auto -->|Request click dispatch| mainEvent
mainEvent -->|Trigger native DOM click| inputs
Traversal and Injection Details:
- Frame-Aware Selectors: The connector overrides traditional queries with recursive subframe traversal. If an element isn’t found in the parent document, the helper loops through
document.querySelectorAll("iframe")and inspectsiframe.contentDocumentfor same-origin frames. - Contextual execution: Instead of executing commands globally, the automator works within the modal’s
ownerDocument. This ensures all inputs, radios, and selects are triggered relative to the frame they inhabit. - Cross-World Trigger Interceptor: Since Extensions run in an isolated world, dispatching standard
.click()events on React-controlled elements can fail to bubble up. The extension injectsinject.jsinto theMAINpage world, acting as an event proxy to dispatch native user click and keyboard events that bypass React’s virtual DOM traps.
🌐 The Hosted Platform & SaaS Benefits
While the application is fully open-source and easy to self-host, you can access the pre-configured, production-ready version directly at AI Job Apply Hosted Platform.
Why use the hosted version?
- Zero Configuration: Skip setting up Docker, PostgreSQL with pgvector, Redis, and local development environments. Simply sign up and begin applying.
- Built-in LLM Power: No need to register for Cerebras API keys or set up local endpoints. The hosted service handles model configurations and token limits out-of-the-box.
- Cross-Device Sync: Your RAG knowledge base, uploaded resumes, tailored cover letters, and Kanban tracking state sync automatically across all browsers and devices.
- Continuous Updates: Job board structures change constantly. The hosted version is continuously updated to patch DOM selectors and iframe traversal scripts as platforms roll out UI updates.
- Reliable Cloud Storage: Your complete application history, parsed resume details, and custom cover letters are backed up securely in the cloud.
🚀 How to Try it Out
The project is fully dockerized and ready for local development:
1. Build and Start Services
Navigate to the infrastructure folder and start the stack:
cd infra
docker compose up -d --build
This launches:
- React Frontend:
http://localhost:5173 - FastAPI Backend:
http://localhost:8000 - PostgreSQL & pgvector (Port
5432) - Redis (Port
6379) - Mailpit SMTP Sandbox:
http://localhost:18025
2. Seed the Database
Populate the database with test profiles, mock candidates, and pre-configured promocodes:
docker compose exec backend python seed.py
Tip: You can use the mock candidate credentials kkumar.sandeep89@gmail.com with password password.
3. Run Integration Tests
Verify endpoint routing, RAG search, and completion logic by running the test suite:
python3 backend/test_endpoints.py
4. Pro Tier Billing Bypass
To unlock advanced RAG capabilities and unlimited LLM solvers without setting up Stripe keys, enter the promocode FREETRIAL at the pricing checkout screen. This bypasses payment gateways and activates premium privileges directly in the PostgreSQL database.
🛠️ Tech Stack Recap
- Backend: FastAPI, SQLAlchemy 2.0+, Alembic, PostgreSQL, pgvector, Redis, PyPDF, Cerebras SDK.
- Frontend: React 19, Vite, Tailwind CSS v4, Axios, React Router v7.
- Browser Extension: Vanilla Javascript, Chrome Extension Manifest V3, Playwright (for simulation monitors).
Check out the full repository and start automating your search: 👉 job-apply-ai GitHub Repository