Summary
When both is_remote=True and hours_old=N are passed to scrape_jobs() for the Indeed scraper, is_remote is silently dropped from the API filter, and onsite jobs come back in the result set. This is documented as a mutual-exclusion limitation, but the silent fallback makes it easy to miss in production code.
Repro
from jobspy import scrape_jobs
df = scrape_jobs(
site_name=["indeed"],
search_term="Data Engineer",
location="United States",
is_remote=True,
hours_old=24,
country_indeed="usa",
results_wanted=10,
verbose=0,
)
print(df[["title", "location", "is_remote"]])
Expected
Either:
- Only remote jobs returned (i.e., the is_remote filter is honoured, perhaps by issuing a second query or by client-side filtering), or
- A logging. warning (or UserWarning) telling the caller that is_remote is being dropped because hours_old was set.
Actual
9 of the 10 returned rows have is_remote=False (varying onsite locations across the US). No warning, no log line — the parameter is simply skipped because of the if/elif chain in jobspy/indeed/__init__.py::_build_filters:
if self.scraper_input.hours_old:
filters_str = ... # date filter wins
elif self.scraper_input.easy_apply:
...
elif self.scraper_input.job_type or self.scraper_input.is_remote:
... # never reached when hours_old is set
The README does mention "Only one from this list can be used in a search: hours_old / job_type & is_remote / easy_apply" under the Indeed section, but downstream code that builds these arguments dynamically (search-pass loops, configurable time windows) won't get any
Feedback that the filter is being silently ignored.
Impact
In our agent, this means LLM/eval cycles burn on jobs we'd reject on location anyway. We worked around it by post-fetch filtering on the returned is_remote column, but we only spotted it after the match rate had halved and we manually inspected the data.
Suggested fix
A 3-line change at the top of _build_filters (or in scrape_jobs itself) to log a warning when conflicting filters are passed:
if self.scraper_input.hours_old and self.scraper_input.is_remote:
logger.warning(
"Indeed: is_remote is ignored when hours_old is set "
"(see README — only one of hours_old / is_remote+job_type / easy_apply "
"can be used per search). Pass hours_old=None and filter on is_remote, "
"or filter results client-side."
)
Same idea for easy_apply + job_type/is_remote.
Even better — accept both and run a second query (or post-filter the DataFrame on is_remote) so callers get the result they asked for. But the warning alone would have saved us a few hours of debugging.
Environment
- python-jobspy==1.1.82 (PyPI, latest)
- Python 3.14
- Windows 11
Thanks for the library — it works well overall, this is the only sharp edge we've hit.
Summary
When both
is_remote=Trueandhours_old=Nare passed toscrape_jobs()for the Indeed scraper,is_remoteis silently dropped from the API filter, and onsite jobs come back in the result set. This is documented as a mutual-exclusion limitation, but the silent fallback makes it easy to miss in production code.Repro