Site scraper

Indeed Scraper: Daily Job Listings from Every Indeed Site

HTTP $0.50JS rendering $2.00 per 1,000 results or your own scraper from $1.95/GB

ProxyField gets new Indeed jobs with salaries and company names into your database every day in two ways: the script on this page run on ProxyField residential proxies, from the Easy Trial (1 GB for $2.95), or the Web Scraping API, in development (contact sales), priced at $2.00 per 1,000 rendered pages, from $1.00 at volume. Per job that is about $0.20 per 1,000 jobs for the card fields (title, company, location, pay) at 10 cards a page, and $2.20 per 1,000 jobs with the full posting. Both routes return Indeed's pages, not a finished job feed: the script below is the parser, and it writes one row per job, keyed on Indeed's job key jk, with salary minimum, maximum, currency and period when the employer shows pay.

Web Scraping API per 1,000 successful results
Web Scraping API prices per 1,000 successful results
FetchEntryVolume, from
HTTP$0.50$0.20
JS rendering$2.00$1.00

Billed on successful results only.

Your own scraper on residential proxies
from $1.95/GB Easy Trial: 1 GB for $2.95
gate.proxyfield.io:7000

Run it in 10 minutes:

  1. pip install playwright && playwright install chromium
  2. Buy the Easy Trial (1 GB for $2.95) and put your login and password in the script's PROXY.
  3. Set COUNTRY ("us", "gb", "de" …), QUERY and PLACE.
  4. python3 indeed_daily.py: yesterday's new jobs go into jobs.db and new_jobs_<date>.jsonl.
  5. Add one cron line: 0 6 * * * cd /opt/indeed && python3 indeed_daily.py >> run.log 2>&1.

What lands in your database, as the script writes it to new_jobs_<date>.jsonl (illustrative values, not real jobs). Salary fields are null when the posting shows no pay, never estimated:

{"site": "www.indeed.com", "jk": "a1b2c3d4e5f60718", "title": "Data Analyst", "company": "Example Health Co", "location": "Chicago, IL", "salary_text": "$70,000 - $85,000 a year", "salary_min": 70000.0, "salary_max": 85000.0, "currency": "$", "period": "year", "first_seen": "2026-10-28", "last_seen": "2026-10-28"}
{"site": "www.indeed.com", "jk": "0f9e8d7c6b5a4321", "title": "Junior Data Analyst", "company": "Example Logistics", "location": "Chicago, IL 60606", "salary_text": "$28 - $32 an hour", "salary_min": 28.0, "salary_max": 32.0, "currency": "$", "period": "hour", "first_seen": "2026-10-28", "last_seen": "2026-10-28"}
{"site": "www.indeed.com", "jk": "1a2b3c4d5e6f7a8b", "title": "Senior Data Analyst", "company": "Example Bank", "location": "Remote in Chicago, IL", "salary_text": null, "salary_min": null, "salary_max": null, "currency": null, "period": null, "first_seen": "2026-10-28", "last_seen": "2026-10-28"}
Data

Indeed data on public pages

An Indeed scraper reads what a job seeker sees without signing in. Your own script fetches the pages through ProxyField residential proxies today; the Web Scraping API, in development, is designed to fetch any Indeed URL and return the page. Either way your code pulls the fields out: ProxyField sells no separate Indeed API with pre-parsed fields.

Indeed pageWhat the public page shows
Job search (/jobs?q=…&l=…)Job cards: title, company, location, salary when shown, job type, how long ago it was posted, the job key
Job page (/viewjob?jk=…)Full description, salary, job type, benefits, company rating, apply route
Company page (/cmp/<name>)Overview, size, industry, open jobs
Company reviews and salariesRatings, review text, role and location of the reviewer, reported pay by job title

Indeed asks visitors to sign in for some deeper review and salary pages; anything behind a login is out of scope under ProxyField's acceptable use policy. The API's design returns the page itself as HTML, Markdown or plain text, or a JSON envelope with the status code, the final URL, the headers, the cookies and the content; it has no parsed Indeed fields, so your parser reads them.

A parsed job feed or raw pages?

There are two ways to get Indeed jobs into a database: a hosted scraper that returns finished job records, or fetched pages plus your own parser. ProxyField sells the second: residential proxies for your script today, and the Web Scraping API in development.

