Handshake Jobs Scraper — Public Job Listings
Pricing
from $3.00 / 1,000 validated handshake job returneds
Handshake Jobs Scraper — Public Job Listings
Extract anonymous public Handshake job listings from public search or direct job URLs. Returns normalized titles, employers, locations, compensation, dates, descriptions, and canonical application URLs for early-career hiring research.
Pricing
from $3.00 / 1,000 validated handshake job returneds
Rating
0.0
(0)
Developer
Khadin Akbar
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
15 days ago
Last modified
Categories
Share
What is Handshake Jobs Scraper — Public Job Listings?
Handshake Jobs Scraper extracts anonymous public Handshake job listings into a dataset, one normalized row per unique job, from public Handshake search pages or direct public job URLs. Each row returns the job ID, title, employer name, website and logo, location, remote flag, employment type, industry, published salary range, posting and expiry dates, the full description in HTML and plain text, and the canonical job and apply URLs. Early-career recruiters, university career teams, labor-market researchers and AI agents use it to track internships and graduate roles with no Handshake account.
Why recruiters use this Handshake scraper
- Two ways in. Discover jobs from public
joinhandshake.com/find-jobs/...pages, or enrich knownapp.joinhandshake.com/public/jobs/{id}URLs. - Full job detail. Every row follows the job page for
descriptionHtml,descriptionText,industry,employmentTypeand dates. - Published pay, cleanly split.
salaryMin,salaryMax,salaryCurrencyandsalaryUnitcome straight from the listing. - Local filters.
titleIncludes,companyIncludes,remoteOnlyandminSalarynarrow the jobs found on your URLs. - Expiry awareness.
validThroughshows when each listing closes. - Provenance on every row.
sourceSearchUrlandscrapedAtrecord where and when each job was collected.
Best fit and related Actors
Best fit for public early-career and graduate roles on Handshake: internship inventories by category, entry-level salary research, or monitoring a Handshake search page for new postings. It works with anonymous public pages, so it covers the jobs Handshake shows without login.
- For the same roles on LinkedIn, use LinkedIn Jobs Scraper alongside this Actor and compare employers.
- When your targets are startups, continue with Wellfound Jobs Scraper for startup roles and company details.
Example workflow: from a Handshake category to an internship tracker
A university career-services team starts with the public page https://joinhandshake.com/find-jobs/remote/software-engineering/, maxItems set to 50 and titleIncludes set to intern. The dataset returns each internship's title, companyName, salaryMin, salaryMax, postedAt, validThrough and applyUrl. They sort by validThrough to flag roles closing soon, export the table into the student newsletter, then schedule the same input weekly and compare jobId values to highlight only new postings.
How to scrape Handshake jobs
- Paste public Handshake search pages in Public search URLs, or known job pages in Direct public job URLs.
- Set Maximum jobs, and optionally Job title contains, Employer name contains, Remote jobs only and Minimum published salary.
- Click Start, then export the dataset as JSON, CSV or Excel, or read it through the API.
Input
Discover public remote engineering jobs:
{"searchUrls": ["https://joinhandshake.com/find-jobs/remote/software-engineering/"],"maxItems": 25,"includeDescriptions": true,"remoteOnly": true,"titleIncludes": "engineer"}
Look up known public jobs:
{"jobUrls": ["https://app.joinhandshake.com/public/jobs/11209339","https://app.joinhandshake.com/public/jobs/11209137"],"maxItems": 2,"includeDescriptions": true}
| Field | Type | What it controls |
|---|---|---|
searchUrls | array | Public joinhandshake.com/find-jobs/... pages to discover jobs from. |
jobUrls | array | Direct app.joinhandshake.com/public/jobs/{id} URLs. |
maxItems | integer | Unique jobs saved across all URLs, up to 100. Default 10. |
includeDescriptions | boolean | Include the description in HTML and plain text. Default true. |
titleIncludes | string | Keep jobs whose title contains this phrase. |
companyIncludes | string | Keep jobs whose employer name contains this phrase. |
remoteOnly | boolean | Keep jobs explicitly marked remote. Default false. |
minSalary | number | Keep jobs whose published maximum pay is at least this amount. |
Output
One dataset row per unique public Handshake job.
| Field | What it contains |
|---|---|
jobId, title | Handshake job ID and title |
companyName, companyUrl, companyLogoUrl | Employer name, website and logo |
location, isRemote | Published location and remote flag |
employmentType, industry | For example FULL_TIME and Internet & Software |
salaryMin, salaryMax, salaryCurrency, salaryUnit | Published pay range, currency and unit such as YEAR |
postedAt, validThrough | Posting date and listing expiry |
descriptionHtml, descriptionText | Full description, when includeDescriptions is on |
jobUrl, applyUrl | Canonical public job page and application link |
sourceSearchUrl, scrapedAt | Originating search page and collection time |
{"jobId": "11517349","title": "Software Engineering Intern","companyName": "Clerkie","companyUrl": "https://www.clerkie.io/","companyLogoUrl": "https://joinhandshake-production.imgix.net/app/public/assets/institutions/326956/original/hs-emp-logo-data.","location": "Remote","isRemote": true,"employmentType": "FULL_TIME","industry": "Internet & Software","descriptionHtml": "<p>We’re hiring<strong> Software Engineering Interns!</strong> ...</p>","descriptionText": "We’re hiring Software Engineering Interns! This is a full-time (40+ hours a week) salaried 12 month internship...","salaryMin": 80000,"salaryMax": 100000,"salaryCurrency": "USD","salaryUnit": "YEAR","postedAt": "2026-09-28T19:01:39.000Z","validThrough": "2026-10-28T06:00:00.000Z","jobUrl": "https://app.joinhandshake.com/public/jobs/11517349","applyUrl": "https://app.joinhandshake.com/public/jobs/11517349","sourceSearchUrl": "https://joinhandshake.com/find-jobs/remote/software-engineering/","scrapedAt": "2026-10-01T01:44:24.966Z"}
Each run also writes an OUTPUT record with the outcome (COMPLETE, PARTIAL, VALID_EMPTY, INVALID_INPUT, UPSTREAM_FAILED or CONFIG_ERROR) and a RUN_SUMMARY with page-level counts.
Pricing
This Actor uses Pay per event plus Apify platform usage. You pay one Validated Handshake job returned event for each unique, validated job written to the dataset and one Actor Start event per run. Duplicates and filtered-out jobs are never charged, and maxItems caps the job events. Platform usage is billed separately by Apify.
Worked example in words: a run with maxItems set to ten that saves ten jobs costs ten Validated Handshake job returned events plus one Actor Start event, plus a small amount of platform usage. Check the live Pricing tab on this page for current event prices.
API example
curl -X POST "https://api.apify.com/v2/acts/khadinakbar~handshake-jobs-scraper/run-sync-get-dataset-items?token=YOUR_API_TOKEN" \-H "Content-Type: application/json" \-d '{"searchUrls": ["https://joinhandshake.com/find-jobs/remote/software-engineering/"], "maxItems": 10, "includeDescriptions": false}'
from apify_client import ApifyClientclient = ApifyClient("YOUR_API_TOKEN")run = client.actor("khadinakbar/handshake-jobs-scraper").call(run_input={"searchUrls": ["https://joinhandshake.com/find-jobs/remote/software-engineering/"],"maxItems": 25,"titleIncludes": "intern",})for job in client.dataset(run["defaultDatasetId"]).iterate_items():print(job["title"], job["companyName"], job["salaryMin"], job["validThrough"])
Use with AI agents (MCP)
Connect the Actor to Claude, ChatGPT, Cursor or any MCP client through Apify MCP and ask:
From the public Handshake remote software engineering page, list 10 internships with employer, pay range, posting date and apply link.
The agent passes the page in searchUrls with titleIncludes set to intern, reads rows from the dataset, and checks OUTPUT.outcome. Every row keeps jobUrl, sourceSearchUrl and scrapedAt for citation, and maxItems sets the cost scope before the run starts.
Best results
| Starting situation | Helpful action | Expected outcome |
|---|---|---|
| A broad category page | Add titleIncludes, for example intern or analyst | Only matching roles |
| Known job links | Use jobUrls instead of a search page | Direct enrichment of exactly those jobs |
| Lean exports | Set includeDescriptions to false | Faster, compact rows |
| Paid roles only | Set minSalary in the listing's pay unit | Jobs with published pay at or above it |
| Ongoing monitoring | Schedule weekly and compare jobId values | New postings each week |
| Closing-soon alerts | Sort on validThrough | Roles nearing their deadline |
Builder's note
I built this Actor on Handshake's anonymous public pages, which already carry structured job data, so it reads them with plain HTTP and needs no account. I designed it around URLs because Handshake's public category pages are the cleanest way to define a search, and I kept title, company, remote and salary filters local so they refine exactly what those pages show. I found that the expiry date matters as much as the posting date for student audiences, so validThrough sits on every row.
FAQ
Do I need a Handshake account?
No. The Actor reads anonymous public Handshake job pages.
Can it search all of Handshake by keyword?
It works from the public pages you provide. Use a category page as the starting point and narrow with titleIncludes and companyIncludes.
Why is salary empty on some jobs?
Pay fields are filled when the employer publishes compensation on the listing.
Does it return student or applicant data?
It returns public job postings only.
Responsible use
This Actor collects public job-posting data from Handshake. Use it in line with Handshake's terms and applicable laws, and keep its output to job-market research and recruiting; it is designed for job postings and market analysis. Handshake is a trademark of its owner; this independent Actor is not affiliated with or endorsed by Handshake.