> ## Documentation Index
> Fetch the complete documentation index at: https://docs.crustdata.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Search Jobs

> Query the indexed Crustdata job dataset with structured filters, cursor-based pagination, sorting, field selection, and aggregations.

**Use this when** you need to find, segment, or count job listings across
the full Crustdata job dataset — for hiring-trend analysis, building target
account lists from recent hiring activity, monitoring specific roles, or
powering a dashboard.

<Note>
  **`metadata.date_added` is the job-posted date: the date the listing was
  added on the source portal.** Every query in this page that filters on
  `metadata.date_added` is asking about **job-posted time**, not Crustdata
  indexing time. Treat this endpoint as a query interface over Crustdata's
  indexed dataset, not as a direct poll of an employer-managed listings feed.
</Note>

```
POST https://api.crustdata.com/job/search
```

<Note>
  Replace `YOUR_API_KEY` in each example with your actual API key. All
  requests require the `x-api-version: 2025-11-01` header.
</Note>

## Job record mental model

Every job listing returned by Search Jobs is a single `Job` object with five
top-level groups:

* **`crustdata_job_id` and `job_details`** — The stable job id plus the
  posting's own metadata: title, category, URL, workplace type, and number
  of openings.
* **`company`** — The hiring company's firmographics at index time: basic
  info, headcount, followers, revenue, funding, locations, and competitors.
  No extra `/company/enrich` call required.
* **`location`** — The job's advertised location (city, state, country, raw
  string), not the company HQ.
* **`content`** — Full job description text. Use the `(.)` filter operator
  on `content.description` to keyword-hunt.
* **`metadata`** — Job timing metadata: `date_added` for the date the
  listing was posted (added on the source portal) and `date_updated` for
  the most recent refresh. These are your primary sort and filter fields
  for recent windows.

<Note>
  **Jobs ID cheat sheet.** The Jobs APIs use three id concepts — keep them straight:

  * **`crustdata_job_id`** — the Crustdata job identifier. Returned on every `Job`. Use it as your dedupe key.
  * **`company.basic_info.crustdata_company_id`** — the Crustdata company identifier returned on every `Job`.
  * **`company.basic_info.company_id`** (filter alias) — the dot-path used in `filters` and `aggregations.column` for indexed [Search Jobs](/job-docs/search/introduction). It points to the same integer as `company.basic_info.crustdata_company_id`. This alias is **not sortable**; for deterministic pagination, sort on `metadata.date_added` instead.

  When you `group_by` on `company.basic_info.company_id`, each bucket also returns `metadata.company_name`, `metadata.company_website_domain`, and `metadata.linkedin_id` for labeling.
</Note>

## At a glance

| Detail          | Value                                                                                                         |
| --------------- | ------------------------------------------------------------------------------------------------------------- |
| **Endpoint**    | `POST https://api.crustdata.com/job/search`                                                                   |
| **Auth**        | `Authorization: Bearer YOUR_API_KEY`                                                                          |
| **API version** | `x-api-version: 2025-11-01` header (required)                                                                 |
| **Body**        | **Required.** Send `{}` to match the whole dataset; every realistic query uses `filters`.                     |
| **Body keys**   | `filters`, `cursor`, `limit` (0–1000, default 20), `sorts`, `fields`, `aggregations` — all optional           |
| **Response**    | `{ "job_listings": [ Job, ... ], "next_cursor": string?, "total_count": integer?, "aggregations"?: [ ... ] }` |
| **Errors**      | `400` invalid request · `401` unauthorized · `500` internal                                                   |

### Guaranteed contract vs current behavior

