Getting Started

Job Search API Quickstart: Your First Query in 5 Minutes

CleanJobData Engineering

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 has id, title, location (a display string), locations (structured city, state and country IDs), company, published, application_url, has_remote, employment_type, experience_level, and salary_min, salary_max, salary_currency and salary_text when available.
  • pagination: limit, next_page and prev_page.
  • meta: includes filters_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:

GoalParameterExample
Keywordstitletitle=data analyst
Countrylocation (comma-separated ISO country codes)location=US,CA
Exact city, state or countrycity_id, state_id, country_idcity_id=5391959
Remote onlyremote=trueremote=true
Remote typeremote_typefully_remote, remote_country, remote_region, hybrid
Salary rangesalary=min,maxsalary=100000,150000
Only jobs with a disclosed salaryrequire_salary=true
Seniorityexperience_levelSE,EX (EN, MI, SE, EX)
Job typeemployment_typeFULL_TIME,CONTRACT
Recent postingsmax_age or published_aftermax_age=7d
One employercompany_website_urlstripe.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 limit is 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 429 that clears within a second or two. Watch the X-RateLimit-Remaining header to stay ahead of it.
  • Your daily (Trial) or monthly (paid) request quota is separate from the per-second limit.
  • On the Trial, application_url is 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