Caution
This project is for educational and learning purposes only. Do not use this tool to spam job applications, violate LinkedIn's Terms of Service, or misrepresent your identity. The author takes no responsibility for any misuse.
An intelligent job application bot that reads your profile, opens LinkedIn, and fills Easy Apply forms automatically β powered by LangGraph + OpenAI GPT-4o-mini + Playwright.
graph LR
You(["π€ You"])
subgraph Portal["Profile Intelligence Portal"]
UI["React UI<br/>:5173"]
API["FastAPI<br/>:8000"]
DB[("SQLite")]
UI <--> API
API <--> DB
end
subgraph Bot["Easy Apply Bot"]
Agent["LangGraph Agent"]
PW["Playwright Browser"]
Agent --> PW
end
GPT["βοΈ OpenAI GPT-4o-mini"]
LI["π LinkedIn"]
You -- "chat to edit profile" --> UI
You -- "β‘ Start Apply button" --> API
API -- "reads profile" --> Agent
API -- "chat LLM" --> GPT
Agent -- "form analysis" --> GPT
GPT -- "fill actions" --> Agent
PW -- "automates" --> LI
Open http://localhost:5173 after starting both servers. The portal has two panels:
Talk to the AI to build and update your profile:
| What to say | What happens |
|---|---|
"My name is Jane, email is jane@example.com" |
Basic info saved to DB |
"I know Python, FastAPI, Docker and AWS" |
Skills list updated |
"Expected CTC 10 LPA, notice period 30 days" |
Job preferences saved |
"Add a certification: AWS Solutions Architect" |
Custom certifications key created |
"Remove the certifications field" |
Key deleted from profile |
"What should I add to get more interviews?" |
AI gives advice (no DB change) |
The AI only writes to the database when you explicitly ask it to update something. You can ask questions freely β they won't modify your profile.
- Live view of everything stored in your profile
- Custom fields (created via chat) appear under Additional Info with a Γ delete button
- Refresh button (β») to reload from DB
- LinkedIn / GitHub / Portfolio quick links if set
- Make sure both servers are running
- Go to http://localhost:5173
- Set up your profile via chat first
- Click β‘ Start Auto Apply in the top-right header
- The button turns red and shows a live PID β the bot is running
- Click β Stop Auto Apply to kill it at any time
The portal polls the bot status every 5 seconds. If the bot crashes, the button resets to idle automatically.
Important
When using Option B without the Profile Portal, the bot has no DB to read from.
Before running, open user_profile.py and fill in EXAMPLE_PROFILE at the top with your own details:
EXAMPLE_PROFILE = {
"name": "Your Name",
"email": "you@example.com",
"skills": ["Python", "FastAPI", ...],
"expected_ctc": 900000,
# ... all other fields
}The bot reads this file at startup and uses it as context for every form field it fills.
# 1. Edit your profile first (only needed once):
# open user_profile.py β edit EXAMPLE_PROFILE at the top
# 2. Login to LinkedIn (one time):
python data/setup_linkedin_browser.py
# 3. Run the bot:
python run_easy_apply.pyThe bot opens Chromium, navigates to LinkedIn, finds Easy Apply jobs, and fills every form using your profile context.
user_profile.py feeds the agent its context. It checks in this order:
1. SQLite DB β profile_portal/backend/profile_portal.db (live, edited via portal)
2. EXAMPLE_PROFILE β user_profile.py (fallback β edit with your details)
If running for the first time without the portal, open user_profile.py and edit EXAMPLE_PROFILE at the top with your own details β name, email, skills, CTC, etc. The bot will use this JSON as context for all form fields.
flowchart TD
A(["βΆ Open Easy Apply"]) --> B["extract_form_html<br/>Read modal step HTML"]
B --> C["analyze_form<br/>GPT-4o-mini β List[FillAction]"]
C --> D["execute_js_actions<br/>Playwright fills each field"]
D --> E{"check_navigation"}
E -- "Next β" --> B
E -- "Review β" --> F["submit_application"]
E -- "Submit β" --> F
E -- "Stuck" --> G{"retry < 3 ?"}
G -- "Yes" --> C
G -- "No" --> H(["β skip_job"])
F --> I(["β
Applied!"])
style A fill:#7c3aed,color:#fff
style F fill:#10b981,color:#fff
style I fill:#10b981,color:#fff
style H fill:#ef4444,color:#fff
style C fill:#4f46e5,color:#fff
sequenceDiagram
actor You
participant UI as React UI
participant API as FastAPI
participant GPT as GPT-4o-mini
participant DB as SQLite
You->>UI: "Add TensorFlow to my skills"
UI->>API: POST /api/chat
API->>DB: load profile + history
API->>GPT: profile JSON + chat history + message
GPT-->>API: ChatReply { response, updates, delete_keys }
API->>DB: update_profile(updates)
API-->>UI: updated profile + reply text
UI->>You: Shows reply, refreshes profile panel
job-apply-agent/
βββ easy_apply_agent.py # LangGraph agent (form fill logic)
βββ run_easy_apply.py # Entry point β Playwright loop
βββ user_profile.py # Reads SQLite profile β JSON for LLM
βββ data/
β βββ setup_linkedin_browser.py # One-time LinkedIn login
βββ profile_portal/
β βββ backend/ # FastAPI + SQLite
β β βββ main.py # Routes (profile, chat, apply control)
β β βββ chat_service.py # LLM chat + Pydantic structured output
β β βββ db.py # Profile & message CRUD
β βββ frontend/ # React + Vite
β βββ src/
β βββ App.jsx
β βββ components/ChatPanel.jsx
β βββ components/ProfilePanel.jsx
β βββ api/client.js
βββ requirements.txt
βββ .env # OPENAI_API_KEY (gitignored)
# 1. Install
git clone https://github.com/akashcodejames/linkedin-easy-apply-agent
cd linkedin-easy-apply-agent
pip install -r requirements.txt
playwright install chromium
# 2. Set your API key in .env
echo "OPENAI_API_KEY=sk-..." > .env
# 3. Login to LinkedIn (one time)
python data/setup_linkedin_browser.py
# 4. Start the portal (two terminals)
cd profile_portal/backend && source venv/bin/activate && uvicorn main:app --reload --port 8000
cd profile_portal/frontend && npm install && npm run dev
# 5. Open http://localhost:5173 β update profile, then click β‘ Start Auto Apply| Tech | |
|---|---|
| Browser | Playwright (Chromium, persistent session) |
| Agent | LangGraph state machine |
| LLM | OpenAI GPT-4o-mini |
| Structured output | Pydantic v2 + with_structured_output() |
| Profile store | SQLite β json.dumps into LLM prompt |
| Backend | FastAPI + uvicorn |
| Frontend | React + Vite |
| Chat memory | ConversationSummaryBuffer |