technographics.technologies.name resolves to the company dataset and job_details.title is a job field, so the watch delivers companies running Snowflake that are hiring a data engineer. The job condition is the one a single-dataset company watch could not express, because open roles are not carried on company records.
Request body
Nothing changes. A cross-dataset watch is created the same way as any other discovery watch, on the create endpoint for the dataset you want delivered.filters, config, and notifications those pages document. Only the contents of conditions differ.
Response body
Also unchanged. Delivery, deduplication, per-run caps, and the payload shape are identical to a single-dataset watch, and the create response is the same watch object.Rate limits and credits
2 credits per company, 0.5 per person,
0.5 per job. Reading another dataset to narrow your filters is
free, and the baseline run is free.- Rate limit: 10 requests per minute on each watch path, counted separately. See Rate limits.
- Baseline limit: 500,000 records your filter may match at creation.
- Cross-dataset breadth: 1,000,000 companies a cross-dataset condition may cover.
What you can combine
Examples
Limits
Why this exists
A job record already carries some of its company’s attributes, so a job watch can filter on funding stage, headcount, industry, and head office without leaving the job dataset. Nothing marks those fields as special from the outside, which made the boundary invisible: filtering a job watch on funding stage worked, and filtering the same watch on headcount growth failed. Cross-dataset filters put every filter field on all three search endpoints within reach of any discovery watch.What you can combine
400 at creation:
actor.actor_type says whether it points at a person or at a company. The
watch reads that first, so a person id can never match a company that happens to
share the number. Write the condition with ordinary person or company fields, for
example experience.employment_details.current.title or headcount.total.How a run works
Field names decide the dataset
headcount.total is a company field,
job_details.title a job field,
experience.employment_details.current.title a person field. Every name is
listed in the Company Search,
Person Search, and
Job Search references.A handful of names appear in more than one vocabulary.
technographics.technologies.name, technographics.technologies.category,
and technographics.technologies.super_category are on both company and job
records, and indexed_at and updated_at are on all three. Your watch’s own
dataset always wins those, so a job watch filtering on
technographics.technologies.name reads the job record’s own copy rather than
querying the company dataset. Note that the two copies mean different things:
on a job record the field is posting-scoped, covering only the technologies
named in that posting, while on a company record it describes the whole
organization. See the
Job Search reference.We query the other dataset first
Those IDs narrow your watch
Grouping
Conditions from the same dataset sitting side by side in a group are answered together, as one question. In this company watch:job_details.title and location.country are both job fields, so the watch asks for one posting that is an engineering role and is in the United States. It does not return a company with an engineering role in India and a separate sales role in the United States. Writing the two job conditions inside their own and group means the same thing, so you can group them for readability without changing the result.
Under or the two forms also agree: a posting matching A, or a posting matching B, is a posting matching A or B.
When a cross-dataset condition matches nothing
An empty result counts as false and travels up through your groups. Underand, the whole branch is false and the run delivers nothing. Under or, that branch drops out and the rest of the watch still runs.
Timing and deduplication
A single-dataset watch narrows each run to records reindexed since the previous run. That shortcut does not hold once another dataset is involved: a company posts a job on Tuesday, the job record is new, and the company record is untouched. A cross-dataset watch re-evaluates the full current match set on every run instead. What stops a record arriving twice is the record of what has already been sent, not the time window. Each match is delivered once, ever, for a given watch. If it drops out of the set and comes back later, the watch stays quiet.Records from the other dataset
The payload carries your watch’s own records and nothing else. A company watch tells you Acme matched. It does not tell you which posting or which person put Acme there, because only IDs cross between datasets, never records. That is also why you are not charged for the dataset you filtered on. To get those records, call that dataset’s own search or enrich API with the ID from the delivered record and the same conditions you filtered on.job_details.title contains data engineer delivers company 8294878. This gets the posting that put it in the feed:
company.basic_info.crustdata_company_id, and a delivered person record carries it as crustdata_company_id on each current employer.
These are ordinary search and enrich calls, billed at their normal rate. See Pricing.
What is not supported
Negation across datasets
We reject the operators that negate a single record when they sit on a cross-dataset condition:!=, not_in, (!), not_contains, and geo_exclude.
400:
basic_info.year_founded != 2020 on a company watch is accepted, because there it means what it says.
Counting
You can ask whether at least one matching record exists. You cannot ask for a number. “Five or more open engineering roles”, “doubled their postings this quarter”, and “three or more people left” are all out of reach.Growth on the other dataset
“Companies whose engineering headcount grew” works, because that figure sits on the company record. “Companies whose engineering postings grew” does not, because job records carry no growth figures.On a realtime watch
Everything on this page describesfilters, which is the index tier only. A
realtime watch (config.is_realtime: true) refuses filters outright and reaches
another dataset through narrowing_filters instead, a block named for the dataset
it asks. Each dataset’s watcher page covers that split:
Person,
Company,
Job, and
Social Post.
Two things change once you move a cross-dataset watch to the realtime tier.
You name the dataset instead of letting the field name pick it. The flat list
on this page works because the watch reads each field name and routes it. A
narrowing block is one query against one index, so you write the block name and
then that dataset’s own vocabulary inside it.
The denormalized spelling does not carry over. company.headcount.range is
the company’s size as copied onto a job document, and that copy exists only for a
posting the index already holds. A realtime posting is never that, so the field is
refused with nowhere to move to:
basic_info.employee_count_range under narrowing_filters.company instead,
which asks the company index the same question directly.
What a realtime watch may narrow on is fixed per dataset: a person watch on
person or company, a post watch on person or company, and a company or job
watch on company alone. A condition about a job or a post is a condition about
the record the live search just returned, so no index can answer it.
“Hiring now” is the one cross-dataset condition that survives as-is: a job
condition on metadata.date_added with => maps to a real facet the live search
has, so it stays in realtime_filters on a company watch.
Limits
config.max_results_per_run.
A cross-dataset condition under and narrows the set before the baseline limit is measured, so a watch can pair a very broad condition on its own dataset with a narrow one from another. This is accepted even though headcount.total > 10 alone matches over four million companies:
or there is no narrowing, so the same pair is rejected:
metadata is empty.
The second ceiling is measured on the cross-dataset conditions alone, so a condition on your own dataset does not help you past it. A job watch filtering on basic_info.year_founded > 2020 reaches roughly four million companies, and adding a job title and a country still fails, because neither is a company condition. The 400 opens Your company filters cover more than 1,000,000 companies. and then suggests adding another company filter: a location, a date, or a more specific title.
Adding a second company condition, such as headcount.total => 50, is what makes it acceptable.
If we cannot reach the other dataset at creation time, the watch is not created and you can retry:
Examples
Worked recipes you can copy, paste, and adapt. Every one is verified against the live API. Post each to the create endpoint for the dataset you want delivered, and swap the notification channel for your own.Companies hiring for a role
Companies hiring for a role
POST /job/search with the delivered crustdata_company_id and the same job conditions. See Records from the other dataset.Companies hiring away from a rival
Companies hiring away from a rival
Sales hiring at fast-growing US companies
Sales hiring at fast-growing US companies
Three datasets in one watch
Three datasets in one watch
"First time" watches, like a first ML hire
"First time" watches, like a first ML hire
Jobs at a kind of company
Jobs at a kind of company
Jobs at recently founded companies
Jobs at recently founded companies
Jobs at companies employing an ex-competitor
Jobs at companies employing an ex-competitor
People at a kind of company
People at a kind of company
People at fast-growing companies
People at fast-growing companies
People whose employer is hiring for a role
People whose employer is hiring for a role
Founders at recently funded companies
Founders at recently funded companies
Pull the record from the other side
Pull the record from the other side
crustdata_company_id and the same job conditions the watch matched on.Pricing
Credits work exactly as they do on a single-dataset watch. You pay for records delivered to you, at your watch’s own rate: 2 credits per company, 0.5 per person, 0.5 per job. Reading another dataset to narrow your filters is free, and the baseline run is free. See Pricing for the full table.What to do next
- Build a company feed: see the Company Discovery Watcher for the create body, the notification shape, and company recipes.
- Build a person feed: see the Person Discovery Watcher.
- Build a job feed: see the Job Watcher.
- Check a condition before you watch it: run it through the other dataset’s search endpoint and confirm
total_countis not zero.

