List Jobs

Returns a page of normalized job listings matching your filters, each with title, location, salary, company, and more (see the schema below). Lists are paginated with opaque cursors rather than page numbers: the response includes pagination.limit, pagination.next_page, and pagination.prev_page. There is no page query parameter.

How list pagination works
To fetch the next or previous window, send the same filters and sort_by as before, and pass the token from the last response's next_page or prev_page back on the following request. The direction is baked into the token itself, not into which name you send it as — cursor, next_page, and prev_page are three interchangeable names for the same query parameter; whichever you use, the API reads whatever token you passed and moves in the direction it encodes.
application_url may be omitted on some plans
On the Solo MCP developer plan, application_url is left out of every job in the response — use GET /jobs/:id/apply to unlock it for a specific job. Every other plan gets it directly, as shown below.
Freshness: published_after vs max_age
If you pass published_after (ISO 8601), the API does not apply max_age for the same request. Use published_after for a strict calendar lower bound; use max_age for a rolling window (e.g. 7d).
TypeScript

Full copy-paste types (including the FilterApplied union) live on the API types (TypeScript).

Allowed tokens for fields, exclude_fields, and extra_fields: id, title, location, locations, application_url, published, has_remote, remote_type, is_active, expired_at, language, employment_type, salary_min, salary_max, salary_currency, salary_text, experience_level, experience_levels, company, description

GET
https://api.cleanjobdata.com/jobs
Default summary response (fastest)
GET
https://api.cleanjobdata.com/jobs?extra_fields=description
Includes full job description

Query Parameters

Search & Keywords

titlestring
Search by job title or keywords. Supports full-text search.
Example: software engineer
searchstring
Alias of title (same behavior).
Example: software engineer
sort_bystring
Sort order. Values: published (default), relevance (requires title).
Example: relevance

Geographic Filtering

Pro Tip: Use the Geo Suggest API to find IDs for 100% accuracy.

city_idnumber
Filter by specific city ID. Comma-separated.
Example: 5391959
state_idnumber
Filter by specific state ID. Comma-separated.
Example: 5332921
country_idnumber
Filter by specific country ID. Comma-separated.
Example: 233
locationstring
When no city_id, state_id, or country_id are provided: comma-separated ISO 3166-1 alpha-2 country codes (e.g. US,GB,DE). Invalid tokens are skipped.
Example: US,CA
remoteboolean
Set to true for remote-only jobs (filters on has_remote). Requires the exact string true — 1 or yes are not accepted here, unlike some other boolean flags.
Example: true
remote_typestring
Narrow remote jobs further. Values: fully_remote, remote_country (restricted to a country), remote_region (restricted to a region), hybrid.
Example: fully_remote

Company

company_namestring
Filter by company name (substring match).
Example: Stripe
employer_idstring
Filter by one or more company IDs (the id returned by the Companies API). Comma-separated, up to 100. Not something you look up ahead of time — most job responses include employer_id at the top level (older rows without a resolved ATS employer can have it null), so capture it off any job that has one and reuse it.
Example: a1B2c3, x9Y8z7
company_website_urlstring
Filter by the company's own website domain (not the job board's hosting domain). Matches by registrable domain, so a bare domain or full URL both work. This is the reliable way to scope to a specific employer's real site — see domain below for a narrower, application-URL-derived alternative.
Example: stripe.com
domainstring
Exact match against each job's application_url domain — not the same as company_website_url above. For jobs hosted on an ATS (Applicant Tracking System — the platform an employer uses to post and manage listings, e.g. Greenhouse, Lever), this is often the ATS's own hosting domain (e.g. boards.greenhouse.io) rather than the employer's real site, so it's a narrower tool: useful when you specifically know the domain a job's application link resolves to, not a general company-scoping filter.
Example: acme.com

Field selection (list only)

Default list responses return a summary set of columns (no full description). Use the parameters below to trim or extend the payload. Unknown field names are ignored.

fieldsstring
Comma-separated allowlist: response includes only these columns (must be from the allowed set). Replaces the default summary projection.
Example: id,title,company,published
include_fieldsstring
Legacy alias of fields — same behavior. Prefer fields for new integrations.
Example: id,title,company
extra_fieldsstring
Comma-separated columns to add on top of the default summary (e.g. description). Silently ignored if fields or include_fields is also present in the same request — those take priority and extra_fields never gets applied on top.
Example: description
exclude_fieldsstring
Comma-separated columns to remove from the resolved field set (applied after extra_fields). Same caveat as extra_fields: silently ignored whenever fields or include_fields is also set.
Example: description,company
countboolean
Set to true to include pagination.count: the total number of jobs matching your filters (not just the current page). Costs an extra query — omit unless you need it.
Example: true

