Avoiding Ghost Jobs

How I avoid applying to ghost jobs and job spam in general.

What Are Ghost Job Posts?

according to this article 50% of job listings do not result in the candidate being hired. This number is up from 20% in 2018. This is a huge problem if you’re seeking work the traditional way. These types of jobs are often referred to as “ghost jobs”.

How you can avoid ghost jobs

If you’re not a developer or a tech enthuiast, you might not be able to take this approach, but if you are a little familiar with programming concepts you may be able to use this technique.

A little SQL

I’m using PostgreSQL for this example, but the code should work with slight adjustments on other SQL based databases.

  1. Create a table to maintain the a unique list of companies and their statistics related our our job search.
CREATE TABLE public.unique_companies (
    id serial4 NOT NULL,
    companyname text NOT NULL,
    job_count int4 NOT NULL,
    last_updated timestamp DEFAULT CURRENT_TIMESTAMP NULL,
    applications_count int4 DEFAULT 0 NULL,
    CONSTRAINT unique_companies_companyname_key UNIQUE (companyname),
    CONSTRAINT unique_companies_pkey PRIMARY KEY (id)
);

This tables stores a unique id for each company, the company name, the number of jobs posted for the company, the last date the company was updated, and the number of applications sent to the company.

  1. Create a function to update the unique_companies table whenever we insert or update a job listing. The update_unique_companies() function is the core logic for this operation. We’ll create this function using PL/pgSQL (Procedural Language/PostgreSQL Structured Query Language).

Note on PL/pgSQL PL/pgSQL is a procedural programming language that let’s up perform complex operations on our database that we normally can’t do with vanilla SQL.

Here’s what our update_unique_companies() function looks like:

CREATE OR REPLACE FUNCTION public.update_unique_companies()
 RETURNS trigger
 LANGUAGE plpgsql
AS $function$
BEGIN
    INSERT INTO public.unique_companies (companyname, job_count, website, logo, applications_count)
    SELECT 
        NEW.companyname,
        COUNT(*) as job_count,
        SUM(CASE WHEN hasapplied > 0 THEN 1 ELSE 0 END) as applications_count
    FROM public.jobs
    WHERE companyname = NEW.companyname
    GROUP BY companyname
    ON CONFLICT (companyname) DO UPDATE SET
        job_count = EXCLUDED.job_count,
        applications_count = EXCLUDED.applications_count,
        last_updated = CURRENT_TIMESTAMP;
    
    RETURN NEW;
END;
$function$
  1. Query our jobs table and only get jobs that we haven’t applied to in the last 30 days.
SELECT j.id, j.posttitle, j.companyname, j.locality, j.hasapplied, j.link
FROM jobs j 
JOIN unique_companies uc 
    ON j.companyname = uc.companyname
WHERE 
    (uc.applications_count < 1 OR uc.last_updated <= CURRENT_DATE - INTERVAL '30 days')
    AND j.hasapplied = 0
    AND (j.locality ilike '%remote%' or j.locality ilike '%United States%');

Our results might look like this:

