Resources/GuidesEngineering guide

How to crawl jobs from company career pages

A practical look at collecting job data from company career websites: ATS platforms, custom sites, JavaScript pages and shared recruitment systems.

The real problem

It isn't downloading pages. It's deciding:

  1. where the jobs actually live
  2. which pages belong to the employer you are tracking
  3. which pages represent real vacancies
  4. how to turn different websites into consistent job data
  5. how to keep that data current
On this page
  1. Finding the jobs
  2. 01Start with the companies
  3. 02Where jobs are published
  4. 03Careers page ≠ jobs
  5. 04Shared recruitment platforms
  6. 05Different site types
  7. 06Missing structured metadata
  8. Crawling safely
  9. 07Crawl boundaries
  10. 08Restricted access
  11. 09Identifying job pages
  12. Making it usable
  13. 10Normalization
  14. 11Deduplication
  15. 12Refreshing
  16. 13The full architecture
  17. Next
  18. →Build or use infrastructure?
  19. →How Recuity approaches this
Need the jobs, not the crawler?
See the Job Data API →

At first, the problem sounds simple: find a company's careers page, follow the job links and extract the vacancies.

That works for some websites. But across hundreds or thousands of companies it gets complicated quickly. A careers link may send you to another domain. A shared recruitment platform may contain jobs from many employers. JavaScript may control how vacancies appear. Following the wrong links can take a crawler outside the company you meant to track.

This guide explains the main problems you need to solve.

01

Start with the companies you actually care about

Before thinking about crawling, define the employer universe. For example: UK fintech companies, European cybersecurity startups, your recruitment clients, companies in a particular sector, or employers selected by users of your product.

Your starting point
monvia.comkestrelpay.comferrolend.co.ukaldbridge.capital
Crawling the whole webFind every job anywhere, then work out which employers matter.
Company-firstYou already know the employers. Discover where each publishes vacancies and keep those jobs usable.
02

Find where the company publishes jobs

The main company website is only the starting point. Jobs may sit on a path or subdomain — or the careers link may take you somewhere completely different. That external page may be the correct source for the company's vacancies.

Where jobs can live
company.com/careerscompany.com/jobscareers.company.comjobs.company.com
company.com → careers.external-platform.com/company-nameValid source
Too strictOwn domain only

Misses legitimate jobs hosted on external platforms.

Too looseFollow every link

Leaves the employer entirely within a few hops.

NeededSource relationship

Understand how the company relates to each job source.

03

The careers page may not contain the jobs

Finding a careers page doesn't mean you've found the vacancies. It may contain employer branding, employee stories, benefits, office locations, graduate programmes — and a button leading to another system. The actual listings can be several steps away.

A typical hiring journey
Company websiteNot job data
Careersbranding, benefits, storiesNot job data
Join our teamNot job data
View open rolesNot job data
External recruitment platformNot job data
Job listingsJobs

A reliable system needs to follow this journey without treating every page it encounters as job data.

04

Shared recruitment platforms create another problem

Many recruitment systems host jobs for more than one employer. If a careers link lands on a platform with thousands of vacancies, the crawler can't simply collect everything it sees.

One platform, many employers
shared-platform.com/search4,812 vacancies
Senior Backend EngineerMonviaTracked
Account ExecutiveNorthfield GroupOther
Product DesignerMonviaTracked
Warehouse SupervisorBrightline LogisticsOther
Clinical NurseHollis HealthOther
Trackedbelongs in your datasetOthermust not leak in
Which of these jobs actually belong to the company I am tracking?

Sometimes the relationship is obvious from an employer identifier or URL. Sometimes it isn't. Without getting this right, jobs from unrelated companies leak into your dataset.

The problem becomes more than page discovery. It becomes source attribution.

05

Different companies publish jobs in very different ways

Career websites generally fall into a few broad groups. A single crawling strategy won't work equally well across all of them.

Standard ATS platforms

Established systems with relatively predictable structures, often shared by many companies.

Custom career sites

Navigation, listings and job-detail pages may be unique to the employer.

Jobs inside the main website

Vacancies published through the normal CMS, looking much like ordinary web pages.

JavaScript applications

Initial HTML holds almost no vacancy information; jobs appear after scripts run.

Jobs may also sit behind
ButtonsPaginationFiltersExpandable sections"Load more"Dynamically generated links

Bars indicate how much structure varies from one employer to the next.

06

Don't assume structured metadata will be available

Some job pages expose structured data such as schema.org/JobPosting. When it exists and is accurate, it's very useful. But many employers don't provide it. A valid job page might simply contain:

job page · HTML
<h1>Senior Backend Engineer</h1>

<p>London · Hybrid</p>

<div>
We are looking for an experienced engineer to join our platform team...
</div>
No JobPosting metadataNo public API

A job-data system needs to work with the page itself, not only with structured feeds.

07

Keep the crawler inside the right boundaries

Once a crawler starts following links, it's surprisingly easy for it to wander. Following everything, it could end up traversing a large part of the web.

The hiring-journey boundary
Inside: follow
Careers pages
Job listings
Employer's ATS board
Job-detail pages
Tracked
employer
Boundary
Outside: stop
Social media
News & employee profiles
Partners & universities
Other companies & job portals
But the rule can't beNever leave the company domain.

