Use Indeed Global Job Scraper with Company Salary Reports with Python
Search Indeed jobs across 62 countries by keyword, location, or filters for job type, remote status, experience, education, and salary. Returns full descriptions, structured salaries, and each employer's ratings and salary-by-title reports.
1. Choose the data you need
Review the published README for supported collection modes, examples, and limits. The recipe below uses the current published input definition.
2. Prepare a source-based input
This JSON contains published prefills and required-field defaults. Review every value for your task before running it; it is not a promise of successful collection.
{
"country": "US",
"easyApplyOnly": false,
"includeCompanyRatings": false,
"includeDescriptionHtml": false,
"includeSponsored": false,
"location": "San Francisco",
"maxItems": 3,
"maxItemsPerSearch": 3,
"position": "web developer",
"saveOnlyUniqueItems": true,
"shardByDate": false
}
Download input.example.json and save your reviewed copy as input.json.
| Field | Type | Required | Description |
|---|---|---|---|
position | string | optional | Job title, skill or keywords, e.g. `web developer`, `nurse`. Search one company's jobs with `company:Amazon`, titles only with `title:(data analyst)`; quotes, OR and -exclusions work too. |
location | string | optional | City, region or postal code, e.g. `San Francisco`, `Austin, TX`, `10001`, `London`. Leave empty to search the whole country. |
company | string | optional | Only jobs from one employer, e.g. `Amazon`, `Visa`, `DocuSign`. The name is resolved to Indeed's employer key so the company filter is applied by Indeed itself, then verified per job. |
locationShards | array | optional | Optional list of extra locations to search separately with the same keywords and filters, e.g. `New York, NY`, `Chicago, IL`, `Remote`. Deduplication removes jobs found by more than one shard. This is the reliable way past the roughly 1,000-row cursor ceiling. |
country | string | optional | Indeed country site to search. Results come in the site's language and salaries in its currency. |
maxItemsPerSearch | integer | optional | Maximum number of jobs to save for each search (the keyword search and each search URL). Indeed's cursor stops at roughly 1,000 fetched rows, including duplicates; use `locationShards` or more search URLs to collect more. |
maxItems | integer | optional | Optional hard cap across every search and pasted job URL. The run stops as soon as this many jobs are saved. Leave empty for no global cap. |
shardByDate | boolean | optional | Run each keyword search five times, for the last 1, 3, 7, 14 and 30 days, and remove duplicates. Useful for freshness coverage when you need more than one cursor walk per search. |
jobType | string | optional | Employment type. |
workplaceType | string | optional | Where the work happens. |
datePosted | string | optional | Only jobs added to Indeed within this period. |
experienceLevel | string | optional | Required experience level. |
educationLevel | string | optional | Required education. |
salaryMin | integer | optional | Only jobs whose salary range reaches at least this yearly amount, in the country's currency (hourly and monthly pay is converted to yearly). Jobs without any salary information are excluded. |
easyApplyOnly | boolean | optional | Only jobs you can apply to directly on Indeed. |
radius | integer | optional | Distance around the location (Indeed's default is 25). Ignored without a location. |
radiusUnit | string | optional | Unit of the search radius. |
sort | string | optional | Order of the search results. |
startUrls | array | optional | Indeed search result URLs (any country, with their filters), job URLs (`…/viewjob?jk=…`) or job IDs. Each search URL is scraped as its own search; job URLs are fetched directly. Filters chosen above are added to search URLs. |
includeCompanyRatings | boolean | optional | Add the employer's HTML company report: CEO approval, founding year, work wellbeing scores and average salaries by job title, plus an industry fallback. Ratings, review count and the five category ratings are always included from Indeed's app API at no extra request; this toggle adds the fields that require one company-page fetch per employer. Turn off for the fastest, cheapest runs. |
includeSponsored | boolean | optional | Add `isSponsored` from Indeed's mobile search page for jobs that also appear there. This costs one extra lightweight page per search and is best effort: jobs outside the first mobile result page keep the field omitted rather than guessed. |
includeDescriptionHtml | boolean | optional | Also save the job description as HTML (`descriptionHtml`) with its formatting. The plain-text `description` is always included. |
saveOnlyUniqueItems | boolean | optional | Save each job once even when several searches find it. Jobs are always unique within one search. |
3. Start an Actor run
Set APIFY_TOKEN in your environment. Python 3.10 or newer is sufficient; this recipe uses no third-party packages.
Execution may incur charges. Review current pricing on Apify. The request asks for a 120-second timeout and a charge limit; these settings are subject to Apify’s pricing and do not guarantee an all-inclusive spending cap.
import json
import os
import urllib.request
# Save the schema-derived example as input.json; review and edit it first.
with open("input.json", encoding="utf-8") as stream:
actor_input = json.load(stream)
token = os.environ["APIFY_TOKEN"]
request = urllib.request.Request(
"https://api.apify.com/v2/acts/JCXoFe3h1UxJEFHCg/runs?timeout=120&maxTotalChargeUsd=0.50",
data=json.dumps(actor_input).encode("utf-8"),
headers={"Authorization": "Bearer " + token, "Content-Type": "application/json"},
method="POST",
)
# This starts a potentially billable run. Do not automatically retry this POST.
with urllib.request.urlopen(request, timeout=40) as response:
run = json.load(response)["data"]
print("Run ID:", run["id"])
print("Status:", run["status"])
# Inspect and download results in Apify Console once the run succeeds.
4. Inspect the actual results
Open the run in Apify Console and wait for a successful completion. Use the published output schema to locate results and the dataset schema to understand declared views. Missing or optional record fields must not be treated as guaranteed values.
One record per Indeed job listing, with title signals, normalized salary, location, benefits, contacts, coverage counts, and the employer's rating and salary-by-title report.
Questions before you run
Which inputs are required?
No top-level fields are marked required in the published schema. Conditional requirements can still apply; check the README.
What does this guide verify?
The fields, prefills, and schema links are rendered from the published build. This is a usage recipe, not a paid live extraction test. Source availability and collection results can vary.
What if the run request times out?
Check Apify Console before retrying. The first request may already have created a run. Do not automatically repeat a run-creation POST.
Generated from source build lqQyrji8BaXSFlXeb and its README and schemas. Source changes regenerate this guide; no third-party editorial claims are inferred.