Overview
What a listener is, what you can call today, which release brings the rest, and the names a listener does not take
A listener watches keywords or a boolean topic on the platforms you choose, on a schedule you set, and will deliver each new mention it finds. Listeners ship in LRel2a. Today you can price and validate one: POST /v1/listening/streams?dry_run=1 takes the same body a create will take, answers with the monthly cost in credits and money, costs 0 credits and creates nothing.
curl -X POST "https://www.socialcrawl.dev/v1/listening/streams?dry_run=1" \
-H "x-api-key: YOUR_API_KEY" -H "Content-Type: application/json" \
-d '{"keywords": ["Acme", "Acme Cloud"], "platforms": ["reddit"], "schedule": {"every": "1h", "name": "Acme brand"}}'The answer holds the listener as it would be stored, each group's rule as a tree and in words (read_back), the estimate at data.estimate, the per-lane plan at data.plan, the use screen's verdict at data.screen, warnings and next_step. The quickstart walks through a full answer, and pricing shows how the estimate is computed.
What is a listener?
A listener holds one or more groups, each a boolean rule or plain keywords, with an optional context that says which sense of an ambiguous keyword you mean. It also holds the platforms to watch and a required schedule:
{
"groups": [
{ "id": "outage", "rule": "\"Globex Mobile\" AND (outage OR down OR \"no signal\")" },
{ "id": "pricing", "rule": "(\"Globex Mobile\" OR \"Globex Prepaid\") AND (price OR bill OR tariff)" }
],
"platforms": ["youtube", "reddit"],
"context": "Globex Mobile, the mobile network operator, and its Globex Prepaid plans. Not Globex Corporation or any other company named Globex.",
"schedule": { "every": "1h", "max_credits_per_month": 15000, "name": "Client B: Globex Mobile" }
}Top-level keywords or rule is the shorthand for one group. Send exactly one of keywords, rule and groups. schedule.every is one of 30m, 1h, 3h, 6h, 1d and 7d, and 30m works only when every chosen platform is read newest first (lanes). schedule.max_credits_per_month is a cap in credits, never money. The names are discover's wherever the meaning is the same, so a body you send to a discover schedule reads the same way here.
What ships when
Every route below is mounted today. A route of a later release answers a 400 not_released with details.release naming the release that brings it, and charges nothing. On the create route, next_step points at the dry run.
| Route | What it does | Release |
|---|---|---|
POST /v1/listening/streams?dry_run=1 | Price and validate a listener; creates nothing | LRel1, available now |
POST /v1/listening/streams | Create a listener | LRel2a |
GET /v1/listening/streams | List your listeners | LRel2a |
GET /v1/listening/streams/{id} | Read one listener | LRel2a |
PATCH /v1/listening/streams/{id} | Change a listener | LRel2a |
POST /v1/listening/streams/{id}/pause | Pause a listener | LRel2a |
POST /v1/listening/streams/{id}/resume | Resume a listener | LRel2a |
DELETE /v1/listening/streams/{id} | Delete a listener | LRel2a |
GET /v1/listening/streams/{id}/mentions | Read a listener's mentions | LRel2a |
GET /v1/listening/streams/{id}/runs | Read a listener's checks | LRel2a |
POST /v1/listening/preview | Run a listener body once over the last 24 hours | LRel2b |
GET /v1/listening/usage | Credits and checks per client, listener or platform | LRel2b |
There is no estimate route: POST /v1/listening/estimate answers 404 with did_you_mean naming the dry run. LRel2a also adds the listener events to the events feed and webhook endpoints. Comments under matching posts arrive in LRel3, and panels of accounts filtered by a topic in LRel4.
Which call fits the job?
Until listeners ship, a recurring watch on a topic is a discover schedule, and the new posts of an account you already know are a Monitor with webhook_rows: "new". The dry run answers what a keyword or boolean topic listener would cost. Choosing endpoints holds the one routing rule for every standing watch.
Names a listener does not take
These names answer a 400 not_offered, with docs_url pointing at this page and, where one exists, did_you_mean. Nothing is charged.
| Name sent | Send instead |
|---|---|
query | rule: a listener's boolean text. Discover's query is one phrase sent as written. |
rules | groups, each with an id and a rule or keywords |
refresh_interval_minutes | schedule.every |
budget | schedule.max_credits_per_month: divide a money budget by money_per_credit (pricing) |
match_in | Nothing: a listener matches a post's title and text together |
delivery.endpoint_ids | subject_ids on a webhook endpoint |
stream_id | subject_id on the events feed and webhook endpoints |
stream_ids | subject_ids on the events feed and webhook endpoints |
threads | Nothing: new comments of a known post are a Monitor with webhook_rows: "new" |
Some names belong to a later release and answer not_released naming it instead: accounts and schedule.purpose (LRel4), comments under matching posts (LRel3), label and questions (once their accuracy gate passes), sentiment (with label=stance) and tz (local-time schedules; a listener's slots and months are UTC).
Errors
Each refusal is free and carries details.reason, details.fields and details.next_step. Change the field it names rather than retrying.
details.reason | Status | What to do | Reference |
|---|---|---|---|
invalid_param | 400 | Send the field or value in did_you_mean, allowed_values or suggested_value | INVALID_REQUEST |
conflicting_params | 400 | Drop one of the fields in details.fields | INVALID_REQUEST |
not_released | 400 | Drop the field, value or route, or wait for details.release | INVALID_REQUEST |
not_offered | 400 | Send the name in did_you_mean, or drop it (table above) | INVALID_REQUEST |
interval_below_lane_minimum | 400 | Pick an every from allowed_values, or drop the platform whose lane details.lane names | INVALID_REQUEST |
unsupported_operator | 400 | Use the rule in nearest | INVALID_REQUEST |
plan_limit | 400 | Send fewer groups or search terms, within limit | INVALID_REQUEST |
budget_below_one_check | 400 | Ask the user to approve suggested_value, then send it in field | INVALID_REQUEST |
prohibited_use | 400 | Do not rephrase to pass. A dry run reports the verdict in data.screen instead; a create refuses it from LRel2a | INVALID_REQUEST |
judge_unavailable | 503 | Retry after Retry-After. A dry run never answers it: when the use screen cannot run, data.screen.result is unavailable | SERVICE_UNAVAILABLE |
The use screen checks what you declare in each group's rule or keywords and in context against the uses the Terms of Service section 7.2 forbid: monitoring a named private individual, a protest group or its organisers, or government or intelligence monitoring of people. On a dry run it returns its verdict in data.screen with a warning and creates nothing either way. A create or a change that declares such a use is refused with prohibited_use from LRel2a, and a preview from LRel2b.