Compensation & Seniority

salarystring
Filter by salary range. Format: min,max (e.g. 100000,150000) — or just min alone (e.g. 100000) for a lower-bound-only filter with no upper cap.
Example: 100000,150000
min_salarynumber
Legacy lower-bound-only alias. Prefer salary=min for new integrations.
Example: 100000
require_salaryboolean
By default, salary/min_salary filters also include jobs with no disclosed salary. Set true to only return jobs with a verified salary meeting your minimum.
Example: true
experience_levelstring
Values: EN (Entry), MI (Mid), SE (Senior), EX (Executive). Comma-separated.
Example: SE,EX
employment_typestring
Values: FULL_TIME, PART_TIME, CONTRACT, INTERN, TEMPORARY, FREELANCE, APPRENTICESHIP, VOLUNTEER, PER_DIEM, OTHER. Comma-separated.
Example: FULL_TIME,CONTRACT

Freshness & Pagination

published_afterstring
ISO 8601 timestamp: only jobs published at or after this instant. When set, max_age is not applied on the same request.
Example: 2024-05-01T00:00:00.000Z
max_agestring
Rolling window on published date: positive integer with optional unit h (hours), d (days), w (weeks). A bare number is treated as days (e.g. 7 equals 7d). Omit if using published_after.
Example: 24h, 7d, 1w, or 7
created_max_agestring
Rolling window on ingestion time instead of published date — same format as max_age. Useful for sync jobs: a listing whose published date is old but only recently finished processing still matches this, which published_after/max_age can miss.
Example: 24h, 7d, 1w, or 7
limitnumber
Results per request. Default 20. Max depends on your plan: 20 on the Trial tier, 100 on Starter/Pro/Enterprise. Requests above your tier's max are silently clamped down to it.
Example: 50
cursorstring
Opaque token from the previous response’s pagination.next_page or pagination.prev_page.
Example: (value from prior response)
next_pagestring
Same token as pagination.next_page, sent as a query parameter to load the next window.
Example: (value from prior response)
prev_pagestring
Same token as pagination.prev_page, sent as a query parameter to load the previous window.
Example: (value from prior response)
include_expiredboolean
By default, only is_active jobs are returned. Set to true to also include closed/expired listings — most useful scoped to one employer (with company_website_url or employer_id) for a 'recent roles including closed ones' view.
Example: true

Response Schema

The body includes data (jobs), pagination (see below), and meta.

Success Response Example
{
"data": [1 items],
"pagination": {3 keys},
"meta": {2 keys}
}

The Envelope

dataarray
An array of Job objects matching your filters.
paginationobject
Always has limit. next_page and prev_page are opaque cursor strings when more results exist in that direction; otherwise null or omitted. Pass the string back on your next request under any of these three (equivalent) query param names: cursor, next_page, or prev_page — the direction is determined by the token itself, not by which name you send it as.
metaobject
Includes query_time_ms and filters_applied: an array describing which filters were honored, with one object per logical filter (geo IDs may produce one row per ID). Shapes vary by key — see the FilterApplied union on the API types page.
meta.filters_applied shapes
  • title, experience_level: key, value, display_label.
  • salary: key, min, max, display_label.
  • published_after, max_age: key, value, display_label (value is hours since cutoff for max_age).
  • city_id / state_id / country_id: key, kind, name, display_label, plus the matching *_id field.
  • location (ISO2 codes from the location query param): key, value, display_label (code).
  • Remote-only filter (from remote=true): row key is remote_only; value is always true.
  • company_name, remote_type: key, value, display_label.

employer_id, company_website_url, domain, created_max_age, include_expired, and count are applied to the query but do not currently produce a filters_applied row.

The Job Object

