Indeed & LinkedIn Job Scraper + Change Tracking
Pricing
from $1.00 / 1,000 unique job checkeds
Indeed & LinkedIn Job Scraper + Change Tracking
Scrape Indeed and LinkedIn jobs or public Greenhouse employer boards into one API. Keep salary currencies and pay periods, remove exact duplicates, and track new and changed jobs. Start with a five-job example, clear pricing and spending caps. Apify free-plan credit can be used.
Pricing
from $1.00 / 1,000 unique job checkeds
Rating
0.0
(0)
Developer
Shahryar
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
3 days ago
Last modified
Categories
Share
Indeed & LinkedIn Job Scraper and Job Change Tracking API
Job Intelligence API searches Indeed and LinkedIn, optionally adds public Greenhouse employer boards, and returns a consistent job feed with salary fields, original source links, exact-match deduplication and observed changes. Use it for recruiting research, job-board feeds and employer hiring monitoring. No LinkedIn login or personal cookies are required. Search-source availability can change; failed requests are reported explicitly.
Try five jobs first
- Select Try for free or Try it on this Actor's Store page and sign in to Apify.
- Use the small Indeed example below. Set the platform maximum charge to $0.05 as an additional guard.
- Select Start, then open the Job dataset output. Export the results as JSON, CSV or Excel.
- Open Run summary and estimated charges to see checked-job counts, source failures and charges before increasing your limits.
{"sources": ["indeed"],"searchQueries": ["software engineer"],"location": "New York, NY","country": "us","maxItems": 5,"maxSourcePages": 1,"includeLinkedInDetails": false,"maxSpendUsd": 0.05}
One source request and five checked jobs cost $0.026 at up to 1 GB. The $0.05 setting is a cap, not a fixed charge. A source can return fewer jobs or fail; source-attempt fees still apply.
Can I try it without paying for an Apify plan?
Yes. Apify's Free plan currently includes $5 of monthly platform credit, shared across the Apify Store and your own Actors, with no credit card required. This Actor supports free-plan users with the same features and event prices. Runs consume your available Apify credit; this is not an unlimited free API or a separate Solidscrape coupon. Check your remaining credit and Apify's current terms before running.
The small example above lets you evaluate real output while keeping a clear spending cap. No external account, personal cookies or third-party API key is needed in the input.
Combine Indeed and LinkedIn jobs
{"sources": ["indeed", "linkedin"],"searchQueries": ["software engineer"],"location": "New York, NY","country": "us","maxItems": 20,"maxSourcePages": 2,"maxSpendUsd": 0.10}
Two source requests and 20 checked jobs cost $0.061, without optional LinkedIn details. Source rankings vary, so this is a bounded sample, not complete market coverage.
Get jobs directly from a Greenhouse employer board
{"sources": ["greenhouse"],"employerBoards": [{"slug":"figma", "company":"Figma"}],"searchQueries": ["engineer"],"maxItems": 10,"maxSourcePages": 1,"maxSpendUsd": 0.10}
Greenhouse company names are the labels you supply with each board. Board keywords match job titles, avoiding unrelated roles that mention a skill in their description. Location filters apply to search providers, not employer boards. The Actor does not enumerate all companies automatically.
What makes this useful
- Consistent job, company, location and salary fields across sources, with original source links and per-source identifiers.
- Salary ranges preserve currency, pay period and estimated status when supplied. Hourly pay is not guessed into annual pay. Missing salary fields stay unknown; original salary text is retained. Greenhouse pay ranges retain their source labels in
salaryRanges; multiple regional or job-level ranges are not collapsed into one misleading range. - Exact source IDs and job-specific application URLs support deduplication. Similar title/company/location combinations receive a possible-duplicate group ID but remain separate jobs.
- LinkedIn descriptions are optional because they require extra requests. Indeed and employer-board descriptions are included when available; text is capped at 6,000 characters. A normalized full-text hash supports description-change detection.
- Source request diagnostics, partial comparison status, clear spending limits and observation timestamps.
Monitor changes
Pass a previous run's default key-value store ID as previousSnapshotStoreId, keeping the same searches, sources, coverage limits and detail settings. Enable changesOnly to emit only new and updated records. The first run is a baseline, not evidence that those jobs were newly posted.
For a first monitoring workflow:
- Run the Greenhouse example above and wait for it to succeed.
- Copy that run's default key-value store ID from its Storage tab. You need the store ID, not its run ID or dataset ID.
- Run the same input again, adding the two fields below. Replace the placeholder with your own first run's store ID.
{"sources": ["greenhouse"],"employerBoards": [{"slug":"figma", "company":"Figma"}],"searchQueries": ["engineer"],"maxItems": 10,"maxSourcePages": 1,"maxSpendUsd": 0.10,"previousSnapshotStoreId": "YOUR_PREVIOUS_KVS_ID","changesOnly": true}
An empty dataset on the second run can be correct: inspect SUMMARY.counts.unchanged and SNAPSHOT. Unchanged jobs were still checked and billed. For repeated comparisons, use the most recent successful run's store ID each time; a fixed old baseline always compares against that older observation. No schedule is created automatically.
All unique jobs checked are saved in SNAPSHOT and billed, including unchanged jobs suppressed from the dataset. Up to 500 jobs are retained in history so one missing search sample does not reset their first-seen dates. observedInRun and currentJobIds distinguish fresh observations from carried history. Older records can be evicted at the history limit, and historyEvictions reports that count.
NOT_SEEN means a job was absent from this bounded sample. It does not mean expired or closed. Lifecycle changes require explicit evidence: a source-declared deadline that has passed, or a checked detail page explicitly stating that applications are closed. A 403, 404, failed search, or missing record is not closure evidence. Lifecycle is unknown when this evidence is unavailable.
Changes compare title, company, location, workplace, employment type, structured salary, application URL, explicit lifecycle evidence and description hash when available. Missing fields are not interpreted as deletions. Changes are observations from upstream sources, not independently verified hiring decisions.
Pricing
| Event | USD | Meaning |
|---|---|---|
| Actor start | $0.001 per allocated GB, minimum one | Startup |
| Source request | $0.02 | One search page or employer-board request attempt, including empty, blocked or unrecognized responses |
| Unique job checked | $0.001 | One unique job saved in SNAPSHOT and compared, including unchanged jobs |
| Optional LinkedIn detail attempt | $0.003 | One selected job-detail attempt, including blocked, missing or unrecognized responses |
Example: two source pages and 20 unique checked jobs cost $0.061 at up to 1 GB, without optional details. Ten LinkedIn detail attempts add $0.03. Results and optional detail availability are not guaranteed by the fee.
There are no filter surcharges. Compute and residential search traffic are included. The lower of the input and platform event-spending caps applies. Work stops before known event costs exceed the budget. Source requests include up to three zero-byte connection attempts within 30 seconds; responses are not automatically retried. Maximum allocated memory is 1 GB.
Outputs
The Job intelligence view shows the main fields. Use Salary comparison for pay data and Observed job changes for change type, changed fields and first/last-seen timestamps.
This abbreviated example is illustrative; it is not a current vacancy or a promise of which fields every source will supply:
{"jobId": "indeed:example-posting","title": "Software Engineer","company": "Example Company","location": "New York, NY","workplace": null,"salary": {"min": 110000, "max": 140000,"currency": "USD", "period": "year","isEstimated": false, "annualized": false},"applicationUrl": "https://example.com/jobs/12345678","change": {"type":"baseline", "fields":[], "comparisonStatus":"partial"},"lifecycle": "unknown"}
- Default dataset: normalized job records, optionally filtered to changes.
SNAPSHOT: checked jobs and retained history for the next run.SUMMARY: counts, stop reason and estimated charges.SOURCE_REPORTandPAGE_*: source attempts and normalized candidate records. Source-page work may be billed even if a run stops before final job output is produced.DETAIL_REPORTandJOB_*: optional detail attempts and saved job records.NOT_SEEN: absent prior records, without inferred closure.PROGRESS: checkpoints; uncertain billing outcomes stop rather than being charged twice.
Save previous snapshots in retained storage if they are needed beyond your account's retention period. Each user's baseline comes from their selected storage; no cross-customer history is shared.
API
POST the quick-start JSON to https://api.apify.com/v2/acts/solidscrape~job-intelligence-api/runs with your Apify bearer token in the authorization header. Poll the run ID, then read its default dataset and the output records above. Secrets are not required in input.
For a small synchronous request in JavaScript (Node.js 22+), using your own token from an environment variable:
const response = await fetch('https://api.apify.com/v2/acts/solidscrape~job-intelligence-api/run-sync-get-dataset-items?memory=512&timeout=120&maxTotalChargeUsd=0.05',{method: 'POST',headers: {Authorization: `Bearer ${process.env.APIFY_TOKEN}`,'Content-Type': 'application/json'},body: JSON.stringify({sources: ['indeed'], searchQueries: ['software engineer'],location: 'New York, NY', maxItems: 5,maxSourcePages: 1, maxSpendUsd: 0.05})});if (!response.ok) throw new Error(`Apify request failed: ${response.status}`);const jobs = await response.json();console.log(jobs);
Use asynchronous /runs for larger jobs and monitoring so you retain the run's store ID. The synchronous endpoint above returns dataset rows only. After-run storage access can incur normal Apify storage charges; retain snapshots only as long as your workflow needs them.
Frequently asked questions
Does this scrape LinkedIn without logging in?
Yes. It reads public job listings without personal cookies. It does not access private profiles or guarantee access to every LinkedIn job. Search cards provide basic fields; enable includeLinkedInDetails when you need available descriptions and employment types, at $0.003 per detail attempt.
Are salary amounts comparable across sources?
Compare currency and pay period first. The Actor keeps hourly, monthly and annual amounts distinct and does not guess annual pay. LinkedIn often supplies salary text without a structured range. Missing structured salaries stay null. Multiple employer pay bands retain their labels in salaryRanges.
Why did a changed-only run return zero rows?
All observed jobs may be unchanged. Check SUMMARY and SNAPSHOT before treating an empty dataset as a failure. If no source succeeded, inspect SOURCE_REPORT; the run reports failed requests explicitly.
Does a missing job mean it expired?
No. A bounded search can omit a still-open job. NOT_SEEN records absence; expiration or closure requires a source deadline or explicit closure evidence.
Which Actor should I choose for LinkedIn alone?
Choose this Actor when combining sources or comparing snapshots matters. In one ten-job comparison, a dedicated LinkedIn competitor was faster and cheaper for LinkedIn details alone. This Actor does not claim to be the cheapest or fastest for every single-source workload.
What should I include when reporting a problem?
Use this Actor's Issues tab. Include the affected source, run ID, expected behavior and the relevant SOURCE_REPORT error. Never post API tokens, cookies or private customer data. Share a debug run only if you are comfortable with the data it contains.
Practical limits
Up to five search queries, five employer boards, 20 source pages, 500 collected candidates and 250 unique checked jobs per run. Sources are interleaved for a balanced sample; search rankings and coverage can vary. Narrow searches are recommended. This is not an exhaustive job-market census.
LinkedIn search cards often omit salary or descriptions. Optional detail requests can improve fields but can also be blocked. Workplace type is derived only from explicit location labels such as Remote or Hybrid; a mention of remote work in an unrelated description does not establish a fully remote role. No application forms are submitted, and no candidate profiles or private account data are collected.
This independently developed Actor is provided by Solidscrape. LinkedIn, Indeed and Greenhouse are the respective source providers, not publishers or endorsers of this Actor.