Skip to main content
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. This page walks you through the basics: the job record mental model, your first search, the response shape, and choosing a search pattern, then folds in worked example recipes you can copy, paste, and adapt. For filter grammar, operators, and the full field catalog, see Search reference.
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.
Replace YOUR_API_KEY in each example with your actual API key. All requests require the x-api-version: 2025-11-01 header.
Pulling every listing for a set of companies? Submit an asynchronous batch job-listings search instead — up to 10 companies per job, with the complete result set delivered as a single downloadable file.

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, source platform, employment 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 [.] on content.description to find an exact keyword or brand, and (.) for multi-word descriptive matching ((.) is typo-tolerant, so a single keyword can match lookalike words).
  • 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.
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. 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.

At a glance

Guaranteed contract vs current behavior

Examples

Worked examples like “SDR hiring in mid-market”, “companies that closed Series B”, and full-text keyword hunts.

Pagination & sorting

Sorting, cursor-based pagination, field selection, and aggregations with count and group_by.

Reference

Common indexed fields, annotated Job example, full field catalog, id map, bucket metadata, errors.

Request body

Response body

Rate limits and credits

Current pricing for indexed Jobs Search:
Pricing: 0.03 credits per result returned. A request with no results does not consume credits.
Default rate-limit is 30 requests per minute. Send an email to gtm@crustdata.co to discuss higher limits if needed for your use case.

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).
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.

Which search pattern should I use?

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.

Examples

Worked recipes you can copy, paste, and adapt. Each example is a full working request. For the core walkthrough (job record mental model, your first search, choosing a search pattern), see the sections above. For filter grammar, operators, and the full field catalog, see Search reference. For sorting, pagination, field selection, and aggregations, see Pagination & sorting.
The long forms "Sales Development Representative" and "Business Development Representative" use (.) (all-words match), but the short acronym "SDR" uses [.] (exact phrase). Short acronyms with (.) can overmatch — e.g. "SDR" would also match "USDR". Use [.] for 2–3 character acronyms.
Because filters operate on individual job rows, you cannot ask for “companies with both roles” in a single query. Instead, run two bounded-window aggregations and intersect the company ids client-side.
Watch out for short-acronym false positives. (.) is an all-words match, so a query of "AE" in job_details.title can also match unrelated titles. Prefer [.] for 2–3 character acronyms.
1

Query 1 — companies with Software Engineer listings

2

Query 2 — companies with Account Executive listings

3

Intersect the company ids client-side

Combine an inclusive headcount range with keyword search on the title field.
Country values are not normalized. location.country can appear as "USA", "United States", or "United States of America". The in array below covers the three most common forms, but for full coverage you should first run a group_by on location.country and collect the exact bucket keys present in your dataset slice.
job_details.source identifies the platform each posting comes from — professional_network, or the platform of the company job board that published it (workday, smartrecruiters, greenhouse, workable, lever, icims, ashby, rippling, yc). Use in to keep only postings from specific job-board platforms, and pair it with job_details.employment_type to keep only the declared employment type.
job_details.employment_type is currently populated for most postings published on company job boards and is null for now when source is professional_network, so combine it with a source filter as shown above. See the Job details catalog for both value vocabularies.
Group by job_details.source to see how many matching listings each source platform contributes. Each bucket key is one source value. Sent without filters, it profiles the whole dataset; add filters to profile any slice.
Buckets only appear for source values with at least one matching listing, so a filtered query can return fewer than 10 buckets. Counts drift as the dataset is refreshed.
To get just the total number of jobs matching a filter, send a count aggregation with limit: 0. No job rows are returned, so the call consumes no per-result credits, and the total comes back in both total_count and the aggregation value.
For a simple total, read total_count. The count aggregation returns the same number in aggregations[0].value and is handy when you send it alongside other aggregations in one request.
Use in on job_details.title when you want listings whose title is exactly one of a known set — for example a normalized list of sales titles across a target account list. This is the precise alternative to fuzzy (.) all-words matching.
in matches the whole title exactly (case-insensitive), so "Sales Development Representative" will not match "Senior Sales Development Representative". When you want partial or word-level matches instead, use (.) for all-words or [.] for an exact contiguous phrase.
When your filter includes a text operator ((.)), you can sort by relevance to surface the strongest title matches first instead of the most recent.
relevance needs a text query. Sorting by relevance without a (.) (or [.]) text condition in your filter returns 400 with Unsupported columns in conditions: ['relevance (no text query present)']. Pair relevance with at least one text filter, or sort by metadata.date_added instead.

What to do next

  • Discover filter values — use Autocomplete to find the exact title, category, or company-name values a filter accepts.
  • Paginate and aggregate — see Pagination & sorting for cursor pagination, sorting, field selection, and aggregations.
  • Look up fields — see Reference for the full Job catalog, id map, bucket metadata, and errors.
  • Fetch fresh listings for one company — see Live Search.
  • Inspect the full schema — read the OpenAPI reference.