Primary
idnumber
Unique identifier for the job listing.
titlestring
The official job title.
locationstring | nullNullable
A human-readable location string (e.g., 'San Francisco, CA; Remote').
locationsarray | nullNullable
Structured location rows resolved from `location` — city/state/country IDs, coordinates, and a per-row remote flag. See the Location object below.
publishedstring
ISO 8601 timestamp of when the job was published.
descriptionstring | nullNullable
Full HTML description. Note: This field is excluded from list results by default unless 'extra_fields=description' is requested.
application_urlstring
The direct URL to apply for the job.
employment_typestring | nullNullable
Values: FULL_TIME, PART_TIME, CONTRACT, INTERN, TEMPORARY, FREELANCE, APPRENTICESHIP, VOLUNTEER, PER_DIEM, or OTHER.
experience_levelstring | nullNullable
Primary seniority level: EN (Entry), MI (Mid), SE (Senior), or EX (Executive).
has_remoteboolean
Whether the job has any remote option at all. For finer-grained remote type, see remote_type below.
remote_typestring | nullNullable
Present when has_remote is true. Values: fully_remote, remote_country, remote_region, hybrid.
is_activeboolean
Indicates if the job listing is currently active.
expired_atstring | nullNullable
ISO 8601 timestamp of when the job listing expired (if applicable).
languagestring | nullNullable
The primary language of the job listing (e.g., 'en', 'de').
salary_minnumber | nullNullable
Minimum annual or hourly salary (if available).
salary_maxnumber | nullNullable
Maximum annual or hourly salary (if available).
salary_currencystring | nullNullable
ISO currency code (e.g., USD, EUR).
salary_textstring | nullNullable
Human-readable salary string (e.g., '$120k - $150k').
experience_levelsarray
An array of all detected experience levels for this job.
companyobject | nullNullable
The employer's company profile — name, logo, website, socials, headcount, and more. See the Company object below.
employer_idstring
This employer's stable ID. Not requestable via fields/extra_fields, but always present — capture it from any job to filter future /jobs queries to this exact employer via the employer_id param.

The Company Object

Nested

Verified metadata about the hiring organization, stored on the job object. Not every key is present for every listing—treat optional fields as nullable/missing.

company.namestring
Legal name of the company.
company.logostring | nullNullable
URL to the company's logo image.
company.website_urlstring | nullNullable
Primary website URL.
company.descriptionstring | nullNullable
A brief overview of the company.
company.industrystring | nullNullable
The primary sector (e.g., 'Fintech', 'SaaS').
company.employee_countstring | nullNullable
Estimated size range (e.g., '11-50', '5001-10000').
company.linkedin_urlstring | nullNullable
Direct link to the company's LinkedIn profile.
company.twitter_urlstring | nullNullable
Direct link to the company's Twitter/X profile.
company.github_urlstring | nullNullable
Direct link to the company's GitHub organization.
company.facebook_urlstring | nullNullable
Direct link to the company's Facebook page.
company.instagram_urlstring | nullNullable
Direct link to the company's Instagram profile.
company.youtube_urlstring | nullNullable
Direct link to the company's YouTube channel.
company.teamarray
An array of key team members. Each object contains:
  • name: Full name of the team member.
  • title: Job title or role (nullable).
  • linkedin_url: Link to their professional profile (nullable).
  • photo_url: URL to their profile photo (nullable).

The Location Object

Array Item

Each item in the locations[] array is a fully resolved geographic entity.

locations[].kindstring | nullNullable
The resolution granularity of this location.
  • city_state_country: Fully resolved.
  • city_country: City and country resolved.
  • state_country: State and country resolved, no specific city named.
  • city, state, or country: Single level resolved.
locations[].display_labelstring | nullNullable
Formatted name, e.g., 'San Francisco, CA, US'.
locations[].city_namestring | nullNullable
The name of the city.
locations[].city_idnumber | nullNullable
The unique ID for the city (if available).
locations[].state_namestring | nullNullable
The name of the state or province.
locations[].state_idnumber | nullNullable
The unique ID for the state (if available).
locations[].state_codestring | nullNullable
Short code for the state (e.g., 'CA').
locations[].country_namestring | nullNullable
The name of the country.
locations[].country_idnumber | nullNullable
The unique ID for the country (if available).
locations[].country_codestring | nullNullable
ISO 3166-1 alpha-2 country code.
locations[].timezonestring | nullNullable
The IANA timezone ID (e.g., 'America/Los_Angeles').
locations[].is_remoteboolean
Indicates if this specific location entry represents a remote setting.
locations[].is_primaryboolean
True if this is the primary location for the job.
Interactive Testing
To see live data without writing code, use our Live API Playground or download one of our Starter Templates.