Blog/Sales & enrichment
10 min read

Indeed API: Passing false to One Filter Returned a 502, Twice

Omit the flag and you get 245 jobs with 38 duplicates. Switch it on and you get 234 with 19. Send it as false and the call fails without charging.

Indeed API: Passing false to One Filter Returned a 502, Twice

Copy this line to your agent to pull a clean list of job postings.

set up https://monid.ai/SKILL.md and use indeed /search_jobs with exclude_staffing_agencies true

We ran the same job search three times on 2026-10-01, changing one boolean. Omitting it returned 245 postings. Setting it to true returned 234. Setting it to false, which should be the same as omitting it, returned a 502 from the provider, and did so on both attempts. Neither failure was billed. The more useful finding is what the filter actually buys you, and it is not the eleven postings it hides. This guide runs through Monid, the OpenRouter for agent tools.

What does the Indeed API return?

A search over live postings, plus a detail call, and a response shape you should know about before you write the parser.

The two calls

Search jobs takes a query and optional location, fromage for recency in days, jt for job type, explvl for experience level, and a couple of exclusion filters. Get job details takes one posting and returns the full record.

The response is not a jobs array

This is the part that costs people an hour. The search response hands back Indeed's own page-state object under output.data, the same structure their web app uses, with well over a hundred keys covering layout flags, experiment toggles and ad slots.

Two of those keys are the ones you want:

{ "totalJobCount":   245,
  "uniqueJobsCount": 207 }

Both sit at the top level of data. Everything around them is app state, and reaching for a field called something like results will give you nothing.

Why two counts

Because postings repeat. The same role gets listed more than once, by the employer and by whoever is recruiting for them, and Indeed tracks both the raw match count and the de-duplicated one. The distance between the two numbers is the interesting part, and the next sections are about moving it.

📖 See also Jooble API and Job Postings Data: Hiring as a Buying Signal

Why did false return a 502?

We do not know why, and we can tell you exactly when, which is enough to avoid it.

Same query, same location, same recency window, one parameter changed:

exclude_staffing_agenciesHTTPBilledResult
omitted200yes245 postings
true200yes234 postings
false502nonothing
false, retried502nonothing

Twice, not once, so it is not a flaky upstream. The JSON literal false reaches something that cannot handle it, while the absence of the key is handled fine.

Why this particular shape of bug bites

Because false is what a configuration object produces when a feature is off. Code that builds a query from settings will emit the key with a falsy value rather than leaving it out, so the default path through a sensible codebase is the one that fails. You will see it as an intermittent 502 that correlates with a checkbox nobody thought about.

The fix is to omit the key rather than send the negative. Build your query object and delete any key whose value is false before you send it.

The billing detail worth keeping

Both 502s were billed zero. The three successful calls were billed one each. That is consistent with what we have measured elsewhere: a provider that refuses with a visible HTTP error does not cost you, while a 200 that hands back an empty record is a different story. Failing loudly is the cheap kind of failure.

What does excluding staffing agencies actually do?

Not what the name suggests, and the real effect is better.

filter off   totalJobCount 245    uniqueJobsCount 207    duplicates 38
filter on    totalJobCount 234    uniqueJobsCount 215    duplicates 19

Read the second column again. Switching the filter on lowered the raw count by 11, which is what you would expect from a filter. It also raised the unique count by 8.

How a filter adds results

It does not, really. The two columns are counted differently, and what moved is the duplication. With agencies in the set, 245 raw postings collapsed to 207 unique ones, so 38 were repeats. With agencies out, 234 collapsed to 215, leaving 19.

The filter halved the duplicate load. That is the actual product: staffing agencies repost the same role, often several times and often with slightly different titles, and removing them is less about hiding eleven listings than about stopping the churn that makes a job feed unreadable.

What this means if you are building a feed

Turn it on by default. The cost is eleven postings in our sample, most of them duplicates of things you still have. The benefit is half the noise.

Measure your own ratio before you tune anything else. The gap between totalJobCount and uniqueJobsCount is a one-call diagnostic on how polluted a given query is. A query where the two numbers are close does not need much cleaning. A query where they are far apart is one where your de-duplication will earn its keep.

Do not treat either number as the job market. They are counts of postings, and one open role can be several postings while one posting can be several seats.

How do you run a job search and read the counts?

For agents

Grab an API key at app.monid.ai, then paste this to your agent and hand it the key:

set up https://monid.ai/SKILL.md

It learns the whole discover, inspect, run workflow itself. More in the agent quickstart.

For humans

npm install -g @monid-ai/cli
monid keys add -k <your-api-key> -l main

The call

monid run -p indeed -e /search_jobs \
  --query '{"query":"data engineer","location":"Austin, TX","fromage":"7","exclude_staffing_agencies":true}'

fromage is the maximum age in days and takes 1, 3, 7, 14 or 30. location accepts a city and state, a ZIP, or the literal remote. jt filters job type, explvl filters seniority, and start pages in increments of ten.

The one rule about the booleans

Send true or leave the key out. Never send false. If you are generating the query from a config object, strip falsy keys first:

const q = Object.fromEntries(
  Object.entries(raw).filter(([, v]) => v !== false && v !== undefined)
);

Reading the result

const { totalJobCount, uniqueJobsCount } = run.output.data;
const duplicateLoad = totalJobCount - uniqueJobsCount;

