Skip to content
Listener.comDevelopers

Requests & responses

Hosts, formats, ids, timestamps, headers, caching and CORS. The conventions every endpoint shares.

3 min read

Base URL#

All REST endpoints live under https://api.listener.com/v1. Every request must use HTTPS.

Format#

  • Request and response bodies are JSON, UTF-8, with snake_case field names.
  • Requests with a body send Content-Type: application/json. Bodies are limited to 1 MB (413 body_too_large).
  • GET parameters go in the query string. Lists of ids are comma-separated: show_ids=8sQe1v0x2kTq,k2LmP0aZ9qWe.

Objects and ids#

Every resource carries an object field naming its type and an id:

JSON
{ "object": "campaign", "id": "cmpA7Hq2LmX9vRt4BzKe", "title": "Acme Coffee Q4 host reads", "status": "active" }

Workspace resources (shows, campaigns, clusters, reports) keep the ids the apps use. Objects that only exist in the API use prefixed ids so you can tell them apart at a glance:

PrefixObject
we_Webhook endpoint
evt_Event
wd_Webhook delivery
req_Request
whsec_Webhook signing secret
lsk_API key
lcid_ / lcsk_OAuth client id / client secret
lat_ / lrt_OAuth access token / refresh token

Dates and times#

Timestamps are ISO 8601 strings in UTC: 2026-10-14T09:30:00Z. Calendar dates are YYYY-MM-DD. Analytics days are UTC calendar days (see Time windows).

Response headers#

Header
Request-Idreq_…, unique per request. Log it; support will ask for it. Also in every error body as request_id.
Listener-VersionThe API version that served the request. See Versioning.
RateLimit-Limit, RateLimit-Remaining, RateLimit-ResetYour rate-limit budget. X-RateLimit-* aliases are sent too. See Rate limits.
ETagOn GET responses, for conditional requests. Weak (W/"…").
Cache-Controlprivate, max-age=0 on GET, no-store otherwise.
Idempotent-Replayedtrue when a response was replayed for a repeated Idempotency-Key.

Conditional requests#

Send the ETag you received as If-None-Match on the next GET. If nothing changed, the API answers 304 Not Modified with no body, which saves bandwidth when you poll. The ETag ignores computed_at and cache_age_seconds, so a recomputed but identical result still answers 304.

curl
curl -i https://api.listener.com/v1/shows \
  -H "Authorization: Bearer $LISTENER_API_KEY" \
  -H 'If-None-Match: "…etag from the last response…"'

Computed results and caching#

Analytics are computed from the same documents the apps read. Reads are cached for up to two minutes, and analytics responses say so with computed_at and cache_age_seconds. Writes made through the API clear the cache for what they touch, so a read right after your own write sees it.

Writes#

MethodUse
POSTCreate something, or perform an action (/status, /refresh, /roll-secret).
PATCHPartial update: send only the fields you change.
PUTReplace a settable value (default EMV, episode goals, a slot’s cluster link).
DELETEDelete. Deletes that cannot be undone need confirm=true.

Every write supports dry_run and an Idempotency-Key. Successful writes emit events.

Most writes answer with a write result that says exactly what was stored:

Write result
{
  "object": "write_result",
  "resource": "campaign",
  "ok": true,
  "id": "cmpA7Hq2LmX9vRt4BzKe",
  "method": "add",
  "written": { "title": "Acme Coffee Q4 host reads", "status": "upcoming" },
  "warnings": [],
  "urls": { "app": "https://network.listener.com/app/campaigns/cmpA7Hq2LmX9vRt4BzKe", "public": "https://network.listener.com/northwind-audio/campaigns/cmpA7Hq2LmX9vRt4BzKe" },
  "data": {}
}

written is the exact payload; server-side timestamps appear as "<serverTimestamp>". warnings lists side effects worth knowing about (for example other campaigns that share a brand). Webhook endpoint writes are the exception: they return the webhook_endpoint or webhook_delivery object itself.

Unknown routes and methods#

An unknown path answers 404 route_not_found. A known path with the wrong method answers 405 method_not_allowed with an Allow header listing the methods it supports.

CORS#

/v1, /oauth/token and /oauth/revoke answer cross-origin requests from any origin (Access-Control-Allow-Origin: *) and expose the headers above. Authentication is by bearer token only; no cookies are involved. This exists for OAuth apps running in a browser. Do not put API keys in browser code (see Authentication).