ItemA hosted parsed-feed scraperProxyField pages + your parser
What comes backjob records in the service's field listthe rendered page; your parser writes the fields (script below)
Who writes the parserthe serviceyou: the script on this page, about 85 lines
When Indeed changes its markupyou wait for the service to updateyou fix a selector yourself
Which fieldsthe service's listanything on the public page, in your own schema
Price unitset by the service, often per job or per runAPI: $2.00 per 1,000 successful results with JS rendering, from $1.00, so $0.20 per 1,000 jobs from cards; proxies: per GB, from $2.95 for 1 GB
Daily schedulethe service's schedulerone cron line on your server
The same code for other sitesnoyes: any public URL
Availablethe service's termsresidential proxies now; the API in development (contact sales)

A daily Indeed job feed with salaries and company names

A daily feed is one short search per keyword and city, run every morning, written into your database with the job key as the primary key:

StepWhat to set
Only new jobssort=date (newest first) and fromage=1 (posted in the last day)
Pagingstart=0, 10, 20… until a page has no cards
Primary keythe card's data-jk value; a job seen again only updates last_seen
Columnssite, jk, title, company, location, salary_text, salary_min, salary_max, currency, period, first_seen, last_seen
Salarythe text as shown ("$70,000 - $85,000 a year"), parsed into minimum, maximum, currency and period; all empty (NULL) when the posting shows no pay, never estimated
Full postingopen /viewjob?jk=<key> once per new key for the description and benefits
Scheduleone cron line, e.g. 0 6 * * * cd /opt/indeed && python3 indeed_daily.py >> run.log 2>&1
Read moreShow less

This Playwright script runs one search through one sticky ProxyField residential IP in the site's country, checks robots.txt before every page, skips images and fonts to save traffic, stops on a refusal, upserts every card into SQLite and writes the day's new jobs to a JSON Lines file. Change COUNTRY to move it to another Indeed site:

Python
# indeed_daily.py: yesterday's new jobs for one search -> jobs.db and new_jobs_<date>.jsonl
import json, random, re, secrets, sqlite3, time
from datetime import date
from urllib.parse import quote_plus
from urllib.robotparser import RobotFileParser
from playwright.sync_api import sync_playwright

COUNTRY = "us"            # one key per Indeed site: "gb", "ca", "de" ...
SITES = {"us": ("www.indeed.com", "en-US"), "gb": ("uk.indeed.com", "en-GB"),
         "ca": ("ca.indeed.com", "en-CA"), "de": ("de.indeed.com", "de-DE"),
         "fr": ("fr.indeed.com", "fr-FR"), "au": ("au.indeed.com", "en-AU"),
         "es": ("es.indeed.com", "es-ES"), "it": ("it.indeed.com", "it-IT"),
         "nl": ("nl.indeed.com", "nl-NL")}
SITE, LOCALE = SITES[COUNTRY]       # the Indeed domain and the browser locale
SESSION = secrets.token_hex(4)      # one sticky IP for the whole run
PROXY = {"server": "http://gate.proxyfield.io:7000",   # an IP in the same country, kept 30 min:
         # e.g. <login>-country-us-session-3f9a1c2e-ttl-1800 (add -city-new_york for one city)
         "username": f"<login>-country-{COUNTRY}-session-{SESSION}-ttl-1800",
         "password": "<password>"}
QUERY, PLACE = "data analyst", "Chicago, IL"           # "London", "Berlin" ...
SEARCH = (f"https://{SITE}/jobs?q={quote_plus(QUERY)}&l={quote_plus(PLACE)}"
          "&sort=date&fromage=1&start={start}")
MAX_PAGES = 10            # this script's own cap: start=0 to 90; split a search that needs more

def parse_pay(text):
    """'$70,000 - $85,000 a year' -> (70000.0, 85000.0, '$', 'year'); NULLs when no pay is shown.
    Reads US and UK number formats; adapt it for sites that write 45.000 EUR or 'pro Jahr'."""
    cur = re.search(r"[$£€]", text or "")
    nums = [float(n.replace(",", "")) for n in re.findall(r"\d[\d,]*(?:\.\d+)?", text or "")]
    if not cur or not nums:   # no currency sign: a job type such as "Full-time", not pay
        return None, None, None, None
    per = next((w for w in ("hour", "day", "week", "month", "year") if w in text.lower()), None)
    return min(nums), max(nums), cur.group(0), per

