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.
| Use | Where | What it matches |
|---|---|---|
q, title, description | Query parameters on GET /v1/jobs, /v1/jobs/count and in POST /v1/jobs/search bodies | q 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_advanced | Same places | A Boolean expression with AND, OR, NOT, groups, phrases and prefixes. Words may appear in any order. |
POST /v1/jobs/search/es_dsl | Its own endpoint | An 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)| Part | Syntax | Notes |
|---|---|---|
| Words | engineer | Letters, digits, _ and -. |
| Prefix | engineer:* | 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 "". |
| AND | a AND b or a & b | Both must match. |
| OR | a OR b or a | b | Either may match. |
| NOT | NOT a or !a | Excludes a word. See the rule below. |
| Groups | (a OR b) AND c | Up 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_advanced | Result |
|---|---|
senior engineer | 400, no operator between the words |
NOT manager | 400, no positive word |
senior AND engineer | 65,580 jobs |
nurse AND NOT manager | 125,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
| Clause | What it does |
|---|---|
match_all | Matches everything. Must be {}. |
bool | Combines clauses with must and filter (both mean AND), should, must_not and minimum_should_match (only the value 1). |
term, match, match_phrase | One field and one value, for example {"term":{"country":"US"}}. The value may also be written {"value": ...} or {"query": ...}. |
terms | One field and 1 to 10 values. Several values are allowed only for country, employment_type, currentness, include and exclude_source. |
range | One 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_string | Text 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:
| Alias | Same as |
|---|---|
remote_type | work_arrangement |
published_since | posted_since |
keyword_description | description |
company_name | company |
created_at | first_seen_since (range only) |
last_updated | updated_since (range only) |
application_active | true only; means application_check=available |
deleted | 0/false means current jobs, 1/true means not current |
Rules and limits
- Clauses nest up to 32 levels deep.
shouldaccepts onlytermormatchclauses on one field,countryoremployment_type, with up to 10 values. It means "any of these".must_notacceptssource(which becomesexclude_source) andexistsclauses 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,currentnessorincludelists, because that would change which jobs match. - Anything else (aggregations, scripts, fuzzy or wildcard clauses, unknown fields) returns
400 invalid_requestand the message names the clause or field it rejected. - A query that runs longer than 10 seconds returns
504 query_timeoutand 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