Graduating from university is an exciting milestone, but it also marks the beginning of a new challenge: finding your first job. With so many job boards and opportunities out there, it can feel overwhelming to keep track of everything. What if you could automate the process of finding and organizing job postings tailored to your skills and preferences?
In this tutorial, we’ll build a Personalized Job Posting Agent using JavaScript. This project will not only help you find your first job but also teach you valuable programming skills like working with APIs, processing JSON data, and automating tasks. By the end, you’ll have a tool that fetches job postings, filters them based on your criteria, and organizes them into a readable format.
Understanding the Job Posting API
The Job Posting API, accessible through RapidAPI, serves as the foundation for our automated job search. This API aggregates job listings from diverse sources, including company career pages, job boards, and employment agencies, providing a centralized source of job market data.
The API's primary search endpoint, /api/v2/jobs/search
, accepts a wide array of query parameters to refine job searches and returns the paginated result with 10 job postings per page forfrom one day or month. These parameters are described in the API Documentation and include:
dateCreated
: Filters jobs based on their creation day (e.g.,2025-08-20
) or month (e.g.,2025-08
).page
: Specifies the page number for paginated results.countryCode
: Filters jobs based on the country.city
: Filters jobs based on the city.title
: Searches for jobs with specific keywords or phrases in the title.occupation
: Filters jobs based on the occupation or job role.language
: Filters jobs based on the language of the job description.company
: Filters jobs based on the company name.
Furthermore, the API supports for Boolean queries, allowing us to create highly refined searches. We can use "OR" (comma-separated keywords), "AND" (encoded +
sign), and "NOT" (hyphen) operators to filter jobs based on specific criteria within various fields. The API also supports keyphrase searches using double quotes (e.g., city="New York"
), allowing us to target specific locations or requirements. By combining these operators, we can create very specific and targeted job searches.
Defining your Dream Job
Choosing the right location, position, company, and salary for your first job out of university is a crucial decision that requires careful consideration of your priorities and long-term goals. Start by identifying your must-haves versus your nice-to-haves. Location might be a key factor if you desire to stay close to family or prefer a certain lifestyle (urban, suburban, rural), while the specific position might align directly with your area of study or represent a stepping stone toward a desired career path. Evaluating your personal values alongside the job attributes will help narrow down your query.
For this tutorial, let's assume you're looking for a job in the USA where you can work as a JavaScript programmer (includes synonyms such as software developer, engineer, coder, etc.)
countryCode=us
: Only jobs in the USAoccupation=programmer
: Only jobs in Software Developmenttitle=JavaScript,JS,React,Vue,Angular,FullStack
: JavaScript related jobs
Please note that you only have 25 free requests per month, and the API returns 10 jobs per page. Also, without a specific date (dateCreated
), you'll receive results from the day before yesterday, which is useful if you query daily for new jobs.
Coding the Job Posting Agent
Before you start coding, it's essential to have the right tools and accounts set up: ensure Node.js
(version 14 or higher) and npm
(Node Package Manager) are installed, as they're crucial for running JavaScript and managing dependencies; select your preferred text editor
or IDE for a smoother coding experience; possess a basic understanding of JavaScript syntax, asynchronous operations, and promises; create a RapidAPI account
to access the job postings API; subscribe to the Daily International Job Postings API on RapidAPI, making sure to grab your API key
; and have an email account
with SMTP access (e.g., Gmail) ready for sending job notifications.
Setting up the Project
First, create a new directory for your project and initialise a Node.js project:
mkdir job-search-agent
cd job-search-agent
npm init -y
This will create a package.json
file in your project directory. Now, install the necessary npm packages:
npm install node-fetch nodemailer dotenv
node-fetch
: For making HTTP requests to the API.nodemailer
: For sending email notifications.dotenv
: To load environment variables from a.env
file.
Storing the Credentials
Create a file called .env
in your project directory to store your API key and email credentials. Replace the placeholder values with your actual API key and email credentials:
RAPIDAPI_KEY=YOUR_RAPIDAPI_KEY
EMAIL_USER=your_email@gmail.com
EMAIL_PASS=your_password
RECIPIENT_EMAIL=your_email@example.com
Writing the Code
Next, create a JavaScript file named jobSearchAgent.js
with the following code:
// jobSearchAgent.js
require('dotenv').config();
const fetch = require('node-fetch');
const nodemailer = require('nodemailer');
const API_URL = 'https://daily-international-job-postings.p.rapidapi.com/api/v2/jobs/search';
const API_KEY = process.env.RAPIDAPI_KEY;
const EMAIL_USER = process.env.EMAIL_USER;
const EMAIL_PASS = process.env.EMAIL_PASS;
const RECIPIENT_EMAIL = process.env.RECIPIENT_EMAIL;
async function fetchJobPostings(baseParams) {
let allJobs = [];
let page = 1;
let totalCount = Infinity; // Initialized to infinity to enter the loop
const MAX_PAGES = 1; // Maximum number of pages to fetch - be careful to stay below 25 requests per month
while (allJobs.length < totalCount && page <= MAX_PAGES) {
const params = `${baseParams}&page=${page}`;
const url = `${API_URL}?${params}`;
const options = {
method: 'GET',
headers: {
'X-RapidAPI-Key': API_KEY,
'X-RapidAPI-Host': 'daily-international-job-postings.p.rapidapi.com'
}
};
try {
const response = await fetch(url, options);
const data = await response.json();
if (!data) {
console.warn(`No data received for page ${page}.`);
break; // Exit loop if no data
}
if (data.totalCount !== undefined) {
totalCount = data.totalCount;
// console.debug(`Total jobs found: ${totalCount}`);
} else {
console.warn('totalCount is not defined in the response. Pagination might not work correctly.');
totalCount = 0 // prepare breaking while loop
}
if (data.result) {
allJobs = allJobs.concat(data.result); // Accumulate jobs
// console.debug(`Page ${page}: Fetched ${data.result.length} jobs of ${totalCount} in total.`);
page++;
} else {
console.warn(`No jobs array found on page ${page}.`);
break; // Exit if no jobs array
}
} catch (error) {
console.error(`Error fetching jobs for page ${page}:`, error);
break; // Exit loop on error
}
}
console.log(`Fetched ${allJobs.length} jobs of ${totalCount} in total from ${page - 1} pages.`);
return allJobs; // Return all fetched jobs
}
The function fetchJobPostings()
makes a GET request to the Techmap's Job Postings API with the specified parameters and returns the JSON response.
async function parseAndFilterJobs(jsonData, keywords) {
if (!jsonData) {
console.warn("No jobs data received or 'jobs' array missing.");
return [];
}
const lowerCaseKeywords = keywords.map(keyword => keyword.toLowerCase());
const filteredJobs = jsonData.filter(job => {
if (!job.jsonLD) return false;
const description = job.jsonLD.description.toLowerCase();
return lowerCaseKeywords.some(keyword => description.includes(keyword));
});
console.log(`Number of jobs with '${lowerCaseKeywords.join("', '")}' in their description: ${filteredJobs.length}`)
return filteredJobs;
}
The function parseAndFilterJobs()
takes the JSON data and an array of keywords, then filters the job postings to include only those whose description contain at least one of the keywords.
function formatJobPostings(jobPostings) {
if (jobPostings.length === 0) {
return '<p>No matching job postings found.</p>';
}
let html = '<h2>Job Opportunities</h2>\n';
html += '<table border="1">\n';
html += ' <tr> <th>Title</th> <th>Company</th> <th>Location</th> <th>URL</th> </tr>\n';
jobPostings.forEach(job => {
html += ' <tr> ';
html += `<td>${job.title || 'N/A'}</td> `;
html += `<td>${job.company || 'N/A'}</td> `;
html += `<td>${job.city}, ${job.state}</td> `;
html += `<td><a href="${job.jsonLD.url}">Link</a></td> `;
html += '</tr>\n';
});
html += '</table>';
// console.log(html)
return html;
}
The function formatJobPostings()
formats the filtered job postings into an HTML table for easy readability.
async function sendEmail(to, subject, content) {
const transporter = nodemailer.createTransport({
service: 'gmail',
auth: {
user: EMAIL_USER,
pass: EMAIL_PASS
}
});
const mailOptions = {
from: EMAIL_USER,
to: to,
subject: subject,
html: content
};
try {
await transporter.sendMail(mailOptions);
console.log('Email sent successfully!');
} catch (error) {
console.error('Error sending email:', error);
}
}
The function sendEmail()
uses Nodemailer to send an email with the formatted job postings to the specified recipient.
async function main() {
const query = [
"countryCode=us",
"occupation=programmer",
"title=JavaScript,JS,React,Vue,Angular,FullStack"
]
const searchParams = query.join("&"); // Change as needed
const keywords = ['vue', 'react', 'angular']; // Your desired keywords
const jobData = await fetchJobPostings(searchParams);
if (jobData) {
const filteredJobs = jobData //await parseAndFilterJobs(jobData, keywords);
const formattedJobs = formatJobPostings(filteredJobs);
await sendEmail(RECIPIENT_EMAIL, 'New Job Opportunities', formattedJobs);
} else {
console.error('Failed to fetch job postings.');
}
}
main();
Finally, the main()
function orchestrates the entire process: fetching job postings, filtering them based on keywords, formatting the results, and sending an email notification.
Scheduling Automated Searches
To schedule the job search agent to run automatically, you can use the node-cron
library. Install it with:
npm install node-cron
Then, modify your jobSearchAgent.js
file. Please note that only 25 requests per month are free - making 1 call every weekday should be OK.
const cron = require('node-cron');
// ... (previous code)
cron.schedule('0 6 * * 1-5', async () => { // Runs every workday at 6:00 AM
console.log('Running job search...');
await main();
console.log('Job search complete.');
});
console.log('Job search agent scheduled to run every Monday at 9:00 AM.');
This code uses node-cron
to schedule the main
function to run every Monday at 9:00 AM. Adjust the cron schedule as needed to suit your preferences. To run this, simply execute node jobSearchAgent.js
in your terminal.
Outlook
Congratulations! You've built a personalised job search agent that automates a significant part of your job hunt. This project showcases your JavaScript skills and provides a practical tool for landing your first tech job after university. Remember to customise the API queries, filtering criteria, and email settings to match your specific needs.
Good luck with your job search!