As we saw in section 2, real jobs may legitimately live elsewhere. The system has to decide which external destinations belong to the hiring journey and which don't.

This matters most at scale: a small navigation mistake repeated across thousands of employers creates a huge amount of irrelevant crawling.

08

Some websites make automated access difficult

Not every career website is equally easy to crawl. Some sites limit automated requests, require JavaScript, use bot-detection systems, rate-limit traffic, or behave differently depending on how they're accessed.

The important point isn't how to get around those systems. It's that reliable job collection is an operational problem: sources behave differently and sometimes become temporarily inaccessible.

MonitoringNotice when a source stops returning jobs.
Fallback behaviourDegrade gracefully rather than drop data.
MaintenanceExpect sources to change over time.
09

Identify the actual job pages

Once you reach a listing area, you still need to work out which links represent vacancies. Destinations vary widely — and sometimes there's no obvious URL until the user clicks. URL patterns alone aren't enough; look at the page content and its relationship to the listing.

From listings to vacancies
Senior Software EngineerLondon View role/jobs/1234
Backend EngineerRemote View role/openings/backend-engineer
Software EngineerLondon View role/careers/software-engineer-london
→
Company A ├── Senior Software Engineer ├── Platform Engineer └── Product Manager Company B ├── Data Scientist └── DevOps Engineer
Everything else is noise:blog posts·benefits pages·event pages·team profiles
10

Turn different job pages into one structure

Discovering the vacancy is only half the work. Employers describe the same information in completely different ways — one in labelled fields, another buried in the description. Your system needs to normalize the result.

Two employers, one record shape
Employer A · labelled fields
Salary
£70,000 – £85,000
Location
London
Working pattern
Hybrid
Contract
Permanent
Employer B · in the description

…join our London team on a permanent, hybrid basis. We offer £70–85k for a senior engineer…

→
Normalized job
{
  "job_title": "Senior Software Engineer",
  "company_name": "Example Company",
  "location": "London",
  "location_type": "Hybrid",
  "job_types": ["Permanent", "FullTime"],
  "seniority_level": "SeniorLevel",
  "min_salary": 70000,
  "max_salary": 85000,
  "salary_currency": "GBP"
}
Useful fields
job titledescriptionsource URLlocationremote / hybrid / onsiteemployment typeseniorityexperienceskillssalaryclosing dateeducationcertificationslanguages

The goal: your application sees one consistent structure, however the employer built its careers website.

11

Avoid duplicates

The same vacancy can be discovered through several routes — and rediscovered through filters, pagination or different navigation paths. If every discovery becomes a new record, users quickly see duplicates.

Three routes, one vacancy
company.com/careers/123jobs.company.com/123external-ats.com/company/123
One job recordSenior Software EngineerExample Company · London
Useful signals
EmployerSource URLExternal job identifierTitleLocationPage content

Deduplication isn't an optional cleanup step. It's part of producing usable job data.

12

Crawling once is not enough

Even a perfect crawl goes stale. Companies continuously publish new roles and remove old ones, so a maintained job index keeps asking:

What has appeared?What is still listed?What is no longer listed?
Between two crawls
Previous crawl120
Current crawl125
Newly discovered+10
No longer found−5
A limitation to keep in mind

If an employer leaves an old vacancy online, a crawler can't know they've stopped hiring for it. The safest source of truth is what the employer currently publishes.

That's why maintaining a job index is an ongoing process, not a one-time scrape.

13

Crawling is only the first layer

Once jobs are discovered and structured, most products still need another layer on top. At this point the architecture looks more like this:

From company websites to your product
··Company websites
01Source discovery
02Crawlingthe crawler
FindingWhere jobs live, and what is in bounds
03Job identification
04Extraction
05Normalization
06Deduplication
StructuringReal vacancies, in one shape, once
07Refreshing
08Search index
ServingCurrent and searchable
··Your product
What products build on the index
Keyword searchSemantic searchLocation & radius searchSalary filteringSeniority filteringRemote / hybrid filteringAlertsJob matchingAnalyticsAI workflows

The crawler itself is only one component.

Build or use infrastructure?

The hard part isn't the first ten companies

For a handful of employers, building this internally may be completely reasonable. The calculation changes as the number and variety of companies grows.

A handful of employersManageable in-house

A few known sources you can check by hand when something breaks.

Each additional employer can mean another mix of
Career-page discoveryExternal domainsShared ATS platformsJavaScript renderingCrawl boundariesAccess restrictionsJob-page identificationExtractionNormalizationDeduplicationRecrawlingSearch indexingMonitoring

It's keeping hundreds or thousands of sources working over time — while you're also trying to build the product customers actually use.

How Recuity approaches this

Build on the jobs, not the crawling infrastructure

Recuity starts with the companies that matter to your product. Bring your own employer list or discover companies in Recuity.

Recuity then handles the job-data layer behind those companies: discovering their career sources, finding the jobs and turning them into a consistent structure.

Job Search APISearch and filter jobs across the companies you track.
Job detailsRetrieve the complete structured job record when you need it.
JSONL exportLoad the dataset into your database, search or data pipeline.
Choose the companies→Recuity maintains the job data→You build on top