| Topic                               | What it means                                                                                                                                                                                                                                               |
| ----------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Endpoint, HTTP method, auth headers | `POST /job/search`, bearer auth, `x-api-version: 2025-11-01`.                                                                                                                                                                                               |
| Request body shape                  | Optional `filters`, `cursor`, `limit`, `sorts`, `fields`, `aggregations`. `filters` is a `SearchCondition` or a `SearchConditionGroup`.                                                                                                                     |
| Response body shape                 | `{ "job_listings", "next_cursor", "total_count", "aggregations"? }`. Aggregation-only queries return `"job_listings": []`.                                                                                                                                  |
| Supported operators                 | `=`, `!=`, `<`, `=<`, `>`, `=>`, `in`, `not_in`, `is_null`, `is_not_null`, `(.)`, `[.]`. See [Filter operators](/job-docs/search/reference#filter-operators).                                                                                               |
| `limit` bounds and default          | Minimum `0`, maximum `1000`, default `20`. Set `limit: 0` when you only want aggregations.                                                                                                                                                                  |
| Error status codes                  | `400`, `401`, `500`.                                                                                                                                                                                                                                        |
| Indexed-field allowlist             | Only indexed fields can appear in `filters`, `sorts`, or `aggregations.field`. See [Reference](/job-docs/search/reference) for the detailed catalog, or [Common indexed fields](/job-docs/search/reference#common-indexed-fields) for the most-used subset. |
| Pricing and rate limits             | See [Pricing](/general/pricing) and [Rate limits](/general/rate-limits) for current numbers.                                                                                                                                                                |

<CardGroup cols={3}>
  <Card title="Examples" icon="list-filter" href="/job-docs/search/examples">
    Worked examples like "SDR hiring in mid-market", "companies that closed
    Series B", and full-text keyword hunts.
  </Card>

  <Card title="Pagination & sorting" icon="list-ordered" href="/job-docs/search/reference#pagination--sorting">
    Sorting, cursor-based pagination, field selection, and aggregations with
    `count` and `group_by`.
  </Card>

  <Card title="Reference" icon="book" href="/job-docs/search/reference">
    Common indexed fields, annotated `Job` example, full field catalog, id
    map, bucket metadata, errors.
  </Card>
</CardGroup>

## Request body

| Parameter      | Type                          | Required | Default    | Description                                                                                                                                            |
| -------------- | ----------------------------- | -------- | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `filters`      | object                        | No       | —          | Single `SearchCondition` or nested `SearchConditionGroup`. Omit to match all indexed jobs.                                                             |
| `cursor`       | string                        | No       | —          | Opaque cursor from a prior response's `next_cursor`. Pass it to fetch the next page with the same filter, sort, and field set.                         |
| `limit`        | integer                       | No       | `20`       | Rows per page. Min `0`, max `1000`. Use `0` for aggregation-only queries.                                                                              |
| `sorts`        | array of `SearchSort`         | No       | —          | Ordering rules. Each item has `field` (dot-path) and `order` (`asc` or `desc`). Sorts are applied in array order.                                      |
| `fields`       | string\[]                     | No       | all fields | Dot-paths to include in each returned job. Omit to return everything. Always specify `fields` in production for smaller payloads and faster responses. |
| `aggregations` | array of `AggregationRequest` | No       | —          | Roll-up queries. Supports `count` and `group_by`. Use with `limit: 0` if you only want counts.                                                         |

### Response body

| Field          | Type            | Description                                                                                                                                                                                     |
| -------------- | --------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `job_listings` | `Job[]`         | Matching job listings for the current page. Empty array `[]` when `limit` is `0` or when an aggregation-only query is made.                                                                     |
| `next_cursor`  | string or null  | Opaque cursor to fetch the next page. `null` when there are no more pages.                                                                                                                      |
| `total_count`  | integer or null | Total number of jobs matching the filter across all pages. Can be `null` for very broad queries where computing an exact total would be prohibitively expensive — never assume it is populated. |
| `aggregations` | array           | Aggregation results, present only when the request included an `aggregations` array.                                                                                                            |

### Rate limits and credits

<Note>Current pricing for indexed Jobs Search:</Note>

<Callout icon="coins" color="#5345e4">
  <strong>Pricing:</strong> <code>0.03 credits per result returned</code>. A
  request with no results does not consume credits.
</Callout>

<Note>
  Default `rate-limit` is 30 requests per minute. Send an email to
  [gtm@crustdata.co](mailto:gtm@crustdata.co) to discuss higher limits if
  needed for your use case.
</Note>

***

## Your first search: filter by company and title

Find the most recent Software Engineer listings at Stripe (filtered via the
`company.basic_info.company_id` alias, which maps to `crustdata_company_id = 631394`).

<CodeGroup>
  ```bash curl theme={"theme":"vitesse-black"}
  curl --request POST \
    --url https://api.crustdata.com/job/search \
    --header 'authorization: Bearer YOUR_API_KEY' \
    --header 'content-type: application/json' \
    --header 'x-api-version: 2025-11-01' \
    --data '{
      "filters": {
        "op": "and",
        "conditions": [
          { "field": "company.basic_info.company_id", "type": "=", "value": 631394 },
          { "field": "job_details.title",              "type": "=", "value": "Software Engineer" }
        ]
      },
      "fields": [
        "job_details.title",
        "job_details.url",
        "company.basic_info.name",
        "location.raw",
        "metadata.date_added"
      ],
      "sorts": [
        { "field": "metadata.date_added", "order": "desc" }
      ],
      "limit": 2
    }'
  ```

  ```json Response theme={"theme":"vitesse-black"}
  {
      "job_listings": [
          {
              "company": { "basic_info": { "name": "Stripe" } },
              "job_details": {
                  "title": "Software Engineer",
                  "url": "https://www.linkedin.com/jobs/view/4342849906"
              },
              "location": { "raw": "United States" },
              "metadata": { "date_added": "2025-12-10T15:57:16" }
          },
          {
              "company": { "basic_info": { "name": "Stripe" } },
              "job_details": {
                  "title": "Software Engineer",
                  "url": "https://www.linkedin.com/jobs/view/4302655996"
              },
              "location": { "raw": "Dublin, County Dublin, Ireland" },
              "metadata": { "date_added": "2025-09-23T13:52:55" }
          }
      ],
      "next_cursor": "H4sIAJKOKmoC_xXMSw4CIQwA0KsQ1rNowbbUqxgzgQKZhZE4n4Ux3t1x__I-_nW09T0veVv81XnixFYYFTg3JO2qQDUUNjGG3hjImqiVEFECYr1IN-ylBhW23P3k_DbW_bxuKJQ4EgkBwOQihiiCdD_JPvb8mG0cz7_E9P0BNbRcOogAAAA=",
      "total_count": 18
  }
  ```
</CodeGroup>

<Tip>
  **Always send `fields`.** The full `Job` schema is large (firmographics +
  location + description + metadata). Fetching only the dot-paths you need
  keeps responses small, fast, and predictable.
</Tip>

***

## Which search pattern should I use?

<Tabs>
  <Tab title="I want to explore the dataset">
    Use **Search Jobs**. You can slice millions of indexed job listings by
    company, title, category, location, date, or any other indexed field — and
    roll up results with `count` or `group_by` aggregations. Pair it with
    cursor-based pagination to walk through large result sets.
  </Tab>

  <Tab title="I only have a company domain or name">
    Call [`POST /company/identify`](/company-docs/identify/introduction) first to resolve
    the domain, name, or profile URL into a `crustdata_company_id`, then use
    that id in your Jobs Search filters.
  </Tab>

  <Tab title="I don't know the exact value to filter on">
    Use [Autocomplete](/job-docs/autocomplete/introduction) to discover valid
    values for a field — start typing a title, category, or company name and get
    back matching values you can drop straight into a Search Jobs filter.
  </Tab>

  <Tab title="I want only counts, not job rows">
    Pass `"limit": 0` and an `aggregations` array to Search Jobs. You get the
    total match count (and optional `group_by` buckets) without consuming any
    row payload. See
    [Aggregations](/job-docs/search/reference#aggregations) for examples.
  </Tab>

  <Tab title="I want a repeatable account list">
    Stay on Search Jobs and filter by indexed company, title, category,
    funding, location, or date fields. Use cursor pagination to walk the full
    result set and dedupe companies with
    `company.basic_info.crustdata_company_id`.
  </Tab>

  <Tab title="I want fresh live listings for one company">
    Use [Live Search](/job-docs/search/live-search) to fetch fresh job
    listings directly from a single company's source profile. Narrow by
    design — one company per call, no filters, no pagination.
  </Tab>
</Tabs>

***

## What's next

* **See worked queries** — see [Examples](/job-docs/search/examples) for SDR/BDR keyword hunting, mid-market filtering, funding-triggered queries, and aggregations.
* **Discover filter values** — use [Autocomplete](/job-docs/autocomplete/introduction) to find the exact title, category, or company-name values a filter accepts.
* **Paginate and aggregate** — see [Pagination & sorting](/job-docs/search/reference#pagination--sorting) for cursor pagination, sorting, field selection, and aggregations.
* **Look up fields** — see [Reference](/job-docs/search/reference) for the full `Job` catalog, id map, bucket metadata, and errors.
* **Fetch fresh listings for one company** — see [Live Search](/job-docs/search/live-search).
* **Inspect the full schema** — read the [OpenAPI reference](/openapi-specs/2025-11-01/introduction).
