If you are tracking a hiring market, the job you actually want is boring: get every posting for a role in a location, keep the list fresh, and have it land somewhere you can sort. Writing that yourself means a headless browser, a proxy pool, and a standing commitment to fix it whenever the markup shifts.
This walks through doing it with a call.
What you get back
One row per posting, with the title, company, location, posting date and the link. The fields come from the listing itself, so what is missing on LinkedIn is missing here too. A posting with no stated salary returns no salary rather than a guess.
The call
The scraper takes a search query and a location, which are the same two things you would type into the site:
curl -X POST "https://api.apify.com/v2/acts/parseforge~linkedin-jobs-scraper/run-sync-get-dataset-items?token=$APIFY_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"searchQuery": "software engineer",
"location": "United States",
"datePosted": "anyTime",
"maxItems": 50
}'
The response is the dataset: a JSON array, one object per posting. There is no pagination to handle and nothing to parse.
datePosted is the filter worth knowing about. Set it to the last day or the
last week and a nightly run only brings back what is new, which keeps both the
run and your deduplication cheap.
Getting it into a spreadsheet
Every run writes to a dataset that exports in other formats. Swap the endpoint and you get a CSV instead of JSON:
https://api.apify.com/v2/datasets/<DATASET_ID>/items?format=csv
That URL is stable, so a spreadsheet can pull it directly. In Google Sheets,
IMPORTDATA against it gives you a sheet that refreshes on its own.
Running it nightly
Two options, both without a server:
- A schedule. Apify runs the scraper on a cron you set and keeps each run’s dataset. Nothing to host.
- A webhook. Point it at your endpoint and it fires when a run finishes, with the dataset id in the payload. Useful when you want to push rows into your own database rather than pull them.
What breaks, and what does not
LinkedIn changes its markup. When it does, the scraper is repaired and rebuilt. The thing that does not change is the call above: same endpoint, same input fields, same shape coming back. That is the part worth integrating against.
The harder limit is scope. This reads public job listings. It does not sign in, and it does not reach anything behind an account.