id posttitle companyname locality hasapplied link
121543 Integrations Analyst Synthesis Health Remote 0 https://boards.greenhouse.io/synthesishealth/jobs/4467795005
121332 Business Development Sales Manager Asylon Remote 0 https://www.indeed.com/rc/clk?jk=06bc31d14311a341&bb=SJxu1w6ZQyQh0AL1BCX3JDk8wXt612I-QtWfvt6U5oGZOdFJw_sSk4fL5BMudEE78LY28EA8x8xVdzzn7tBOeIPBX79-P-jP49Pe57MuGctB24LP3voCLGSTA1gNjdU0&xkcb=SoC967M36dLzzGywoR0nbzkdCdPP&fccid=d6d3756abbd57ffc&cmp=Asylon&ti=Business+Development+Manager&vjs=3
120760 Developer Support Specialist III Calendly Remote - US 0 https://boards.greenhouse.io/calendly/jobs/7695983002
121413 Remote Clinical Therapist Mend Counseling Services PLLC Remote 0 https://www.indeed.com/rc/clk?jk=7bf53a0306b61fbb&bb=azxD338pC6KvQDWEpFYI86lbcq75ZIRNOCwsv3j8W_K7uZtGaMCEWx1UGiG-jXft2vJZM9__hWAhTWeLqiqygxlPoxrLBHj0UdFpk-x8VF_JkOPgz7NpzSzuwlFhFw1P&xkcb=SoAF67M36dMMltWLnh0UbzkdCdPP&fccid=0355454917ebcb6a&cmp=Mend-counseling-Services-PLLC&ti=Clinical+Therapist&vjs=3
121328 Senior Physician Recruiter U.S. Physiatry Remote 0 https://www.indeed.com/rc/clk?jk=3ae723e15a26309f&bb=SJxu1w6ZQyQh0AL1BCX3JHngneTVYD_4LXomnrdrH3cSeE2OCWFL-IrCoc0u18nO6RIPwEugOvMfNhSazFVNiFgvUUtt2LIfueHusxwPblTTWVzyVBLCkWNTkq1CrnS1&xkcb=SoAh67M36dLzzGywoR0HbzkdCdPP&fccid=36d641cc983ed648&cmp=U.S.-Physiatry-LLC&ti=Physician+Recruiter&vjs=3
121373 Director | AI Architecture (Remote) DISCOVER US, Remote 0 https://boards.greenhouse.io/trace3/jobs/6153472
121329 Sr. Accountant/Financial Analyst Aakar Construction Remote 0 https://www.indeed.com/rc/clk?jk=62751acef06b2224&bb=SJxu1w6ZQyQh0AL1BCX3JJxjYxtRvpwwqbl_Az55ZNZOM3SDEED29_O-YSgZnpfoKHHOfO5V8LQ8suzAD2cvEDZcfKF_uWgcD_NsoXupvJhrqkukGSetRTyK6QT688FR&xkcb=SoCV67M36dLzzGywoR0GbzkdCdPP&fccid=d6b9de014583b1ca&cmp=Aakar-construction&ti=Senior+Financial+Analyst&vjs=3
121368 Nephrologist - Telehealth Cardio & Renal Therapeutics Of Nort Remote 0 https://www.indeed.com/rc/clk?jk=cd85389e38c8ef4b&bb=W_IXRWZom2SNB0TYqtSHDj3PFtbXKGez-bavQmEFnIjXZvp65M_UfA7OHRmo492NcXeTpOwFy5fpsF-MUZ0KwnqVIyFKrf_ZyUOBZQ_3zOFiEOfNoohXqHaQZhHTGwzh&xkcb=SoDE67M36dMN9-AIMz0pbzkdCdPP&fccid=dd616958bd9ddc12&vjs=3
121327 Administrative Assistant/Receptionist Legal Notification Services, Inc Remote 0 https://www.indeed.com/rc/clk?jk=ffd4b60b966dc9d1&bb=SJxu1w6ZQyQh0AL1BCX3JKek8zUZDueLjRuuOdhebuRkbNkwm7SJHpbABqTgZ9dGq3UBJtNYWOQp720zOyLQmgmzDsbvapKvx7As_kuo5X5Xs19nW7ljbOveLSMBVBn8&xkcb=SoB867M36dLzzGywoR0JbzkdCdPP&fccid=08363e61f93be2a0&cmp=Legal-Notification-Services%2C-Inc&ti=Receptionist%2Fadministrative+Assistant&vjs=3
121408 Business Development Executive, DoD Solutions cBEYONData Washington, DC Metro area / Remote 0 https://boards.greenhouse.io/cbeyondata/jobs/5285725004
121335 Assistant Accountant Aakar Construction Remote 0 https://www.indeed.com/rc/clk?jk=b1c8b2b903c05ca8&bb=f253_72Nr4gVry2bhonzpVUTUEt4cIK2Z8ju1MtcgdOd1TWgE_oeSsdfZfaawO7-MM175EZg8EYW8GDOzmOSmzMiZ1XNax23KzkERiC_sPgndqKiVOGA_KKNarYVEh-z&xkcb=SoBV67M36dLy2dARFj0GbzkdCdPP&fccid=d6b9de014583b1ca&cmp=Aakar-construction&ti=Assistant+Accountant&vjs=3

Note on Job Storage The jobs we save are stored in a separate table from the unique_companies table. This allows us to maintain a detailed record of each job listing while keeping track of company-level statistics in the unique_companies table.

Conclusion

Now when we query our jobs table we only see jobs that we have haven’t applied to before, or in the last 30 days. So, even though ghost jobs are still out there, we only have to waste our time on one of them per company.

If you have any questions or experiences with ghost job posts, feel free to share them in the comments below or email me at blakelinkd@gmail.com.