API DOCUMENTATION / QUERY LANGUAGE

Query language

The Boolean title and description syntax and the Elasticsearch-style query body, with the exact rules and real examples.

Three ways to search text

Pick the simplest one that does the job.

UseWhereWhat it matches
q, title, descriptionQuery parameters on GET /v1/jobs, /v1/jobs/count and in POST /v1/jobs/search bodiesq searches title and description together. title matches the title as one exact phrase. description matches the description. Quotes and asterisks are removed first.
title_advanced, description_advancedSame placesA Boolean expression with AND, OR, NOT, groups, phrases and prefixes. Words may appear in any order.
POST /v1/jobs/search/es_dslIts own endpointAn Elasticsearch-style JSON query. It is translated into the same filters, so it can express exactly what the filters can.

Boolean search with title_advanced and description_advanced

Send an expression in title_advanced to search titles, or in description_advanced to search descriptions. Each parameter takes one expression of up to 200 characters.

GET /v1/jobs/count?title_advanced=nurse%20AND%20NOT%20manager
GET /v1/jobs/count?title_advanced=engineer:*%20AND%20(senior%20%7C%20staff)
PartSyntaxNotes
WordsengineerLetters, digits, _ and -.
Prefixengineer:*Matches words that start with the text, such as engineering.
Phrase"customer success"Words next to each other. Write a quote inside a phrase as "".
ANDa AND b or a & bBoth must match.
ORa OR b or a | bEither may match.
NOTNOT a or !aExcludes a word. See the rule below.
Groups(a OR b) AND cUp to 12 levels of parentheses.

Operators are not case sensitive. NOT binds tighter than AND, and AND binds tighter than OR, so use parentheses when you mix them. An expression can have up to 64 tokens.

Two rules cause most 400 errors. First, put an operator between words: senior engineer is rejected, and senior AND engineer works. Second, every expression needs at least one positive word: NOT manager alone is rejected, and so is a OR NOT b. A bad expression returns 400 invalid_request with the message "must be a valid Boolean search expression".

Examples with real counts

Counts are current jobs on October 14, 2026, from GET /v1/jobs/count.

title_advancedResult
senior engineer400, no operator between the words
NOT manager400, no positive word
senior AND engineer65,580 jobs
nurse AND NOT manager125,432 jobs
engineer:* AND (senior | staff)80,776 jobs

Elasticsearch-style queries

POST /v1/jobs/search/es_dsl accepts a JSON body with a query and the usual paging and shaping options. Use it when you already have Elasticsearch queries or want a nested yes/no structure.

{
  "query": {
    "bool": {
      "filter": [
        {"term": {"country": "US"}},
        {"term": {"work_arrangement": "remote"}},
        {"range": {"pay_min": {"gte": 150000}}},
        {"exists": {"field": "application_url"}}
      ],
      "must": [{"match": {"title_advanced": "data AND (engineer OR scientist)"}}]
    }
  },
  "fields": ["job_id", "title", "employer_name"],
  "limit": 25
}

The body may contain these top-level keys: query (required), limit, cursor, sort, fields, include and description_format. The body can be up to 16,384 bytes and must be sent as application/json. Responses look like GET /v1/jobs responses.

Supported clauses

ClauseWhat it does
match_allMatches everything. Must be {}.
boolCombines clauses with must and filter (both mean AND), should, must_not and minimum_should_match (only the value 1).
term, match, match_phraseOne field and one value, for example {"term":{"country":"US"}}. The value may also be written {"value": ...} or {"query": ...}.
termsOne field and 1 to 10 values. Several values are allowed only for country, employment_type, currentness, include and exclude_source.
rangeOne field with gte, gt, lte or lt. gt and lt are treated as inclusive. Fields: posted_since/posted_until, first_seen_*, updated_*, curated_*, last_fetch_*, pay_min/pay_max, created_at, last_updated and application_check_since (lower bound only).
exists{"field": "pay"}. Fields: description, keyword_description, location, locations, pay, posting_url, application_url. Inside must_not it means "does not exist".
query_string, simple_query_stringText search: query and an optional fields list of title and/or description. Both fields (or none) search q; one field searches that field as a phrase. These do not use the Boolean syntax above; for Boolean text, match on title_advanced or description_advanced.

Fields and aliases

Any filter name works as a field in term, terms, match and match_phrase: for example country, state, city, work_arrangement, employment_type, source, company_id, pay_currency, title_advanced and description_advanced. The list of filters is on the job filters page. These aliases are also accepted:

AliasSame as
remote_typework_arrangement
published_sinceposted_since
keyword_descriptiondescription
company_namecompany
created_atfirst_seen_since (range only)
last_updatedupdated_since (range only)
application_activetrue only; means application_check=available
deleted0/false means current jobs, 1/true means not current

Rules and limits

  • Clauses nest up to 32 levels deep.
  • should accepts only term or match clauses on one field, country or employment_type, with up to 10 values. It means "any of these".
  • must_not accepts source (which becomes exclude_source) and exists clauses only.
  • Clauses are combined with AND. Two clauses that set the same single-value filter to different values are rejected, and so is an AND of different country, employment_type, currentness or include lists, because that would change which jobs match.
  • Anything else (aggregations, scripts, fuzzy or wildcard clauses, unknown fields) returns 400 invalid_request and the message names the clause or field it rejected.
  • A query that runs longer than 10 seconds returns 504 query_timeout and uses no records. Add a country, state, source or date range and try again.

Continue in the reference

Choose a search style, then try it with a count first.

Which route should I use? Code samples
All API documentation