def text(card, selector):
    el = card.query_selector(selector)
    return el.inner_text().strip() if el else None

robots = RobotFileParser(f"https://{SITE}/robots.txt")
robots.read()
db = sqlite3.connect("jobs.db")
db.row_factory = sqlite3.Row
db.execute("""CREATE TABLE IF NOT EXISTS jobs (site TEXT, jk TEXT, title TEXT, company TEXT,
    location TEXT, salary_text TEXT, salary_min REAL, salary_max REAL, currency TEXT,
    period TEXT, first_seen TEXT, last_seen TEXT, PRIMARY KEY (site, jk))""")
today = date.today().isoformat()

with sync_playwright() as p:
    browser = p.chromium.launch(proxy=PROXY)
    page = browser.new_page(locale=LOCALE)
    page.route("**/*", lambda route: route.abort()        # images, media and fonts are traffic you pay for
               if route.request.resource_type in ("image", "media", "font") else route.continue_())
    for start in range(0, MAX_PAGES * 10, 10):
        url = SEARCH.format(start=start)
        if not robots.can_fetch("*", url):
            print(f"robots.txt disallows {url}: stop")
            break
        resp = page.goto(url, wait_until="domcontentloaded", timeout=60000)
        if resp is None or resp.status in (403, 429) or "Security Check" in page.title():
            print(f"refused at start={start}: stop, retry this search later at a slower pace")
            break
        # Indeed changes its markup: check these selectors on a live page first
        cards = page.query_selector_all("div.job_seen_beacon")
        if not cards:
            break             # end of the results, or the markup changed
        for card in cards:
            link = card.query_selector("a[data-jk]")
            if not link:
                continue
            pay = text(card, "[data-testid='attribute_snippet_testid']")  # pay when shown; may hold the job type
            db.execute("""INSERT INTO jobs VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
                ON CONFLICT(site, jk) DO UPDATE SET last_seen = excluded.last_seen""",
                (SITE, link.get_attribute("data-jk"), link.inner_text().strip(),
                 text(card, "[data-testid='company-name']"),
                 text(card, "[data-testid='text-location']"),
                 pay, *parse_pay(pay), today, today))
        db.commit()
        time.sleep(random.uniform(5, 10))
    browser.close()

with open(f"new_jobs_{today}.jsonl", "w") as out:   # today's new jobs, one JSON object per line
    for row in db.execute("SELECT * FROM jobs WHERE site = ? AND first_seen = ?", (SITE, today)):
        out.write(json.dumps(dict(row)) + "\n")

Today's new jobs, with pay and company, for your database or a report:

sqlite> SELECT jk, title, company, location, salary_text, salary_min, salary_max, period
   ...> FROM jobs WHERE first_seen = date('now') ORDER BY salary_max DESC;

The rows look like the three at the top of this page. A row without pay keeps the salary columns empty: the posting showed none. Each run, new keys are inserted and known keys only get a new last_seen, so a job that stops appearing has closed. On Windows, Task Scheduler runs it daily: schtasks /create /sc daily /st 06:00 /tn indeed-daily /tr "py C:\indeed\indeed_daily.py". More on running it every day: schedule web scrapers.

What a daily feed would cost on the Web Scraping API (in development): 50 keyword-city searches of up to 5 result pages, plus 1,000 new job pages a day, come to 37,500 rendered pages a month. That is $75 at $2.00 per 1,000, or $37.50 at the volume price.

Per job, not per page: you pay per page, and one search page holds many job cards.

What you collect per jobPages per 1,000 jobsAt $2.00 per 1,000At $1.00 (volume)
Card fields from search pages (title, company, location, pay), at 10 cards a page100$0.20$0.10
The full posting (description, benefits, pay) from /viewjob1,000$2.00$1.00
Both1,100$2.20$1.10

Count the cards on your own result pages in the first run; the job key removes a card that appears on two pages.

The same feed on residential proxies, today: you pay per GB, so the cost depends on what one page weighs with images and fonts skipped, as the script does. Read the GB used in the dashboard's real-time usage stats after the first run, then divide by the pages fetched. A worked example, if a results page weighs 1.5 MB:

