# Reference (/docs/listening/reference) Every listener route, body field, mentions filter, refused name and error on one page This page lists everything a listener takes and returns. To get started, read the [overview](/docs/listening/overview.md) and run the [quickstart](/docs/listening/quickstart.md) first. ```bash title="cURL" curl "https://www.socialcrawl.dev/v1/listening/streams?status=active" \ -H "x-api-key: YOUR_API_KEY" ``` ## Routes | Route | What it does | | --- | --- | | `POST /v1/listening/streams?dry_run=1` | Prices and checks a listener body. Costs 0 credits and creates nothing | | `POST /v1/listening/streams` | Creates a listener | | `GET /v1/listening/streams` | Lists your listeners, filtered by `status`, `name` or `client` | | `GET /v1/listening/streams/{id}` | Reads one listener | | `PATCH /v1/listening/streams/{id}` | Changes a listener | | `POST /v1/listening/streams/{id}/pause` | Pauses a listener | | `POST /v1/listening/streams/{id}/resume` | Resumes a listener | | `DELETE /v1/listening/streams/{id}` | Deletes a listener | | `GET /v1/listening/streams/{id}/mentions` | Reads the mentions a listener kept | | `GET /v1/listening/streams/{id}/runs` | Reads a listener's checks: what each one read, kept, billed and refunded | | `POST /v1/listening/preview` | Runs a listener body once and shows what its first check would keep. Billed like the same API calls. A preview of several pages can take 20 seconds or more, so set a client timeout of at least 60 seconds | | `GET /v1/listening/usage` | Credits and checks per client, listener, group or platform, as JSON or CSV | New mentions also arrive as `mention.created` events on the [events feed](/docs/events.md) (`GET /v1/events`) and as signed pushes to your [webhook endpoints](/docs/webhook-endpoints.md). Every write (create, change, pause, resume, delete, preview) takes an `Idempotency-Key` header ([idempotency](/docs/idempotency.md)). There is no estimate route: the dry run is the estimate. ## Listener body The create, the dry run, the preview and a change all take this body. Send exactly one of `keywords`, `rule`, `groups` or, for an [account panel](/docs/listening/overview.md#account-panels), `accounts`. | Field | What it does | Default | | --- | --- | --- | | `keywords` | One group whose keywords are its searches, each matched as written. A keyword of several words is searched as a quoted phrase. At most 100 keywords of 500 characters each | | | `rule` | One group as boolean text: `AND`, `OR`, `NOT`, `"quoted phrases"` and brackets, up to 2,000 characters | | | `groups` | Several topics in one listener, at most 500. Each has a unique `id` and a `rule` or `keywords`, and can set its own `context`, `exclude_terms`, `platforms`, `languages`, `country` and `tags` | | | `accounts` | An account panel: up to 300 accounts, each `platform:handle` (`reddit:nasa`, `youtube:@NASA`) or a profile URL. Needs a topic and `schedule.purpose` | | | `author_list_id` | In place of `accounts`: the id of a list stored with `POST /v1/prism/discover/inputs`, read once when the listener is created | | | `platforms` | The platforms to watch. Google News is `news`, and `x` is accepted for `twitter` ([lanes](/docs/listening/lanes.md)) | | | `search_in` | What to search on each platform: `posts`, `comments` on Reddit, `blog` and `news` on Naver | `posts` | | `context` | What kept posts must be about, naming the sense of an ambiguous keyword. 3 to 2,000 characters | | | `schedule` | How often the listener checks, its caps and its name. Required ([below](#schedule)) | | | `languages` | ISO 639-1 codes. A post whose language is unknown is kept | | | `country` | ISO 3166-1 codes of the posts or authors to keep | | | `exclude_country` | ISO 3166-1 codes of the posts or authors to drop | | | `unknown_country` | `keep` or `drop` a post whose country is unknown when `country` or `exclude_country` is set | `keep` | | `exclude_terms` | Drops posts that contain any of these terms | | | `exclude` | Account keys (`:`) or evidence ids to drop | | | `sponsored` | `exclude` drops posts marked as ads or paid partnerships, `only` keeps only those, `keep` turns the filter off | `exclude` | | `relevance` | `filter` drops posts the relevance check rejects, `score` keeps every match with its score, `off` does not check. The check is included in the price | `filter` | | `relevance_definition` | `mentions` keeps posts that name the topic, `about` keeps posts mainly about it | `mentions` | | `comments` | `new_posts` also reads the comments under each post the listener newly keeps, on Reddit ([lanes](/docs/listening/lanes.md#comments-under-matching-posts)) | `off` | | `metadata` | Up to 20 string labels for your own use, returned unchanged. `client` groups [usage](#routes) | | | `draft` | `true` saves the listener without running it | `false` | | `new_listener` | `true` creates a second listener even when an identical one exists | `false` | ### Schedule | Field | What it does | Default | | --- | --- | --- | | `every` | How often the listener checks: `30m`, `1h`, `3h`, `6h`, `1d` or `7d`. `30m` works only when every chosen platform is read newest first | | | `max_credits_per_month` | The monthly cap in credits. A check that would pass it does not run, and the listener pauses until the next UTC month or a higher cap. An account panel can pass it by the second pages of one check ([pricing](/docs/listening/pricing.md#what-does-an-account-panel-cost)). Required on a create made with an API key. A dry run without it still answers and recommends one | | | `max_credits_per_run` | The most one check may spend | `data.estimate.max_credits_per_slot` | | `name` | The listener's name, up to 120 characters | | | `purpose` | Required on an account panel: `business`, `brand`, `creator` or `organisation` | | | `start_at` | When the first check fires instead of at create, at most 90 days ahead | | An account holds up to 50 listeners. Schedules and months are in UTC. ## Mentions route `GET /v1/listening/streams/{id}/mentions` takes these filters. Pages are read with `cursor`: send the `pagination.next_cursor` of the page before until `pagination.has_more` is `false`. | Filter | What it keeps | | --- | --- | | `since`, `until` | Posts published at or after, or before, this time | | `seen_since`, `seen_until` | Posts the listener first saw at or after, or before, this time | | `group` | Mentions of this group id. A group a change removed still reads for the 30-day keep. An id the listener never had answers a free 400 with `allowed_values` | | `tag.{key}` | Mentions of groups with this tag, for example `tag.client=client-b` | | `platform` | Mentions from this platform. `x` works as `twitter`. A name no listener can watch answers a free 400 with `allowed_values` | | `language` | Posts in this language code. See the note below the table | | `record_type` | `post` or `comment` | | `parent_id` | The comments under the post with this `evidence.id` | | `judgment_status` | `judged`, `pending` or `unjudged` | | `limit` | 1 to 500 per page, 100 by default | | `format` | `evidence` (every field), `compact` or `text` | | `include` | `raw` adds the platform's original item as `evidence.raw` | A `language` filter is checked after each mention is opened, and a request scans at most 2,000 stored mentions. A page can therefore come back short, or empty, with `pagination.has_more` set to `true`. Keep paging with `pagination.next_cursor` while `has_more` is `true`, even when a page is short or empty. Mentions page in the order they were stored: the oldest check first and, inside a check, the order the platform returned its posts. `data.dropped` counts list items the response check removed because they did not match the documented shape. It is not a count of posts the relevance judge dropped, so it can be 0 when a check's `dropped` on the runs route is not. ## Names a listener does not take These names answer a 400 `not_offered`, with `docs_url` pointing here 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](/docs/listening/pricing.md)) | | `match_in` | Nothing: a listener matches a post's title and text together | | `delivery.endpoint_ids` | `subject_ids` on a [webhook endpoint](/docs/webhook-endpoints.md) | | `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"` | | `from:` (in a rule) | `accounts`: an [account panel](/docs/listening/overview.md#account-panels) of the accounts whose posts you want | `label`, `questions`, `sentiment` and `tz` are not available yet and answer `not_released`. 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](/docs/errors.md#invalid-request) | | `conflicting_params` | 400 | Drop one of the fields in `details.fields` | [INVALID_REQUEST](/docs/errors.md#invalid-request) | | `not_released` | 400 | Drop the field, value or route | [INVALID_REQUEST](/docs/errors.md#invalid-request) | | `not_offered` | 400 | Send the name in `did_you_mean`, or drop it (table above) | [INVALID_REQUEST](/docs/errors.md#invalid-request) | | `interval_below_lane_minimum` | 400 | Pick an `every` from `allowed_values`, or drop the platform whose lane `details.lane` names | [INVALID_REQUEST](/docs/errors.md#invalid-request) | | `unsupported_operator` | 400 | Use the rule in `nearest` | [INVALID_REQUEST](/docs/errors.md#invalid-request) | | `plan_limit` | 400 | Send fewer groups, search terms or accounts, within `limit` | [INVALID_REQUEST](/docs/errors.md#invalid-request) | | `budget_below_one_check` | 400 | Ask the user to approve `suggested_value`, then send it in `field` | [INVALID_REQUEST](/docs/errors.md#invalid-request) | | `prohibited_use` | 400 | Do not rephrase to pass. A dry run reports the verdict in `data.screen` instead; a create or a change refuses it | [INVALID_REQUEST](/docs/errors.md#invalid-request) | | `unknown_stream` | 404 | The listener belongs to another key or does not exist. List your key's listeners | [STREAM_NOT_FOUND](/docs/errors.md#stream-not-found) | | `stream_deleted`, `resume_from_dashboard` | 409 | Create a new listener. A listener stopped on a revoked key cannot be resumed with an API key, so create a new one with an active key | [STREAM_STATE_CONFLICT](/docs/errors.md#stream-state-conflict) | | `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](/docs/errors.md#service-unavailable) | A few answers are not refusals of the body: - `402` [`INSUFFICIENT_CREDITS`](/docs/errors.md#insufficient-credits) or [`KEY_BUDGET_EXCEEDED`](/docs/errors.md#key-budget-exceeded) on a preview whose first page the balance or the key's credit limit cannot pay. A listener's own checks never answer a 402: a check the balance cannot pay pauses the listener instead. - `413` `payload_too_large` for a body over the size limit. Split the groups across several listeners ([PAYLOAD_TOO_LARGE](/docs/errors.md#payload-too-large)). - `503` `store_unavailable` or `store_timeout` when the listener store does not answer in time. Nothing is charged; retry the same call after `Retry-After` ([SERVICE_UNAVAILABLE](/docs/errors.md#service-unavailable)). - The `Idempotency-Key` answers on a write: a reused key with a different body, or a request still in progress ([idempotency](/docs/idempotency.md)). ## What does the use screen check? 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 an account panel its word list also reads each entry, and `schedule.purpose` records what the panel watches. 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`, and so is a preview. The screen records what you declare and refuses declared uses; it is not a check of what a listener is later used for. - [Quickstart](/docs/listening/quickstart.md): Price, create and read a listener - [Pricing](/docs/listening/pricing.md): How the estimate and the cap are computed - [Lanes](/docs/listening/lanes.md): Platforms, order and page caps