---
title: "Create Listener"
method: POST
path: "/social-listeners"
tags: ["Signals"]
---

# Create Listener

`POST /social-listeners`

## Request

- Base URL: `https://be.graph8.com/api/v1`
- URL: `https://be.graph8.com/api/v1/social-listeners`
- Auth: HTTP bearer

Start watching something.

THIS CAN SPEND CREDITS: a `brand` listener with no `query` has one built by
an LLM call, which is charged. Supply `query`, or call
`POST /social-listeners/queries/generate` first, to see what you are
committing to.

A `user` listener additionally resolves `target_handle` against the
platform, which consumes the org's X API quota.

Set `monthly_credit_cap` unless you have set an org-wide one - it is the
only per-listener bound on what this costs.

## Headers

- `Authorization` string, nullable
- `X-Target-Org-Id` string, nullable

## Request body

- CreateListenerRequest: Start watching something. THIS CAN SPEND CREDITS. A `brand` listener with no `query` builds one with an LLM call, which is charged. Every other combination writes a row and the polling worker does the fetching from then on.
  - `platform` 'x' | 'linkedin' | 'reddit' | 'instagram' | 'youtube'
  - `listener_type` 'brand' | 'competitor' | 'keyword' | 'engagement' | 'user', required: `brand` and `competitor` watch a named entity, `keyword` watches a query, `engagement` watches an ICP-shaped bucket, `user` watches one account's posts.
  - `name` string, required
  - `query` string, nullable: Platform-native search query. OMITTING this on a `brand` listener makes the app build one with an LLM call, which SPENDS CREDITS. Supply a query, or call `POST /social-listeners/queries/generate` first if you want to see it before committing.
  - `target_handle` string, nullable: Required for a `user` listener: the account to watch.
  - `filters` object
  - `poll_interval_sec` integer, nullable: How often the worker polls. Shorter means more matches AND more spend.
  - `monthly_credit_cap` integer, nullable: Per-listener ceiling on credits. The one field that BOUNDS what this costs.
  - `force_rebuild_brand` boolean: Rebuild a brand query even though one exists. Spends a credit to do it.

## Response `201`

Successful Response

- ApiResponseSocialPayload
  - `data` SocialPayload, required: One social-listening object, or one handler response. Open-shaped: these payloads carry per-platform detail that differs between X, LinkedIn, Reddit, Instagram and YouTube and grows with them.
  - `pagination` PaginationMeta
    - `page` integer, required: Current page number (1-indexed)
    - `limit` integer, required: Items per page
    - `total` integer, required: Total number of items
    - `has_next` boolean, required: Whether there are more pages
    - `next_cursor` string, nullable: Opaque cursor for the next page; null when there are no more items. Pass it back as the `cursor` query param to fetch the following page without computing page numbers.
    - `total_available` integer, nullable: Search endpoints only: how many records really match, when known. Can be larger than `total`, which is capped at the 10,000 results that can be paged. For company search this is a distinct-company estimate. Null when unknown; absent outside search.
    - `total_is_lower_bound` boolean, nullable: Search endpoints only: true when more records match than `total` shows (the result set is larger than the 10,000 results that can be paged). Absent outside search.
    - `notes` string[], nullable: Search endpoints only: plain-language notes about the result, e.g. that more records match than can be paged, or that a reversed `between` range was put in order.

## Other responses

- `422`: Validation Error

---

[API](https://skmtc.dev/graph8/apis/graph8-developer-api.md) · [All operations](https://skmtc.dev/graph8/apis/graph8-developer-api/llms.txt) · [OpenAPI document](https://skmtc.dev/graph8/apis/graph8-developer-api/revisions/2e7cfa0ca921?raw)