Log duplicateLoad per query. It is free, it comes with every call, and it tells you which of your saved searches is drowning before a human complains.

Give this to your agent

$Set up https://monid.ai/SKILL.md, and then use Monid to search Indeed for data engineer roles in Austin posted in the last week, exclude staffing agencies, and tell me the duplicate load and the ten companies posting most.

📖 See also LinkedIn Job Data: Which API Stays Fresh

Which endpoint should I use for which job?

EndpointWhat it doesInputReturnsLatency we sawBilling
indeed/search_jobsSearch live postingsquery, location, fromage, filtersPage state with the two countsAround 17 sPer call
indeed/get_job_detailsOne posting in fullA posting idThe full recordAround 6 sPer call
apollo/organizations/{organization_id}/job_postingsOne company's open rolesAn Apollo organization idThat company's postingsUnder 2 sPer call
hunterio/companies/findWho the employer isdomainFirmographicsAround 2 sPer call

Every row was verified with monid inspect on 2026-10-01 and the latency column is what we actually waited. Billing shape is given rather than figures, because shape drives design and current numbers live on monid.ai/tools.

The rows answer different questions and people reach for the wrong one regularly. The first is a market view: what is being hired for, across employers. The third is an account view: what this one company is hiring for, which is the shape you want for a buying signal rather than for a job board. Searching the market and filtering down to one company works and costs far more than asking about that company directly.

When should you not use this?

Four cases.

You want one company's hiring, not a market. Go at it by organization. A market search plus a filter is the expensive way to answer a question that has a direct endpoint.

You need salary data you can trust. Postings carry salary when the employer chose to publish it, which is a minority and a biased minority. A compensation question needs a compensation source.

You are counting open roles. Postings are not roles. One job can be posted several times, one posting can be hiring five people, and stale listings stay up. The two counts here measure postings, and the gap between them is exactly the warning that the mapping is not one to one.

You want to contact the applicants. Nothing here is about candidates, and the parts of this data that touch real people carry obligations that an API does not discharge for you.

And the disclosure: we resell these endpoints. The most valuable line in this post is that you should omit a parameter rather than send it as false, which is worth exactly one 502 to anyone, including people who never buy anything from us.

Conclusion

Three calls, one parameter, on 2026-10-01. Omitting exclude_staffing_agencies returned 245 postings. Setting it to true returned 234. Setting it to false returned a 502 from the provider on both attempts, and neither failed call was billed. If you build your query from a config object, strip the falsy keys before you send it, because false is what that code emits by default and it is the one value this filter will not take.

The filter itself is worth turning on, and not for the reason the name implies. It lowered the raw count by 11 while raising the unique count by 8, which means what really moved was the duplication: 38 repeated postings became 19. Staffing agencies repost the same role, and removing them halves the churn in a feed rather than hiding much real hiring.

The response shape is the last thing to know before you start. It is Indeed's own page-state object, not a jobs array, and the two fields worth reading are totalJobCount and uniqueJobsCount at the top level of data. Their difference is a free, per-query measure of how noisy a search is, and it is the number to log.

Free next step: run monid inspect -p indeed -e /search_jobs, make one call with the filter on and one with it omitted, and print both counts. The duplicate load is the whole argument. Start at monid.ai.

FAQ

Why would a boolean parameter reject false?

We can only tell you what we observed: true and omission both returned HTTP 200, while the JSON literal false returned a 502 from the provider on two separate attempts. A 502 is a gateway error, so the most likely story is that the value reaches a layer that expects the key to be present only when the filter is wanted, and something downstream chokes on the negative rather than ignoring it. What matters operationally is that this is the default output of ordinary code: a settings object with the feature off serializes to exclude_staffing_agencies: false, so the failing path is the one a careful codebase takes. Filter falsy keys out of the query before sending, and the problem disappears.

What is the difference between totalJobCount and uniqueJobsCount?

totalJobCount is how many postings matched. uniqueJobsCount is how many of them Indeed considers distinct after collapsing repeats of the same role. In our unfiltered run those were 245 and 207, so 38 postings were duplicates of something else in the same result set. With staffing agencies excluded they were 234 and 215, leaving 19. The gap is the useful quantity and it is free with every call: a wide gap means your query is pulling in a lot of reposted listings and your de-duplication matters, a narrow gap means the feed is already clean. Neither number is a count of open jobs, because one role can be several postings and one posting can be several seats.

How do you get the actual posting text?

The search response carries counts and page state, not bodies. For the content of a posting you call the detail endpoint with a posting id, which returns the full record and took about six seconds in our testing. That makes the cost shape obvious: a search is one call, and reading every result is one call per posting on top of it. For a market scan, pull the search, use the counts and whatever summary fields you need to rank, and only fetch details for the postings that survive your filter. Fetching every body for a 200-posting market before you have decided what you care about is the expensive mistake here.

Is this for recruiting or for market research?

Both work, but they want different calls. Market research lives on the search endpoint: query a role and a location, watch the counts over time, and track which employers appear. Recruiting and sales intelligence usually want one company at a time, and for that the organization-level postings endpoint is the right tool, because it answers directly instead of making you filter a market down to one name. There is a third use that people underrate: hiring is a buying signal. A company that just posted four roles for a stack you integrate with is a company with budget and a new problem, and that is an account-shaped question rather than a job-board one.

Last updated October 2026.

indeed apiindeed job search apijob search apihiring dataduplicate job postings