Job Search API Quickstart: Your First Query in 5 Minutes
TL;DR
Sign up for a free key (no card), send it as a Bearer token to GET /jobs, and filter with title, location (country codes or city/state/country IDs), remote, salary, experience_level and max_age. Responses hold a data array, a pagination object with a next_page cursor, and meta.filters_applied. The Trial allows 300 requests a day and 20 jobs per response; use fields to trim the payload and the cursor to page. This guide uses the same parameters documented in the Jobs API reference.
This guide takes you from nothing to a working job search request, then shows the handful of parameters that cover most real use cases. The full parameter list is in the Jobs API reference.
1. Get a Key
Sign up for the free Trial: no credit card, 300 requests per day (resetting daily), and up to 20 jobs per response. Keep the key server-side. Never put it in browser code. Prefer to explore first? The playground runs real queries with no code.
2. Send Your First Request
The API is at https://api.cleanjobdata.com, and you authenticate with a Bearer token in the Authorization header.
curl "https://api.cleanjobdata.com/jobs?title=software%20engineer&location=US&limit=5" \
-H "Authorization: Bearer $CLEANJOBDATA_API_KEY"In JavaScript (Node 18+):
const res = await fetch(
"https://api.cleanjobdata.com/jobs?" +
new URLSearchParams({ title: "software engineer", location: "US", limit: "5" }),
{ headers: { Authorization: `Bearer ${process.env.CLEANJOBDATA_API_KEY}` } }
);
const { data, pagination, meta } = await res.json();
console.log(data[0].title, data[0].company.name);In Python:
import os, requests
res = requests.get(
"https://api.cleanjobdata.com/jobs",
params={"title": "software engineer", "location": "US", "limit": 5},
headers={"Authorization": f"Bearer {os.environ['CLEANJOBDATA_API_KEY']}"},
)
payload = res.json()
print(payload["data"][0]["title"])3. Read the Response
The response has three parts:
data: the jobs. Each hasid,title,location(a display string),locations(structured city, state and country IDs),company,published,application_url,has_remote,employment_type,experience_level, andsalary_min,salary_max,salary_currencyandsalary_textwhen available.pagination:limit,next_pageandprev_page.meta: includesfilters_applied, which tells you exactly which of your filters the API understood, so your UI can echo them back.
Here is the shape, trimmed to one job (from the Jobs API reference; values are illustrative):
{
"data": [
{
"id": 40652107,
"title": "Senior Full Stack Engineer (AI/Node.js)",
"location": "San Francisco, CA; Remote",
"locations": [
{
"kind": "city_state_country",
"city_id": 5391959,
"city_name": "San Francisco",
"state_id": 5332921,
"state_name": "California",
"country_id": 233,
"country_name": "United States",
"country_code": "US",
"is_primary": true,
"lat": 37.7749,
"lng": -122.4194
}
],
"company": { "name": "Stripe", "website_url": "https://stripe.com", "registrableDomain": "stripe.com" },
"published": "2024-05-07T12:00:00.000Z",
"application_url": "https://stripe.com/jobs/listing/123",
"has_remote": true,
"remote_type": "fully_remote",
"is_active": true,
"employment_type": "FULL_TIME",
"experience_level": "SE",
"salary_min": 180000,
"salary_max": 240000,
"salary_currency": "USD",
"salary_text": "$180,000 - $240,000 per year",
"employer_id": "a1B2c3"
}
],
"pagination": { "limit": 20, "next_page": "eyJ2IjoxLCJz…", "prev_page": null },
"meta": {
"query_time_ms": 45,
"filters_applied": [{ "key": "title", "value": "engineer", "display_label": "engineer" }]
}
}The company object can carry more fields than shown here, such as a logo, description and social links. The reference lists them all.
By default the list leaves out the full description to keep responses small. Add extra_fields=description when you need it.
4. Add Filters
These parameters cover most searches:
| Goal | Parameter | Example |
|---|---|---|
| Keywords | title | title=data analyst |
| Country | location (comma-separated ISO country codes) | location=US,CA |
| Exact city, state or country | city_id, state_id, country_id | city_id=5391959 |
| Remote only | remote=true | remote=true |
| Remote type | remote_type | fully_remote, remote_country, remote_region, hybrid |
| Salary range | salary=min,max | salary=100000,150000 |
| Only jobs with a disclosed salary | require_salary=true | |
| Seniority | experience_level | SE,EX (EN, MI, SE, EX) |
| Job type | employment_type | FULL_TIME,CONTRACT |
| Recent postings | max_age or published_after | max_age=7d |
| One employer | company_website_url | stripe.com |
A combined example:
curl "https://api.cleanjobdata.com/jobs?title=frontend%20engineer&experience_level=SE&remote=true&salary=120000&sort_by=relevance&max_age=14d&limit=20" \
-H "Authorization: Bearer $CLEANJOBDATA_API_KEY"Two details that trip people up: remote must be the exact string true, and a salary filter also includes jobs with no disclosed salary unless you add require_salary=true. sort_by=relevance requires a title.
Look Up City and State IDs
location takes country codes only. For a specific city or state, use the Geo Suggest API to find the ID, then pass it as city_id or state_id:
curl "https://api.cleanjobdata.com/geo/suggest?q=London&kinds=city" \
-H "Authorization: Bearer $CLEANJOBDATA_API_KEY"See Using Geo Suggest with the Jobs API for a full flow.
5. Trim the Payload
Use fields to return only what your UI renders:
curl "https://api.cleanjobdata.com/jobs?title=designer&fields=id,title,company,published,application_url&limit=20" \
-H "Authorization: Bearer $CLEANJOBDATA_API_KEY"If you also pass fields, extra_fields and exclude_fields are ignored, so choose one approach per request.
6. Paginate
Lists use cursors, not page numbers. Take pagination.next_page from a response and send it back with the same filters:
let cursor;
do {
const params = new URLSearchParams({ title: "engineer", limit: "20" });
if (cursor) params.set("cursor", cursor);
const res = await fetch(`https://api.cleanjobdata.com/jobs?${params}`, {
headers: { Authorization: `Bearer ${process.env.CLEANJOBDATA_API_KEY}` },
});
const page = await res.json();
// ...use page.data
cursor = page.pagination.next_page;
} while (cursor);Keep sort_by and your filters identical between pages. Add count=true only when you need the total number of matches, since it costs an extra query.
7. Handle Limits and Errors
- The Trial's
limitis capped at 20. Higher values are silently clamped to your plan's maximum (50 on Starter, 100 on Pro). - A per-second rate limit returns a
429that clears within a second or two. Watch theX-RateLimit-Remainingheader to stay ahead of it. - Your daily (Trial) or monthly (paid) request quota is separate from the per-second limit.
- On the Trial,
application_urlis a cleanjobdata.com redirect that resolves to the employer's page. Paid plans return the direct employer URL.
The errors reference lists every status code and the error format.
Next Steps
- Build something with it: Build a Job Board in an Afternoon.
- Pick filters for your product: Choosing the Right Filters.
- Keep your own database in sync: Syncing Job Data In Depth.
- Compare alternatives: Best Job APIs in 2026.