Replace
YOUR_API_KEY in each example with your actual API key. All
requests require the x-api-version: 2025-11-01 header.Request body
config
Response body
Create returns the whole watch, so you can confirm what was stored before the baseline run starts.Rate limits and credits
Pricing: the first run is a free baseline (a sample of up to 5
matches), then
2 credits per new post delivered. A run that finds
nothing new costs nothing.- Rate limit: 10 requests per minute on
/watch/social_post/search. Each watch path keeps its own budget. See Rate limits. - Baseline limit: 500,000 posts. A broader filter is refused at creation, before anything runs.
- Watched filter: fixed once created. To change what you watch for, create a new watch.
Your first watch: a topic, checked every six hours
Response trimmed for clarity.
"fields": null means the standard post payload, and "filters" comes back exactly as you sent it.
Understanding the response
id: the handle for everything else. Pause, reconfigure, cancel, and read run history all take it.status:activethe moment the watch is created. A run issuspendedby the system rather than half-delivered when your balance is too low.last_run_at:nulluntil the baseline run completes, usually within seconds.filters,sorts,fields,on: echoed back exactly as stored. These are fixed at creation;status,config, andnotificationsare the mutable parts.
How a watch runs
1
Create the watch
POST your filters, a config, and one or more notifications channels.
The response returns the whole watch, including its id.2
Baseline run (free)
The first run happens within seconds and delivers a free sample of up to 5
posts so you can confirm the setup and payload shape. No credits are
charged. Behind that sample, the baseline reads your entire match set to
record what already exists.
3
Recurring runs
On the schedule you set (
every_hours), the watcher re-runs your filters and
delivers posts that are new since the previous run, up to
max_results_per_run. You are charged 2 credits per post delivered.Baseline limit: 500,000 posts. The baseline run only delivers 5, but it
reads your entire match set to record what already exists. That read is
capped at 500,000, and we check it when you create the watch, so a broader
filter comes back
400 and you narrow it before anything runs. Later runs
deliver whatever newly matches, bounded by config.max_results_per_run.What a notification looks like
When a run has matches, wePOST this body to each channel. Posts arrive under results.added, each in the post record shape projected to the watch’s fields. This one came from a test send, so it carries metadata.test; a scheduled run sends the same body without it.
Delivery body
Response trimmed for clarity: one post is shown, and its
text is cut.summary.truncated is true when the run matched more posts than max_results_per_run allowed. With the default overflow_policy: "redeliver" the overflow is held back and arrives on a later run rather than being lost.
Use a test send to wire a receiver before a scheduled run fires. It builds the same body against your current match set, marks it with metadata.test, and delivers nothing to your credit balance.
Filters
A post watch takes the same filter tree as Social Post Search: one condition, or anand/or group of them, over dot-path fields.
Two operators do most of the work on text:
content_type, the virtual filter on the live Social Post Search API, is
not a watch filter. A watch that names it comes back
Unknown filter field at filters: 'content_type'. Filter on has_video
instead.What the author fields carry
The fields underactor are filled according to who wrote the post, and the two
halves never overlap. Combining one from each half under and matches nothing,
so the watch is created and then stays silent forever.
So
actor.title with actor.industry returns nothing, and neither does
actor.title with actor.location.country. Pick the half that matches the
author you want, and add actor.actor_type when you want to be explicit about
it. actor.company_name works on both, though it is mostly people: it is the
author’s current employer, not the posting company.
Manage a watch
A post watch takes the same management paths as every other discovery watch. Pause, reconfigure, read run history, and cancel with theid from create.
status, config, and notifications are the mutable parts. filters, sorts, fields, and on are fixed at creation; PATCHing any of them returns 400.
A
config PATCH is validated as a whole block, so repeat the watch’s current
trigger alongside the key you are changing. The keys you do send are merged
into the stored config rather than replacing it.Examples
Worked recipes you can copy, paste, and adapt. Each one is a full working request, verified against the live API. Swap the notification channel for your own. For the operator list and field catalog, see the Social Post Search reference.Funding announcements from companies in a market
Funding announcements from companies in a market
Company pages announcing a raise, filtered to the US and to posts that got some traction.
[.] matches the phrase literally, so it does not drift onto posts that merely contain the words separately.Request
actor.location.country is filled on company authors only, so this watch
would deliver nothing if you swapped actor_type to person. See
What the author fields carry.Hiring posts from the people at a company
Hiring posts from the people at a company
actor.company_name selects posts by people who work somewhere, rather than posts from the company page itself. Pair it with a keyword to keep the feed on topic.Request
A hashtag feed with an engagement floor
A hashtag feed with an engagement floor
hashtags matches case-insensitively against any tag on the post, and in accepts a list. The reaction floor keeps a busy tag down to the posts people actually engaged with.Request
Posts from people in a title band
Posts from people in a title band
actor.title is the author’s current job title, so this watch follows what a role is saying rather than what one company is saying.Request
(.) tolerates typos and word order, so it also catches “Head of Platform
Engineering”. Use = when you want the exact title string and nothing else.Posts that mention a company you track
Posts that mention a company you track
mentions.company_id takes a Crustdata company id and matches posts that link to that company, whoever wrote them. This is the reliable way to follow mentions, because it survives a company renaming its page.Request
Get the id from Company Search or
Company Identify.
631394 is Stripe.
The actor_type condition drops the company’s own posts so you see what
other people say about it.Posts the people at a company reacted to
Posts the people at a company reacted to
reactors.* filters on who reacted rather than who posted, which surfaces what an account is paying attention to. This watch delivers posts somebody at Stripe reacted to.Request
Conditions on
reactors.* inside one and group bind to the same
reactor. See Nested-array
matching
for how that works and how to express cross-element requirements.Launch posts that carry a video
Launch posts that carry a video
content_type is a search-only filter, so a watch selects attachments through has_video, document.title, article.title, or poll.question instead.Request
Error handling
A social post watch answers every failure the same way, whether you are creating a watch, editing one, testing delivery, or reading run history.
Every watcher failure comes back in the same envelope the search and enrich APIs
use, whatever the status code:
type is the machine-readable code you branch on, message is the sentence you
can show a user, and metadata carries per-problem detail when there is any.
Read error.message, not the top level: there is no detail key and no
non_field_errors key on any watcher response.
Every problem in one response
Validation collects every failure in one pass, so a payload with three things wrong costs one round trip rather than three.metadata carries one
entry per problem, and message is those entries joined in order:
400 - three problems, one response
The shape of the body is checked before the rules that read across fields, so
a payload that is both malformed and semantically wrong can still take two
round trips: the first response names the shape problems, the second names
the rules. Two is the worst case.
Exact responses
Authorization header and a wrong key give the same 401 body, so
the response does not tell you which of the two happened.
A path that does not route never reaches the watcher and so never gets this
envelope.
POST /watch/widget/search returns the gateway’s
{"error_msg": "404 Route Not Found"}, and an entity path on a dataset that
has no entity watcher returns an HTML 404. Branch on the status code first,
then parse.Pricing
You are charged only for posts actually delivered, never for a run that finds nothing new. See Pricing for the full catalog.
What to do next
- Run a one-off search instead: Social Post Search returns the current match set in one call.
- Look up operators and fields: see the Social Post Search reference for the full filter catalog.
- Choose how records travel: see Payload delivery for inline versus linked NDJSON.
- Watch another dataset: Person, Company, and Job discovery watchers work the same way.

