Skip to main content

Partner API Reference

Integrate Society Speaks programmatically. Power your embeds, snapshots, and editorial insights with JSON APIs built for newsroom workflows and developer velocity.

Quick start

curl "https://societyspeaks.io/api/discussions/by-article-url?url=https://example.com/article"

Then embed with the returned embed_url.

For developers

Predictable endpoints, clear error codes, and copy/paste examples.

For editorial leaders

Reliable participation signals and a transparent, auditable methodology.

For governance

Rate limits, attribution requirements, and a clear source of truth.

Testing the API

  • Partner Portal: Create a portal to get your test API key and DNS verification token.
  • Interactive playground: Open API Playground to try lookup, snapshot, and oEmbed from your browser (Swagger UI). Requests run against this site.
  • Create Discussion requires an API key; add it in the playground using the Authorize button (X-API-Key).
  • From the command line: use the curl examples in each section below, replacing the base URL and parameters as needed.

Authentication & Environments

Use test keys for staging and live keys for production. Both run on the same base URL and are isolated by key type. Keys are issued in the Partner Portal.

Test key: sspk_test_...
Live key: sspk_live_... (activated after billing)

Domains must be verified via DNS TXT to allow embeds.

Base URL

https://societyspeaks.io/api

Common Workflows

Programmatic embed (recommended)

  1. 1) Lookup discussion by article URL
  2. 2) If none exists, create discussion
  3. 3) Store discussion_id and use embed_url
# 1) Lookup
curl "https://societyspeaks.io/api/discussions/by-article-url?url=https://example.com/article" \
  -H "X-API-Key: sspk_test_..."

# 2) If 404, create
curl -X POST "https://societyspeaks.io/api/partner/discussions" \
  -H "X-API-Key: sspk_test_..." \
  -H "Content-Type: application/json" \
  -d '{"article_url":"https://example.com/article","title":"Example title","excerpt":"..."}'

Manual embed (quick start)

Use the Embed Generator to create one-off iframes. It relies on the same APIs and returns the same discussion_id and embed_url.

Lookup by Article URL

Find a discussion by the URL of your article.

GET /api/discussions/by-article-url

Query Parameters

url required The article URL (URL-encoded)
ref optional Partner reference for analytics

Example Request

curl "https://societyspeaks.io/api/discussions/by-article-url?url=https://example.com/article"

Success Response (200)

{
  "discussion_id": 123,
  "slug": "should-we-reform-housing-policy",
  "title": "Should we reform housing policy?",
  "embed_url": "https://societyspeaks.io/discussions/123/embed",
  "consensus_url": "https://societyspeaks.io/discussions/123/should-we.../consensus",
  "snapshot_url": "https://societyspeaks.io/api/discussions/123/snapshot",
  "source": "rss",
  "env": "live"
}

Error Responses

400 missing_url - URL parameter required
400 invalid_url - URL could not be parsed
404 no_discussion - No discussion for this URL

Get Discussion Snapshot

Get participation counts and metadata. Does not include analysis content.

GET /api/discussions/{discussion_id}/snapshot

Path Parameters

discussion_id required The discussion ID

Example Request

curl "https://societyspeaks.io/api/discussions/123/snapshot"

Success Response (200)

{
  "discussion_id": 123,
  "discussion_title": "Should we reform housing policy?",
  "participant_count": 847,
  "statement_count": 12,
  "has_analysis": true,
  "opinion_groups": 3,
  "analyzed_at": "2026-02-05T10:30:00Z",
  "consensus_url": "https://societyspeaks.io/discussions/123/.../consensus",
  "teaser_text": "Housing reform debate reveals surprising common ground"
}

When Analysis Not Ready

{
  "discussion_id": 123,
  "discussion_title": "Should we reform housing policy?",
  "participant_count": 23,
  "statement_count": 5,
  "has_analysis": false,
  "consensus_url": "https://societyspeaks.io/discussions/123/.../consensus"
}

Embed URL Parameters

Customize the embed appearance with URL parameters.

Embeds are available for Society Speaks native discussions.