Your own scraper on residential proxiesPagesTrafficCost
1,000 jobs from cards, 10 a page1000.15 GB$0.44 at $2.95/GB
50 searches × 5 result pages a day, for 30 days7,50011.25 GB$33.48 for 12 GB at $2.79/GB

The traffic never expires, so GB bought for this month's feed and left over carry into the next.

Indeed's country sites: one scraper, a few settings

Yes: the same scraper collects Indeed UK and the other country sites, because they share one URL pattern (/jobs?q=…&l=…&start=…, /viewjob?jk=…). Each site has its own listings, language and currency, so four things change per site: domain + IP country + browser locale + the parser's pay format. In the script above, the COUNTRY key sets the first three; you test the fourth once per site, and check that the script gets past the cookie consent banner UK and EU sites show. Indeed UK, side by side for your own scraper and for the Web Scraping API's design:

proxy username:  <login>-country-gb-session-3f9a1c2e-ttl-1800
API request:     {"url": "https://uk.indeed.com/jobs?q=data+analyst&l=London&sort=date&fromage=1", "render": true, "country": "gb"}
Read moreShow less

On the API, country sets the country the request comes from (the exit IP), as a two-letter code; it accepts the proxy pool's 195+ countries, and every Indeed domain costs the same per 1,000 successful results.

Indeed siteURLIP for your own scraper
United Stateswww.indeed.comUS proxies: 61.2M+ residential IPs, 2,800+ cities
United Kingdomuk.indeed.comUK proxies: 16.6M+, 640+ cities
Canadaca.indeed.comCanadian proxies: 5.5M+
Germanyde.indeed.comGerman proxies: 20.2M+, 980+ cities
Francefr.indeed.comFrench proxies: 8.3M+, 170+ cities
Australiaau.indeed.comAustralian proxies: 4.1M+, 140+ cities
Spaines.indeed.comSpanish proxies: 13.7M+, 690+ cities
Italyit.indeed.comItalian proxies: 10.2M+, 210+ cities
Netherlandsnl.indeed.comDutch proxies: 3.2M+, 110+ cities

The table holds 9 of the sites. Indeed lists 63 countries, 91 country and language versions, at indeed.com/countries; take each site's exact domain from there. Other Indeed markets with ProxyField residential IPs include Ireland (310K+), Belgium (180K+), Switzerland (160K+), Austria (130K+), Poland (5M+), Mexico (26.1M+), Brazil (36.8M+), India (42.6M+), Japan (3.3M+) and Singapore (1.4M+).

What to change per country in your own scraper:

  1. The domain in SEARCH, e.g. https://uk.indeed.com/jobs?q=data+analyst&l=London&sort=date&fromage=1&start={start}.

  2. The IP country in the username, e.g. <login>-country-gb for uk.indeed.com. Countries take their two-letter code in lowercase, as in <login>-country-gb.

  3. The browser locale to match: new_page(locale="en-GB"), "de-DE", "fr-FR".

  4. The parser salaries come in the site's currency and wording (£ and "a year" on the UK site), and location formats differ. Test the selectors on one page of each site before the first daily run.

  5. The site column, already in the script's table, because a job key belongs to one country site.

  6. The consent banner

    UK and EU visitors may see a cookie consent dialog before the results; check that your script reaches the job cards.

  7. The law on UK and EU sites, UK GDPR and the GDPR apply to any personal data you collect, such as a recruiter's name in a posting; keep it out of your dataset. Each country site has its own terms page. See Is Web Scraping Legal?

Targeting covers country, region (state) and city in 195+ countries; ASN and ZIP targeting are not offered.

Compare

Indeed's official APIs or a scraper?

Indeed's developer APIs are built for employers, applicant tracking systems and job board partners: Sponsored Jobs, Indeed Apply, Job Sync. They post and sync your own jobs and receive applications; none of them is a public job search feed for collecting listings. If you are an Indeed partner, use the partner APIs; to read public listings, a scraper fetches the pages.

ItemIndeed partner APIsWeb Scraping API
Who can use itApproved employers, ATS and job board partnersProxyField customers
PurposePosting jobs and handling applicationsReading public Indeed pages
OutputYour own jobs and applicantsThe page, for your parser
BillingSet by IndeedPer 1,000 successful results

What you send

