Inspiration
I'm a CS student, and last fall I watched the internship application season hit my friends like a truck. One guy applied to 127 companies. Another spent every weekend writing cover letters. The whole process had turned into a brutal numbers game.
What really got me was seeing someone spend 45 minutes writing a perfect cover letter, only to get auto-rejected three minutes later. The effort didn't match the reality. I kept thinking there had to be a better way.
When I found out about Chrome's built-in AI APIs, something clicked. What if AI could handle the repetitive stuff (reading job descriptions, writing cover letters, filling forms) while students focused on the parts that actually mattered? And what if it all ran locally so your data stayed private? That's where SwiftApply started.
What it does
SwiftApply is a Chrome Extension that helps you apply to jobs faster without sacrificing quality. It uses Gemini Nano to handle the tedious parts of applications.
When you open a LinkedIn job posting:
Job Analysis - It reads the entire description and pulls out the important stuff: key requirements, skills needed, salary info, and a quick summary. Takes about 3 seconds.
Profile Match - It checks your actual qualifications against what the job needs. Not fake percentages, but real criteria like "do you have the right degree?" and "do you have these skills?"
Why You Fit - Shows you specific reasons you're qualified, like "3 years experience in this field" or "currently working as X role."
Cover Letter Generator - Writes a personalized cover letter in 10-30 seconds using your actual work history and skills. No generic templates.
Application Tracker - Saves jobs with one click and lets you track status (Pending, Viewed, Interviewing, Rejected, Accepted). Includes stats and links back to the original postings.
Everything runs on your computer. Your resume and personal info never leave your device, there are no API costs, and it works offline after setup.
How we built it
Tech Stack
- TypeScript and React for the frontend
- WXT Framework for Chrome Extension development
- Tailwind CSS with shadcn/ui components
- Chrome Built-in AI (Gemini Nano) through the Prompt API
Architecture
I split SwiftApply into three main parts:
Content Script - Runs on LinkedIn pages and grabs job data (title, company, description, etc.). Uses MutationObserver to detect when you switch between jobs and debouncing to avoid running constantly.
Background Worker - Handles all the AI stuff. Manages Gemini Nano sessions, processes job analysis and cover letter requests, stores data in chrome.storage.local, and caches your profile for speed.
React Popup - The UI you see with three tabs (Home, Profile, Job Summarizer). Polls for updates to stay in sync and handles all the user interactions.
The AI Integration
Getting the Prompt API to work reliably took some trial and error (an entire week of debugging and reading docs ðŸ˜). I use structured outputs to make sure responses are always parseable:
// Structured output with schema constraints
const schema = {
type: "object",
required: ["cleanSummary", "salary", "skills", "requirements"],
properties: {
cleanSummary: { type: "string" },
salary: { type: "string" },
skills: { type: "array", items: { /* ... */ } },
requirements: { type: "array", items: { type: "string" } }
}
};
const result = await session.prompt(prompt, {responseConstraint: schema});
For cover letters, I give the AI your full work history, skills, and the job details. It generates the creative parts while my code handles formatting and structure. This keeps quality high and output consistent.
Profile Matching System
I didn't want to use AI-generated match scores because they felt misleading. Instead, I built a scoring system based on actual criteria:
$$\text{Profile Score} = 30(\text{Experience}) + 25(\text{Education}) + 15(\text{Location}) + 30\left(\frac{\text{Skills Matched}}{\text{Skills Required}}\right)$$
This gives honest scores users can trust and understand.
Challenges we ran into
1. Gemini Nano Availability
The biggest headache was dealing with model availability. Gemini Nano isn't always ready right away, it needs to download first. Had to add checks everywhere:
const availability = await LanguageModel.availability();
if (availability === 'after-download') {
await LanguageModel.create(); // Triggers download
// Show user a message to wait and try again
}
The API error messages weren't super helpful, so I added a lot of logging to figure out what was going wrong.
2. Getting Reliable AI Output
Early on, the AI would give me responses I couldn't parse. Sometimes valid JSON, sometimes creative prose, sometimes a mix. I learned to be extremely specific in prompts:
- Tell it exactly what format you want
- Use schema constraints every time
- Include examples in the prompt
- Always validate the output before using it
3. LinkedIn's Changing Structure
LinkedIn changes their page structure all the time, which broke my scraper constantly. Had to write flexible selectors that work across different layouts, add retry logic for slow-loading elements, and use MutationObserver carefully to catch changes without destroying performance.
4. State Management Across Extension Parts
Chrome Extensions have three separate contexts (background, content, popup) that can't directly share state. This was confusing at first. I ended up using chrome.runtime.messaging for communication, polling in the popup to stay updated, and caching important data in the background worker.
5. Cover Letter Quality
The first AI-generated cover letters were either too generic or too wordy. Took a lot of prompt tweaking to get them right:
- Specified word counts (300-400 words)
- Told it to avoid clichés
- Gave it examples of what NOT to say
- Passed in actual job descriptions from employment history
The breakthrough was including your full work experience so the AI had real examples to reference instead of making stuff up.
Accomplishments that we're proud of
It Actually Works
I built three different use cases with the Prompt API (job analysis, cover letters, profile matching) and they all work reliably. The code handles errors properly, manages sessions correctly, and performs well. This isn't a demo, it's something people can actually use.
Real Time Savings
SwiftApply genuinely saves time. Normal application: 50 minutes. With SwiftApply: 3 minutes. Over 100 applications that's 78 hours saved. The math is real.
Privacy First
Everything runs on your device. Your resume, work history, and personal info never leave your computer. Given how much data gets leaked these days, this actually matters.
Honest Scoring
I'm really proud that we don't use fake match percentages. The profile scoring system uses transparent criteria you can verify yourself. If it says you're missing a required skill, you are. If it says you have relevant experience, you do.
Quality Output
The cover letters are actually good. I've shown them to career counselors and consistently get 8-9 out of 10 ratings. They're personalized and professional without being obvious AI slop.
What we learned
About AI Development
On-device AI changes everything economically. With no API costs, I could build features that would bankrupt a startup using cloud AI. Students can generate unlimited cover letters without worrying about running out of credits.
Structured outputs are mandatory for production. You can't just cross your fingers and hope the AI gives you what you need. Schema constraints are the difference between a toy and a tool.
Context makes or breaks quality. Generic prompts get generic results. Including user context (skills, experience, location) transforms AI from "kind of helpful" to "actually useful."
About Chrome Extensions
WXT Framework is great. It brings modern web dev practices to extension development. TypeScript, React, hot reload, all of it just works.
Message passing needs careful planning. You can't wing the communication between content scripts, background workers, and popups. Need a clear protocol and state management strategy from the start.
Users care about permissions. I was careful to only request what I absolutely needed. People are rightfully suspicious of extensions these days.
About Building Products
Show your work early. I demoed SwiftApply to classmates constantly. Their feedback shaped major decisions, like ditching the fake skill percentages.
Solve your own problems. I built this because I needed it. That made every design decision obvious because I was the user.
Simple beats clever. I cut several "cool" features because they confused people. The simpler version is better.
What's next for SwiftApply
Short Term (Next 3 Months)
More Chrome AI APIs
- Summarizer API for extremely long job descriptions
- Rewriter API to adjust cover letter tone
- Writer API for generating company-specific interest statements
More Platforms
- Extend to Indeed, Glassdoor, and company career sites
- The architecture is already modular, just need new scrapers
Better Tracking
- Export to CSV/Excel
- Email reminders for follow-ups
- Calendar integration for interviews
Medium Term (6-12 Months)
Translator API
- Apply to international jobs
- Analyze postings in other languages
Interview Prep
- Generate practice questions based on job requirements
- AI role-play for technical and behavioral interviews
Analytics
- Track success rates by company type
- Identify patterns in rejections
- See how you compare to others (anonymized)
Long Term Vision
Mobile App
- Track applications anywhere
- Get notifications on status changes
- Edit profile on the go
University Integration
- Partner with career services
- Provide hiring trend data
- Offer through university subscriptions
Broader Audience
- Full-time jobs, not just internships
- Non-tech fields
- International markets
Community Features
- Share company insights anonymously
- Flag scam postings
- Build a library of successful approaches
The Bigger Picture
SwiftApply started as a time-saving tool, but it could become something more important. Students at less prestigious schools, career changers without traditional backgrounds, and international students all face disadvantages in job searches.
AI assistance that's free and private could help level the playing field a bit. That's what I'm working towards.
Built With
- chrome.scripting
- chrome.storage
- chrome.tabs
- node.js
- promptapi
- shadcn/ui
- tailwindcss
- typescript
- wxt
Log in or sign up for Devpost to join the conversation.