https://societyspeaks.io/discussions/{discussion_id}/embed
Parameter Description Example
theme Preset theme: default, dark, editorial, minimal, bold, muted theme=editorial
primary Primary color (hex without #) primary=1e40af
bg Background color (hex without #) bg=f9fafb
font Font family from allowlist font=Georgia
ref Partner reference for analytics ref=observer

Allowed fonts: system-ui, Georgia, Inter, Lato, Open Sans, Source Serif Pro

PostMessage Events

The embed communicates with the parent frame via postMessage.

societyspeaks:embed:loaded

Sent when the embed finishes loading.

{
  "type": "societyspeaks:embed:loaded",
  "discussionId": 123,
  "statementCount": 5
}

societyspeaks:embed:resize

Sent when the embed content height changes. Use to resize iframe.

{
  "type": "societyspeaks:embed:resize",
  "discussionId": 123,
  "height": 450
}

Example: Auto-resize iframe

window.addEventListener('message', (event) => {
  if (event.data.type === 'societyspeaks:embed:resize') {
    const iframe = document.querySelector('iframe[src*="societyspeaks"]');
    if (iframe) iframe.style.height = event.data.height + 'px';
  }
});

oEmbed Provider

Enable automatic embedding of Society Speaks discussions in platforms that support oEmbed.

GET /api/oembed

Query Parameters

url required Society Speaks discussion URL
maxwidth optional Maximum embed width
maxheight optional Maximum embed height
format optional Response format (only "json" supported)

Example Request

curl "https://societyspeaks.io/api/oembed?url=https://societyspeaks.io/discussions/123/my-discussion"

Success Response (200)

{
  "type": "rich",
  "version": "1.0",
  "title": "Should we reform housing policy?",
  "provider_name": "Society Speaks",
  "provider_url": "https://societyspeaks.io",
  "html": "<iframe src=\"https://societyspeaks.io/discussions/123/embed\" ...></iframe>",
  "width": 600,
  "height": 400,
  "cache_age": 3600
}

Auto-discovery: Discussion pages include a <link rel="alternate" type="application/json+oembed"> tag for automatic oEmbed discovery by compatible platforms.

Create Discussion (Authenticated)

Create a discussion for an article not ingested via RSS. Requires API key.

POST /api/partner/discussions

Headers

X-API-Key required Your partner API key
Content-Type required application/json

Request Body

article_url required The article URL (will be normalized)
title required Discussion title (max 200 chars)
excerpt conditional Article excerpt for AI-generated statements
seed_statements conditional Array of {content, position} statements
source_name optional Your publication name for attribution

Note: Either excerpt or seed_statements must be provided.

Example Request

curl -X POST "https://societyspeaks.io/api/partner/discussions" \
  -H "X-API-Key: your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "article_url": "https://example.com/article/urban-cars",
    "title": "Should cities ban cars from downtown areas?",
    "excerpt": "A new study shows that car-free zones improve air quality...",
    "source_name": "Example News"
  }'

Success Response (201)

{
  "discussion_id": 456,
  "slug": "should-cities-ban-cars-from-downtown-areas",
  "title": "Should cities ban cars from downtown areas?",
  "embed_url": "https://societyspeaks.io/discussions/456/embed",
  "consensus_url": "https://societyspeaks.io/discussions/456/.../consensus",
  "snapshot_url": "https://societyspeaks.io/api/discussions/456/snapshot",
  "source": "partner",
  "env": "live",
  "statement_count": 5
}

Error Responses

401 invalid_api_key - Missing or invalid API key
400 missing_content - Need excerpt or seed_statements
409 discussion_exists - Discussion already exists for URL

Cost and Abuse Protection

Who pays for what: Lookup, snapshot, oEmbed, and the embed page do not use our LLM. They are rate-limited per IP (and optional ref) to prevent abuse. Create Discussion is the only endpoint that can trigger AI-generated seed statements (OpenAI/Anthropic); it uses our API keys, not yours.

Partners use keys we issue. Only requests with a valid X-API-Key (from our allowlist) can create discussions. We do not use partner-provided LLM keys for the create flow. If a key is abused, we revoke it.

Create Discussion is limited to 30 requests per hour per API key. Combined with key control, this caps LLM and database cost from the create endpoint.

Rate Limits

Endpoint Limit
Lookup by article URL 60 req / min / IP
Get snapshot 120 req / min / IP
Vote submission 30 req / min / IP
Create discussion 30 req / hr / key

Responses include a 429 Too Many Requests with rate_limited error when limits are exceeded.

Error Format

All errors return JSON with a consistent format.

{
  "error": "error_code",
  "message": "Human-readable description of the error"
}

Questions? Contact us or visit the Partner Hub.