Job catalog, id
semantics, aggregation bucket metadata, null behavior, and errors.
For worked examples, see Examples. For sorting,
pagination, field selection, and aggregations, see
Pagination & sorting.
Jobs ID cheat sheet. The Jobs APIs use three id concepts — keep them straight:
crustdata_job_id— the Crustdata job identifier. Returned on everyJob. Use it as your dedupe key.company.basic_info.crustdata_company_id— the Crustdata company identifier returned on everyJob.company.basic_info.company_id(filter alias) — the dot-path used infiltersandaggregations.columnfor indexed Search Jobs. It points to the same integer ascompany.basic_info.crustdata_company_id. This alias is not sortable; for deterministic pagination, sort onmetadata.date_addedinstead.
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.Filter grammar
Every filter describes which individual job rows to keep. The API checks each job listing against your filter independently — it never groups or combines rows before filtering. There are two building blocks:All-words operators (
(.)) work fine in AND. Because (.) checks for
individual words — not a contiguous substring — a query like (title (.) "Software Development") AND (title (.) "Software Engineer") matches any
title containing all three words “Software”, “Development”, and “Engineer”
(e.g. “Software Development Engineer”).Single condition
AND / OR group
Array-field filters and grouping
Filtering on array fields
Filtering on array fields
When you filter on a string-array field like
This matches any company whose
company.basic_info.industries, the condition is satisfied if any
element of the array matches.For example:industries array contains that
exact string. Use (.) to match words within any element.Grouping by array fields
Grouping by array fields
When you
group_by on an array field, each array element becomes
its own bucket key. A company in two industries contributes one
count to each of the two industry buckets — so the sum of bucket
counts can exceed total_count for array fields.Filter operators
Use the table below to pick the righttype for each condition. Every
operator works on indexed fields only.
Geographic radius filters (geo_distance / geo_exclude)
The value for geo_distance and geo_exclude is an object describing a
center point and a radius:
Jobs within 25 km of San Francisco:
geo_exclude
with other conditions in an and group:
Common indexed fields
These are the indexed fields most often used infilters, sorts, and
aggregations.field. This table is a summary of the most common paths,
not an authoritative catalog. For the deeper field catalog — including id
semantics, null handling, and bucket metadata — see the full
Field reference below.
Company id filter alias. The filterable field path uses the short alias
company.basic_info.company_id, but the response shape returns the same
integer at company.basic_info.crustdata_company_id. They point to the same
value. See Jobs IDs: a quick map.- Job details
- Company basic info
- Company firmographics
- Location
- Content, metadata, IDs
Sending a filter on a non-indexed field returns
500 with Unsupported columns in conditions: ['...']. Sending an unsupported group_by field
returns a similar 500 listing every supported aggregation field.Field reference
This section covers the return shape, id semantics, aggregation bucket metadata, and the most important indexed field catalogs in one place.Annotated full Job example
The code fence below uses
jsonc because it includes inline // comments
for annotation. Strip the comments before sending it to a strict JSON
parser.Jobs IDs: a quick map
Aggregation bucket metadata
When yougroup_by on company.basic_info.company_id, each bucket carries
a metadata object whose keys use bucket-specific names rather than the
Job response dot-paths:
Job identifiers
Job details (job_details.*)
job_details.source — posting source platform. Every job carries
exactly one of these values: professional_network (listings from
professional networking platforms), or the platform of the company job
board that published the posting — workday, smartrecruiters, greenhouse,
workable, lever, icims, ashby, rippling, or yc. Filter with
= / in-style operators or group_by on it; it is not sortable.job_details.employment_type — employment type declared by the
posting. One of full_time, part_time, contract, intern, or
temporary. Currently populated for roughly 85% of postings published on company
job boards, and null for now when source is professional_network — treat missing values as “not declared”, not as
a specific type. Filterable and groupable; not sortable.Search-only fields.
job_details.source and
job_details.employment_type are available on POST /job/search only —
Live Search does not return them.Company basic info (company.basic_info.*)
company.basic_info.company_id and
company.basic_info.crustdata_company_id refer to the same integer. Use the
short alias in filters and aggregations.field. The response shape
writes the value under crustdata_company_id.Company firmographics
Headcount (company.headcount.*)
Followers (company.followers.*)
Revenue (company.revenue.*)
Funding (company.funding.*)
Competitors and company locations
Job location (location.*)
The city, state, and country fields are derived by geocoding the
raw location string, so they carry normalized place names rather than the
raw text’s wording. city can be an empty string "" when the raw
location resolves to an area broader than a city (for example
"San Francisco Bay Area" geocodes to state: "California" with an
empty city).
Country values are geocoded and normalized.
location.country carries
normalized full country names ("United States", "United Kingdom"). A
small share of rows still carries a residual variant such as
"United States of America". When completeness matters, match both with
in, or pre-discover the exact indexed values by running a group_by on
location.country.Content (content.*)
Metadata (metadata.*)
Null, blank, and sparse field behavior
MostJob fields are nullable in the spec and can legitimately be absent
or empty.
- Null or missing — the field is not present on a given
Job. - Blank string
""— the field was present but had no indexable value (common forjob_details.workplace_type). Treat blank as “unspecified”, not as the same thing as null. - Sparse nested objects —
company.funding,company.revenue, andcompany.competitorsare often missing for smaller or private companies. is_null/is_not_nulloperators filter on null or missing fields directly — send"value": null(thevaluekey is required).
Errors
Every error — including
401 — uses the same nested envelope:
{ "error": { "type", "message", "metadata" } }. Branch on error.type
rather than string-matching message.
Pagination & sorting
How to paginate, sort, select fields, and aggregate results in Search Jobs. For worked examples, see Examples. For filter grammar, operators, and the full field catalog, see Reference.Replace
YOUR_API_KEY in each example with your actual API key. All
requests require the x-api-version: 2025-11-01 header.Sorting
sorts is an ordered array. Each item has a field and order ("asc"
or "desc"). Sorts apply in array order — the first sort is the primary
key, the second breaks ties, and so on.
Sortable fields
The following indexed fields are verified sortable:crustdata_job_idmetadata.date_addedmetadata.date_updatedcompany.headcount.totalcompany.followers.countcompany.revenue.estimated.lower_bound_usdcompany.funding.last_fundraise_datecompany.funding.num_funding_roundsrelevance— only valid when a text query is present infilters(maps to the text-match score); sorting on it without a text condition returns400
- Newest postings first —
{ "field": "metadata.date_added", "order": "desc" } - Biggest companies first —
{ "field": "company.headcount.total", "order": "desc" } - Most followed companies first —
{ "field": "company.followers.count", "order": "desc" } - Most funding rounds first —
{ "field": "company.funding.num_funding_rounds", "order": "desc" }
Pagination
Pagination is cursor-based. Each response returns anext_cursor (or
null when you reach the end). To fetch the next page, resend the original
request body with cursor set to the previous next_cursor.
1
Fetch the first page
Omit
cursor and set limit to your page size (max 1000).2
Walk forward
Take
next_cursor from the response and pass it back as cursor in the
next request. Keep filters, sorts, and fields identical — if you
change them, the cursor becomes meaningless.3
Stop when `next_cursor` is null
A
null cursor means you’ve reached the end of the result set.Consistency between pages
Best-effort, not strict snapshot. A
cursor is consistent with respect to the filter, sort, and field
selection you sent on the first page, so the same query will keep
paging forward over a coherent result stream. However, because the
underlying indexed dataset is continuously updated, new jobs indexed
between page requests can cause minor drift in
total_count and in the
exact position of individual rows. Treat pagination as best-effort,
not a strict snapshot.For bulk exports where every row matters:- Constrain your filter to a bounded date window (for example
metadata.date_added >= 2025-01-01AND< 2025-07-01) so newly indexed jobs outside the window do not affect the walk, and - Re-run the full walk periodically and diff against the prior
snapshot using
crustdata_job_idas the dedupe key.
Dataset freshness and lifecycle
What the indexed Jobs dataset represents. The Search Jobs dataset
is a rolling index of job listings discovered from the web, refreshed
on an ongoing basis. Each row has:
metadata.date_added— when Job was posted.
metadata.date_added window (for example, within the last 30 days) and pair it with the hiring
company’s firmographics. For alerting or repeated exports, keep your
date windows bounded and dedupe rows with crustdata_job_id.Date filter semantics
Dates and timezones. When you pass a date-only value like
"2025-01-01", the backend interprets it as 2025-01-01T00:00:00 in
UTC. Ranges using => are inclusive of the boundary and < is
exclusive, so "metadata.date_added" >= "2025-01-01" AND
< "2025-07-01" covers every listing indexed between Jan 1 (inclusive)
and Jul 1 (exclusive) in UTC. Pass full timestamps like
"2025-01-01T08:00:00" when you need finer precision.Fetch page 2
Field selection
Usefields to return only the dot-paths you need. The top-level groups
are crustdata_job_id, job_details, company, location, content,
metadata. You can request:
- A whole group —
"company"returns everycompany.*sub-object. - A sub-object —
"company.basic_info"returns only the basic info block. - A single field —
"company.basic_info.name"returns just the name.
Aggregations
Aggregations let you roll up results without returning individual job rows. Setlimit: 0 when you only want aggregation output. Two types are
supported:
count— returns the total number of jobs matchingfilters.group_by— buckets the results byfieldand returns per-bucket counts.
AggregationRequest schema
Each
AggregationResponseItem echoes type and field, then carries:
value(integer) — populated forcountaggregations. The total match count.buckets(array) — populated forgroup_byaggregations. Each bucket has akey,count, and ametadataobject whose keys depend on the grouped field. See Aggregation bucket metadata.
aggregations[] in the same order you sent them.
Count all Engineering jobs
Top companies indexing “Software Engineer” listings (bounded window)
Groupable fields
group_by.field is restricted to the following indexed fields:
company.basic_info.company_idcompany.basic_info.crustdata_company_idcompany.basic_info.industriescompany.basic_info.primary_domaincompany.funding.last_round_typecompany.headcount.rangecompany.locations.countryjob_details.categoryjob_details.employment_typejob_details.sourcejob_details.titlejob_details.workplace_typelocation.country
500 with Unsupported aggregation field: '...'. Supported: ....
What’s next
- Search Jobs — back to the main Search page.
- Examples — SDR/BDR keyword hunting, mid-market filtering, funding-triggered queries, and aggregations.
- Pagination & sorting — sorting, pagination, field selection, and aggregations.
- OpenAPI reference — the formal schema for every request, response, and error.