The Web Scraping API is in development: to start, contact sales. In its design, you send an Indeed URL and get the page back: a POST to https://api.proxyfield.io/v1/scrape with your API key in an Authorization: Bearer <api_key> header and a JSON body: url, render (false for plain HTTP, true for JS rendering), country and output (html, markdown, text or json). Because Indeed builds its pages with scripts, wait_for takes a CSS selector the rendered page must show before it is returned, such as the job cards. Parameter reference: API documentation.

A search URL you would send looks like https://www.indeed.com/jobs?q=data+analyst&l=Chicago%2C+IL&sort=date, and a job page like https://www.indeed.com/viewjob?jk=<key>.

Shell
# The request in the Web Scraping API's design (in development: contact sales)
curl https://api.proxyfield.io/v1/scrape \
  -H "Authorization: Bearer <api_key>" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://www.indeed.com/jobs?q=data+analyst&l=Chicago%2C+IL&sort=date", "render": true, "country": "us", "output": "json"}'

Example response in the design (shortened; content is the rendered page's HTML):

JSON
{
  "metadata": {"id": "req_09a6d3f1b8e2c574", "api": "web_scrape", "status": "success", "billed": true},
  "status_code": 200,
  "final_url": "https://www.indeed.com/jobs?q=data+analyst&l=Chicago%2C+IL&sort=date",
  "content_type": "text/html; charset=utf-8",
  "content": "<!DOCTYPE html><html lang=\"en\">…</html>"
}
Pricing

Pricing: $2.00 per 1,000 rendered pages, from $1.00

Indeed builds and guards its pages with scripts, so budget for JS rendering: $2.00 per 1,000 successful results, from $1.00 at volume. Pages that load over plain HTTP cost $0.50 per 1,000, from $0.20. You pay only for successful results. Billed: a page Indeed answered with a 2xx status. Not billed: blocked requests, timeouts and errors on our side; a blocked request is retried on new IPs, and the retries are not billed. Every response says whether it was billed. A target's 404 or 410, and a sign-in page the site serves with a 200 status, count as billed results. The API is in development: to start, contact sales through the sales team on our 24/7 live chat. The volume price starts at 10,000 successful results in one purchase.

The same 120,000 pages over plain HTTP would cost $60, or $24 at the volume price. Payment and balance for the API: the same crypto balance as proxies, which never expires; there is no subscription or monthly fee. All prices: pricing.

Through the sales team on our 24/7 live chat

Workload (30-day month)
Workload (30-day month), Pages a month, JS rendering at $2.00, At $1.00 (volume)
Workload (30-day month)Pages a monthJS rendering at $2.00At $1.00 (volume)
Daily feed: 50 searches × 5 result pages, plus 1,000 new job pages, daily37,500$75$37.50
200 keyword-city searches, first 5 result pages, daily30,000$60$30
3,000 new job pages, daily90,000$180$90
The last two together120,000$240$120
Do it yourself

Scraping Indeed with Python without getting blocked after a few pages

Refusals after the first few result pages come from pace and a changing IP, so: one sticky IP per browser session, 5 to 10 s between pages, and stop on a 403 or 429. The 10-page cap per search is the script's own choice (MAX_PAGES), not an Indeed limit; robots.txt decides which start values may be fetched, and the script checks it before every page. Indeed's Terms of Service restrict automated access without its permission, so check your use against them first (see Legal questions).

What a refusal looks like, so your code can tell it from a results page:

requests.get("https://www.indeed.com/jobs?q=data+analyst")  ->  status 403, <title>Security Check - Indeed.com</title>
Playwright after a burst of pages                            ->  page.title() == "Security Check - Indeed.com"
Read moreShow less

The page may also say "Additional Verification Required". The script stops on these conditions:

ConditionWhat the script does
robots.txt disallows the URLdoesn't request it, and stops
HTTP 403stops this search; retry it later at a slower pace
HTTP 429stops; wait before the next run (429 Too Many Requests)
A page titled "Security Check"stops
A page with no job cardsstops: the end of the results, or changed markup
10 result pages read (start=90, the script's MAX_PAGES)stops; split the search by city, or use fromage=1

And it keeps to these settings:

  • A real browser. Use Playwright, not plain requests, because Indeed builds its pages with scripts.
  • One IP per browser session. Keep the cookies and the IP together with a sticky session (30 s to 36 h) instead of a new IP per request; the script's PROXY does this with -session-<id>-ttl-1800. An IP idle for 120 s gets a new IP, which a 5 to 10 s pace never reaches. Region, session and session time go in the same username: -region-<region>, -session-<id> and -ttl-<seconds> (30 to 129,600 seconds, 20 minutes by default), as in <login>-country-us-region-new_york-city-new_york-session-he9f2qga-ttl-600; every part after the login is optional.
  • The site's country. Use residential IPs from the same country as the Indeed site.
  • 5 to 10 s between result pages, as in the script above, at most 10 result pages per search, and a few seconds before each /viewjob page.
  • Stop on a refusal. On a 403, a 429 or the security check page, stop that search and retry it later at a slower pace. Retrying at once only adds more refusals. See 429 Too Many Requests and Cloudflare 403 errors.
  • Short searches. A broad search can list more jobs than 10 pages hold. Split it: one search per city, and fromage=1 for daily runs, so each search stays within the script's 10 pages.

No setup prevents every refusal. On the Web Scraping API's design you would pay only for successful results.

Access opens once the payment is confirmed; the gateway, credentials or allowlist are in your account. The username format is <login>-country-us-city-new_york, or as a full line: gate.proxyfield.io:7000:<login>-country-us-city-new_york-session-3f9a1c2e-ttl-1800:<password>. With the IP allowlist, you connect to the port you set up in the dashboard instead of 7000. Residential proxies cost $2.95/GB for 1 GB and from $1.95/GB at volume; the traffic never expires, and all purchases are final. Setup guides: Playwright, Selenium.

Use cases

What teams use Indeed data for

  • Hiring trends: which companies and industries add or cut openings, by city.
  • Salary benchmarking from the pay ranges on postings.
  • Recruiting agencies spotting new roles at target employers.
  • Employer brand research from public company ratings and reviews.

Applying to jobs, posting fake listings or reading candidate data through a scraper is not data collection, and fake accounts are forbidden under ProxyField's acceptable use policy.

Sources

  • Indeed, country sites: 63 countries, 91 country and language versions.
  • Indeed, Terms of Service and robots.txt: automated access and the paths crawlers may fetch.
  • Indeed, developer documentation: partner APIs for employers, applicant tracking systems and job boards (Sponsored Jobs, Indeed Apply, Job Sync).
  • Playwright, Python docs: the proxy option at browser launch.
More scrapers

More scrapers

FAQ

Indeed Scraper FAQ

24/7 support by live chat, including help with setup and integration.

How much does it cost to scrape Indeed?

ProxyField's Web Scraping API, in development (contact sales), is priced at $2.00 per 1,000 successful results with JS rendering, from $1.00 at volume, and $0.50 per 1,000 (from $0.20) over plain HTTP. A daily feed of 50 searches (5 result pages each) and 1,000 new job pages comes to 37,500 rendered pages a month: $75 at the entry price, $37.50 at volume. Your own scraper on residential proxies pays per GB instead, from $2.95 for 1 GB.

Which Indeed scraper or API can pull new job listings with salaries and company names into my database every day?

ProxyField's route costs about $0.20 per 1,000 jobs for card fields (title, company, location, pay) and $2.20 per 1,000 jobs with the full posting, on the Web Scraping API (in development, contact sales; $2.00 per 1,000 rendered pages). It returns pages, not a finished job feed: the script on this page is the parser, and it runs today on ProxyField residential proxies from 1 GB for $2.95. It searches with sort=date&fromage=1, upserts each job on its key jk with salary minimum, maximum, currency and period (null when no pay is shown), writes the day's new jobs to a JSON Lines file and runs from one cron line.

Can the same scraper collect jobs from Indeed's other country sites, such as Indeed UK, or does each need its own setup?

The same scraper works: uk.indeed.com, de.indeed.com, ca.indeed.com and the other country sites (Indeed lists 63 countries) use the same URL pattern, so per site you change four things, domain, IP country (<login>-country-gb for the UK), browser locale and the parser's pay format, then check the cookie consent banner and test once. On ProxyField's Web Scraping API (in development) every Indeed domain is priced the same, $2.00 per 1,000 rendered results, from $1.00, with "country": "gb" in the request.

How do I scrape Indeed job listings with Python without getting blocked after the first few result pages?

Refusals after a few pages come from pace and a changing IP, so with ProxyField use Playwright with one sticky residential IP per browser session from the site's country (<login>-country-us-session-<id>-ttl-1800) and wait 5 to 10 seconds between result pages. Stop on any of four signals: a robots.txt disallow, a 403, a 429 or the "Security Check - Indeed.com" page title; the script's 10-page cap per search is its own choice, so split broad searches by city and use fromage=1. Check Indeed's terms for your use first; no setup prevents every refusal.

What is an Indeed scraper?

A tool or script that collects public data from Indeed's search results, job pages and company pages. It reads what a signed-out job seeker sees and turns each job card or posting into fields.

Is there an official Indeed API?

Indeed's APIs serve employers, applicant tracking systems and job board partners, for posting and syncing jobs and handling applications (Sponsored Jobs, Indeed Apply, Job Sync). None of them is a public job search feed for collecting listings.

Is there an Indeed scraper API?

ProxyField has no dedicated Indeed API with parsed fields. Its Web Scraping API, in development (contact sales), is designed to fetch any public Indeed URL and return the page, priced per 1,000 successful results; your parser reads the fields.

How do I scrape Indeed jobs?

Request a search URL with your keywords in q and the place in l, collect the job keys from the cards, and page on with start. Then open each job at /viewjob?jk=<key> for the full posting.

How do I scrape Indeed with Python?

Use Playwright, since Indeed builds and guards its pages with scripts, and route it through a residential proxy as in the script on this page. Plain requests with Beautiful Soup often gets a security check page instead of results.

Does every Indeed job listing show a salary?

No. Pay appears on a card or posting only when the employer includes it, so keep the salary column nullable and store the text as shown. Parse minimum, maximum and period from that text later, per country site.

Show 12 more questionsShow fewer questions

What types of Indeed data can I collect?

Job titles, companies, locations, salary ranges when shown, job types, posting age, full descriptions and benefits, plus public company pages with ratings, reviews and reported salaries. Applicant data and anything behind a login are out of scope.

Does Indeed allow web scraping?

Indeed's Terms of Service restrict scraping and automated access without its permission, and the site actively blocks bots. Check your use against the terms, collect public pages only, or ask Indeed about partner access.

Does Indeed have anti-scraping measures?

Yes, and they are among the stricter ones on job sites: bursts of automated requests get a "Security Check" page, 403 errors or empty results. Indeed also changes its page markup, which breaks fixed selectors.

Can Indeed be scraped with Playwright?

Yes: Playwright runs a real Chromium that executes Indeed's scripts, and it takes a proxy server, username and password at launch. Use a sticky ProxyField session so the browser keeps one IP while it works through a search.

Do I need coding skills to use an Indeed scraper?

For the Web Scraping API or your own script, some coding helps: you send URLs and parse the pages. No-code tools exist too, but they still need IPs and break when Indeed changes its pages.

Does an Indeed scraper need an Indeed account or API key?

No: job searches, job pages and company overviews are public to signed-out visitors. You need credentials only for the scraping service or proxies you use.

How many results does one Indeed search return?

Each results page shows a set of job cards, and a broad search can match more jobs than you can page through in one run. The script on this page reads at most 10 pages per search, its own cap; split broad searches by city, date filter or narrower keywords.

How do I open a job page from an Indeed search result?

Read the card's job key, the data-jk value, and request /viewjob?jk=<key> on the same Indeed site. The key also works as a stable ID for deduplicating jobs across searches.

Can I scrape Indeed company reviews and salaries?

The public parts of a company's reviews and salaries tabs can be fetched like any other Indeed page. Indeed asks visitors to sign in for some deeper pages, and those stay out of scope.

Can I build a custom job database from Indeed?

Technically yes, keyed on each job's jk with first-seen and last-seen dates, as in the daily script on this page. Republishing Indeed's listings is a terms question, so check Indeed's terms before you build a product on them.

How does Indeed get its job listings?

Employers post jobs on Indeed directly, and Indeed also collects listings from company careers sites and applicant tracking systems. That is why the same job often appears on Indeed and on the employer's own page.

Collect Indeed pages with the Web Scraping API

Billed on successful results only. Contact sales for the Web Scraping API and the volume you need, or run your own scraper on residential proxies. 24/7 live chat helps with setup and integration.