ConstructionJobs.com Scraper — Full Board avatar

ConstructionJobs.com Scraper — Full Board

Pricing

from $1.70 / 1,000 jobs

Go to Apify Store
ConstructionJobs.com Scraper — Full Board

ConstructionJobs.com Scraper — Full Board

Scrape US construction job postings from ConstructionJobs.com: superintendents, estimators, project managers, foremen and skilled trades. Get title, hiring company, location, job category, employment type, posted and expiry dates and the full description. Export to CSV, Excel or JSON.

Pricing

from $1.70 / 1,000 jobs

Rating

0.0

(0)

Developer

Piquno

Piquno

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

2

Monthly active users

2 days ago

Last modified

Security

Limited permissions

Share

Scrape all live jobs (about 1,550 in November 2026) on ConstructionJobs.com, the main US construction-trades board. Superintendents, estimators, project managers, skilled trades, equipment operators.

HTTP-only, no browser. 29 flat fields per job, ready for CSV or Excel.

$2.00 per 1,000 jobs. No charge for empty rows.


Why this one

Nothing else on the Store covers this site. Four searches for it returned only unrelated boards.

More importantly, the obvious way to scrape it is broken, and quietly so.

The paged listing hides part of the board

/jobs?page=N shows 21 jobs a page, and in November 2026 its pagination ran to page 48 — about 1,000 jobs — while the sitemap listed roughly 1,550.

A page-loop scraper therefore misses the rest of the board — and reports success the whole time. This actor seeds from the sitemap instead and reaches the entire board. The paged listing is used only as a deliberate "recent only" mode for cheap daily watches, or as a fallback, named in the run's status message, when the sitemap yields no job URL.

The site's structured data has not always parsed

Every job page carries a JSON-LD JobPosting block. When checked in August 2026, JSON.parse threw on 100% of them, for two independent reasons:

  • The description field held raw HTML with unescaped double quotes (<div style="margin-left:auto;">), which terminated the JSON string early.
  • PostalAddress carried a trailing comma before its closing brace.

The pages checked in November 2026 parse as they are; this actor still repairs both faults if they return, and recovers the complete record, description included.

Its salary figure is wrong, so we don't use it

The JSON-LD baseSalary has been observed as minValue: 0, maxValue: 0 on postings that display a real range, and elsewhere as min == max with the wrong period. Salary is read from the page's own Salary block instead — scoped to the posting, because the page also renders a "Related Jobs" rail containing other jobs' pay. A body-wide match silently attaches a different job's salary, and there's a regression test for exactly that.


Input

{
"seedMode": "sitemap",
"maxJobs": 500,
"titleContains": "superintendent",
"locationState": "Texas"
}

Defaults: maxJobs is 50, a quick sample that finishes in about 80 seconds. Each job is one request at roughly one per second, so raise it for a full extract and give the run enough time.

ModeReachesUse for
Full board (sitemap)all jobs (about 1,550 in November 2026)complete extracts, market analysis
Recent only (paged)newest ~714cheap daily monitoring

Each job checked is one request, so without filters maxJobs is also your request count.

Monitoring the board

Set incremental: true with seedMode: "recent" and its own stateKey. Only postings not seen before are emitted, tagged changeType: "new".


Output

{
"jobId": "yard-truck-driver-yard-jockey",
"title": "Yard Truck Driver - Yard Jockey",
"normalizedJobTitle": "CDL Driver, Yard Associate",
"jobFunction": "Equipment & Fleet, Warehouse & Distribution",
"employmentType": "FULL_TIME",
"employerName": "48forty Solutions and Relogistics Services",
"employerProfileUrl": "https://constructionjobs.com/employer/48forty-solutions-and-relogistics-services",
"locationCity": "Fontana",
"locationState": "California",
"locationPostalCode": "92335",
"salaryMin": 30,
"salaryMax": 30,
"salaryPeriod": "hourly",
"educationLevel": "Any",
"experienceLevel": "N/A",
"vacancies": 1,
"datePosted": "2026-07-09T17:02:26.000Z",
"validThrough": "2026-09-21T00:00:00.000Z",
"fieldsPopulated": 28
}

employerSlug and employerProfileUrl let you join postings to the site's employer profiles.

Field fill rates

FieldFill
jobId, title, employerName, locationCity, datePosted100% across all test fixtures
jobFunction, educationLevel, experienceLevel, vacancieshigh
salaryMin / salaryMaxa minority — most construction postings withhold pay
description100%, from the JSON-LD (repaired when malformed)

Nothing is inferred. A posting whose own block reads "Salary Not Specified" returns null, not a number borrowed from elsewhere on the page.


Pricing

EventPrice
Job$0.002 ($2.00 / 1,000)
Actor start$0.00005

A row is billed only if its page holds a job posting (a JSON-LD JobPosting block or a title), and it is billed before it is pushed — so you are never charged for a row you did not receive. Rows removed by your filters are never billed.


Limitations — read before you buy

  • One request per job. There is no bulk endpoint; a full-board extract is as many requests as the board has jobs (about 1,550 in November 2026). Budget the time.
  • Salary is often absent. Use withSalaryOnly if you need it, and expect a much smaller result set.
  • Employer contact details are not available. Phone and email sit behind a login wall on employer profiles and are simply not in the HTML. This actor does not promise them.
  • experienceLevel is frequently "N/A" — that is what the employer entered, not a parsing failure.
  • Format risk. The JSON-LD parsed as is on the pages checked in November 2026; if its two old faults return, the repair path takes over automatically. If the structure changes more deeply, a page that holds no job posting is skipped and never billed (30 in a row stop the run); a page that still holds one is saved, and billed, with whatever fields still parse.

Running locally

npm install
npm test

Tests run against captured live pages in test/fixtures/ — no network required. They assert the things that actually break this scraper: that the sitemap index is followed to its job sitemap, that malformed JSON-LD repairs on every fixture that has it, that a trailing-comma fix alone is not sufficient, and that salary is never borrowed from the Related Jobs rail.