LinkedIn Jobs API Filters: What's Server-Side and What's Client-Side (2026)
Search LinkedIn jobs in a browser and you get a wall of filters: experience level, remote, date posted, salary, job type. It is natural to assume the jobs API exposes the same knobs. It does not — and a lot of tutorials pretend otherwise, inventing parameters that quietly do nothing.
Here is the honest version. Serpent's LinkedIn jobs endpoint takes exactly three parameters: keywords, location, and start. That is the whole surface. The good news is you can still build a precise, filtered feed — you just do it with three techniques instead of a dozen query parameters: sharpen the keyword phrase, paginate and dedupe, and filter the rest client-side from the fields the API returns. This post shows all three, with real numbers from July 2, 2026, and it is equally clear about the filters you cannot reconstruct.
TL;DR: The jobs endpoint has three parameters — keywords (required), location, and start (pagination, +10). Precision comes from the keyword phrase (senior software engineer returned 19 of 20 senior-titled roles; software engineer returned almost none). Paginate with start and always dedupe by job_id — a 50-row pull had 1 duplicate. Filter date, company and location text client-side; seniority, remote and salary are not in the response. Jobs is $0.50/1K on Default, down to $0.025/1K at Scale. Public data, no login. Code below.
The three parameters (and no more)
A jobs request looks like this. Only keywords is required.
GET https://apiserpent.com/api/linkedin/jobs?keywords=software+engineer&location=United+States&start=0
Header: X-API-Key: YOUR_API_KEY
| Parameter | Required | What it does |
|---|---|---|
keywords | Yes | Free-text query matched against the posting (title and body text) |
location | No | A place name — country, region, or city |
start | No | Pagination offset: 0, 10, 20… returns 10 results per page |
Each result carries a fixed set of fields — and this list matters, because it is the raw material for every client-side filter you will build:
{
"job_id": "4416410577",
"job_title": "Data Engineer",
"company_name": "Hebbia",
"company_linkedin_url": "https://www.linkedin.com/company/hebbia",
"company_logo": "https://media.licdn.com/...",
"location": "New York, NY",
"job_posted_date": "2026-05-18",
"job_url": "https://www.linkedin.com/jobs/view/..."
}
Note what is not there: no experience level, no remote flag, no salary, no employment type. Keep that in mind — it defines the ceiling on what you can filter.
Precision lives in the keyword phrase
Because seniority is not a parameter, the instinct is to fetch broadly and filter for "senior" afterward. That barely works, because most postings do not put "senior" in the title in a way a substring match catches — and the response has no seniority field at all. The right move is to push the precision into the query.
Here is the difference, measured live on July 2, 2026 with a US location:
| Query | Unique roles | Titled senior / staff / lead |
|---|---|---|
keywords=software engineer | 49 | ~0 |
keywords=senior software engineer | 20 | 19 |
Searching software engineer and hoping to filter for seniority afterward left me with almost nothing usable. Searching senior software engineer returned 19 senior-titled roles out of 20. The lesson generalizes: encode intent — seniority, specialization, stack — into the keywords phrase, and let the server do the matching. It is both more accurate and cheaper than pulling a wide net and discarding most of it.
Pagination, and why you must dedupe
One call returns up to 10 jobs. To go deeper, increment start by 10. The catch: LinkedIn's public results reorder and overlap between requests, so the same job can appear on more than one page. If you concatenate pages naively, you get duplicates.
The fix is a one-line guard: track job_ids you have already seen. In a real five-page pull of software engineer on July 2, 2026, that guard mattered — 50 rows came back, 49 were unique, one was a duplicate. Small here, larger over deep pulls, and always worth doing.
import time, requests
API_KEY = "YOUR_API_KEY"
BASE = "https://apiserpent.com/api/linkedin/jobs"
def fetch_page(keywords, location="", start=0):
r = requests.get(BASE, headers={"X-API-Key": API_KEY},
params={"keywords": keywords, "location": location, "start": start},
timeout=90)
r.raise_for_status()
return r.json().get("data", {}).get("jobs", [])
def fetch_jobs(keywords, location="", pages=5):
"""Page through results and dedupe by job_id."""
seen, out = set(), []
for p in range(pages):
batch = fetch_page(keywords, location, start=p * 10)
for j in batch:
if j["job_id"] not in seen:
seen.add(j["job_id"])
out.append(j)
if len(batch) < 10: # last page reached
break
time.sleep(0.4)
return out
Filtering client-side: what the fields support
Now the real filtering. Every filter you build reads one of the returned fields, so the honest catalogue of what is possible is short and exact:
| Filter | Reads field | Reliable? |
|---|---|---|
| Recency (posted in last N days) | job_posted_date | Yes |
| By company | company_name | Yes |
| By location text (e.g. "New York") | location | Yes, as a string match |
| By title text (e.g. "backend") | job_title | Only if it's in the title |
Those four cover most real needs. Here they are as small, composable functions:
import datetime
def posted_within(jobs, days, today=None):
today = today or datetime.date.today()
out = []
for j in jobs:
try:
d = datetime.date.fromisoformat(j.get("job_posted_date", ""))
except ValueError:
continue # skip unparseable dates
if (today - d).days <= days:
out.append(j)
return out
def in_location(jobs, text):
text = text.lower()
return [j for j in jobs if text in (j.get("location") or "").lower()]
def title_has(jobs, term):
term = term.lower()
return [j for j in jobs if term in j["job_title"].lower()]
# compose them:
jobs = fetch_jobs("data engineer", "United States", pages=3)
recent_ny = in_location(posted_within(jobs, 14), "new york")
Running that on real July 2026 data, a three-page data engineer pull returned 30 unique roles, of which 19 were posted in the last 14 days and 6 were in New York — a "fresh New York data-engineer" feed, assembled from three server calls and a couple of list comprehensions.
The filters you cannot reconstruct
This is the part the "9 filters" posts leave out. Because the response has no field for them, some filters simply cannot be built reliably from this endpoint:
- Experience level as a facet — you can only approximate it through the keyword phrase (search "senior…"), not filter it after the fact.
- Remote / on-site / hybrid — there is no remote flag. Sometimes "Remote" appears in the
locationtext, but its absence does not mean the role is on-site, so a string match is a hint, not a filter. - Salary — not a structured field. When a salary exists it lives inside the posting's description text, which this search endpoint does not return.
- Employment type (full-time, contract, internship) — no field, and only occasionally present in the title.
If those dimensions are central to your use case, the honest answer is to encode what you can into keywords (for example, "contract data engineer") and accept that the rest is not available from a keyword search. Pretending otherwise just ships a filter that silently lets everything through.
Putting it together: a hiring-intent feed
Stack the three techniques and you have a maintainable pipeline: a sharp keyword query for precision, paginate-and-dedupe for coverage, and a couple of client-side filters for the final cut. From there, two neighboring guides take it further — aggregate the results over time to track hiring trends, or ingest and display them to build a job board. Both build directly on the fetch-and-dedupe core above.
Build your own feed. Create a key in under a minute — new accounts include 10 free Google searches to explore the platform. Grab a free key, drop it into the code above, and pull your first filtered results.
Pricing
Jobs search is priced per request, and one request returns a page of up to 10 postings — so a three-page pull is three requests.
| Tier | Unlock | Jobs price | Per request (≤10 jobs) |
|---|---|---|---|
| Default | None | $0.50 / 1,000 | $0.0005 |
| Growth | One $100 deposit (10× off) | $0.05 / 1,000 | $0.00005 |
| Scale | One $500 deposit (20× off) | $0.025 / 1,000 | $0.000025 |
Refreshing a 50-page feed hourly is 1,200 requests a day — about $0.60/day on Default, or $0.03/day at Scale. The same deposit unlocks the discount across every endpoint. See the full pricing page for the other LinkedIn datasets and the SERP APIs.
A note on compliance
Serpent's jobs endpoint accesses publicly available LinkedIn job postings only. There is no login, password, cookie, or credential in the flow — you send a keywords query and your Serpent key, and nothing else. That keeps the integration clean, but it does not automatically make every downstream use permitted.
Whether your specific use is allowed depends on LinkedIn's terms, your jurisdiction, and how you store and apply the data. Aggregating public job postings for a feed, a board, or hiring analysis is a common, well-trodden use case, but treat compliance as your own decision rather than something this article waves through. The API only ever touches public data and never authenticates as a user; you remain responsible for lawful use.
Three parameters, a precise jobs feed
Sharpen the keyword phrase, paginate and dedupe, filter the rest client-side — a complete recipe for querying LinkedIn jobs without inventing parameters that don't exist. Jobs search from $0.50/1K, dropping to $0.025/1K at Scale, pay-as-you-go with no subscription.
Get Your Free API KeyExplore: LinkedIn API · Social Media APIs · Pricing
FAQ
What parameters does the LinkedIn jobs API take?
Three: keywords (required), location, and start (the pagination offset). keywords is a free-text query matched against posting text; location is a place name; start is 0, 10, 20 and so on to page through results ten at a time. There is no server-side parameter for seniority, remote, salary, or job type — those are not query filters on this endpoint.
How do I filter LinkedIn jobs by seniority, remote, or date?
Seniority is best handled in the query itself: searching "senior software engineer" returns senior-titled roles far more reliably than searching "software engineer" and filtering afterward, because the response has no explicit seniority field. Date, company, and location text can be filtered client-side from the fields the API returns (job_posted_date, company_name, location, job_title). There is no remote flag or salary field in the response, so a reliable remote or salary filter is not possible from this endpoint alone.
How do I paginate the LinkedIn jobs API?
Increment the start parameter by 10 each call: start=0, start=10, start=20, and so on. Each page returns up to 10 jobs. Pages can overlap and reorder between calls, so you must dedupe by job_id as you accumulate — in a real five-page pull of 50 rows, one was a duplicate. Stop when a page returns fewer than 10 results or you hit the depth you need.
How much does the LinkedIn jobs API cost?
Jobs search is $0.50 per 1,000 requests on Default — and one request returns a page of up to 10 jobs. A single $100 deposit unlocks Growth at 10× off ($0.05/1,000) and a single $500 deposit unlocks Scale at 20× off ($0.025/1,000). It is pay-as-you-go with no subscription, and new accounts include free calls to test.
Do I need a LinkedIn login to use the jobs API?
No. You send a keywords query and your Serpent API key in an X-API-Key header. The jobs endpoint accesses publicly available LinkedIn job postings only — there is no LinkedIn login, cookie, or OAuth flow on your side. You remain responsible for using the data lawfully and within LinkedIn's terms.



