Skip to main content

Overview

Fetch full details for a single job by ID on any supported provider. This is a universal alternative to fetchLinkedInJob() that works across all scrapers. Use this when you have a job ID from search results and need to fetch the complete job details including full description.

Signature

async function fetchJobDetails(
  site: string,
  jobId: string,
  options?: {
    format?: string;
    proxies?: string | string[];
    country?: string;
    credentials?: ProviderCredentials;
    useCreds?: boolean;
  }
): Promise<FlatJobRecord | null>

Parameters

site
string
required
Site name. Supported values:
  • "indeed"
  • "linkedin"
  • "glassdoor"
  • "zip_recruiter"
  • "bayt"
  • "naukri"
  • "bdjobs"
Site names are normalized — "ziprecruiter", "zip_recruiter", and "zip-recruiter" all work.
jobId
string
required
Provider-specific job ID. Format varies by site:
SiteID FormatExample
IndeedJob key"fdde406379455a1e"
LinkedInNumeric ID"4127292817"
GlassdoorListing ID"123456789"
ZipRecruiterListing key"some-listing-key"
BaytJob path"/en/job-title-1234567"
NaukriNumeric ID"123456789"
BDJobsNumeric ID"123456"
options
object
Optional configuration object
options.format
string
default:"markdown"
Description format:
  • "markdown" — converts HTML to Markdown (default)
  • "html" — preserves original HTML
  • "plain" — strips all markup
options.proxies
string | string[]
Proxy server(s) for the request. Accepts formats:
  • "host:port"
  • "user:pass@host:port"
  • "http://host:port"
  • "socks5://host:port"
Multiple proxies rotate round-robin.
options.country
string
default:"usa"
Country code for Indeed/Glassdoor. See Country Support for valid values.
options.credentials
ProviderCredentials
Pre-built credentials object for authenticated scraping
options.useCreds
boolean
default:"false"
Enable authenticated scraping fallback when anonymous access is blocked

Return Value

Returns a FlatJobRecord object (same shape as search results from scrapeJobs()), or null if the job wasn’t found.
id
string
Unique job ID with site prefix (e.g. "in-fdde406379455a1e")
site
string
Source site key (e.g. "indeed", "linkedin")
job_url
string
Canonical job URL on the board
job_url_direct
string
Direct employer/ATS URL (if available)
title
string
Job title
company
string
Company name
location
string
Formatted as "City, State, Country"
date_posted
string
ISO date "YYYY-MM-DD"
job_type
string
Comma-separated employment types (e.g. "fulltime, contract")
description
string
Full job description, formatted according to the format option
salary_source
string
How salary was obtained: "direct_data" or "description"
interval
string
Pay interval: "yearly", "monthly", "weekly", "daily", or "hourly"
min_amount
number
Minimum salary/pay amount
max_amount
number
Maximum salary/pay amount
currency
string
Currency code (e.g. "USD", "EUR")
is_remote
boolean
Whether the job is remote
job_level
string
Seniority level (LinkedIn only)
job_function
string
Job function category (LinkedIn only)
company_industry
string
Industry classification (LinkedIn, Indeed)
Company logo URL (Indeed, Naukri)
company_url
string
Company page on the job board (LinkedIn, Glassdoor)
company_url_direct
string
Company’s own website URL (LinkedIn, Indeed)
…and all other fields from FlatJobRecord

Examples

Fetch Indeed Job

import { fetchJobDetails } from "jobspy-js";

const job = await fetchJobDetails("indeed", "fdde406379455a1e");

if (job) {
  console.log(job.title);
  console.log(job.company);
  console.log(job.description);
}

Fetch LinkedIn Job

const job = await fetchJobDetails("linkedin", "4127292817");

if (job) {
  console.log(job.job_level);        // "mid-senior level"
  console.log(job.company_industry); // "Software Development"
}

Fetch Glassdoor Job with HTML Format

const job = await fetchJobDetails("glassdoor", "123456789", {
  format: "html",
  country: "uk",
});

if (job) {
  // Description will be in HTML format
  console.log(job.description);
}

Fetch ZipRecruiter Job with Proxy

const job = await fetchJobDetails("zip_recruiter", "listing-key", {
  proxies: "user:[email protected]:8080",
});

Fetch from Search Results

import { scrapeJobs, fetchJobDetails } from "jobspy-js";

// First, search for jobs
const searchResult = await scrapeJobs({
  site_name: ["indeed", "linkedin"],
  search_term: "software engineer",
  location: "San Francisco, CA",
  results_wanted: 5,
});

// Then fetch full details for each job
for (const job of searchResult.jobs) {
  // Extract ID from the job record
  const jobId = job.id?.split("-")[1]; // Remove site prefix
  
  if (jobId) {
    const details = await fetchJobDetails(job.site, jobId);
    
    if (details) {
      console.log(`${details.title} - ${details.description?.slice(0, 100)}...`);
    }
  }
}

Handle Missing Jobs

const job = await fetchJobDetails("indeed", "invalid-id");

if (!job) {
  console.log("Job not found or no longer available");
} else {
  console.log(job.title);
}

Fetch Multiple Jobs Concurrently

const jobIds = [
  { site: "indeed", id: "fdde406379455a1e" },
  { site: "linkedin", id: "4127292817" },
  { site: "glassdoor", id: "123456789" },
];

const jobs = await Promise.all(
  jobIds.map(({ site, id }) => fetchJobDetails(site, id))
);

const validJobs = jobs.filter((j) => j !== null);
console.log(`Fetched ${validJobs.length} out of ${jobIds.length} jobs`);

Error Handling

try {
  const job = await fetchJobDetails("indeed", "fdde406379455a1e");
  
  if (!job) {
    console.log("Job not found");
  } else {
    console.log(job.description);
  }
} catch (err) {
  // Throws on invalid site name or network errors
  console.error("Failed to fetch job:", err);
}

Supported Sites

SiteSupportedNotes
IndeedFetches full description from job page
LinkedInUses existing LinkedIn detail fetcher
GlassdoorRequires CSRF token initialization
ZipRecruiterParses full HTML description
BaytScrapes job detail page
NaukriUses REST API
BDJobsExtracts from detail page
GoogleNot supported (returns error)
Google CareersNot supported (returns error)

Notes

  • Returns null if the job doesn’t exist or is no longer available
  • Each call initializes a new scraper session which is automatically closed after fetching
  • For bulk operations, consider using scrapeJobs() with linkedin_fetch_description: true or indeed_fetch_description: true
  • The returned record is flattened (same format as scrapeJobs() results)
  • Job IDs should be taken from search results or provider URLs

Build docs developers (and LLMs) love