Documentation
SCAND.Ai is an independent AI industry watchdog. This guide covers everything you need to integrate with our API.
Getting Started
- Create a free account to get your API key
- Add the
X-API-Keyheader to your requests - Explore the interactive API reference (Swagger UI)
curl -H "X-API-Key: sk_your_key_here" https://scand.ai/api/topicsAuthentication
All API requests accept an optional X-API-Key header. Without a key, you're limited to 30 requests per day. With a free key, 100/day. See pricing for higher limits.
X-API-Key: sk_your_key_hereAPI Endpoints
Base URL: https://scand.ai/api
Topics
| Method | Path | Description |
|---|---|---|
| GET | /topics | List active controversies (up to 50) |
| GET | /topics/:slug | Topic detail with parties and noise breakdown |
Stars
| Method | Path | Description |
|---|---|---|
| GET | /stars | List tracked AI figures |
| GET | /stars/:slug | Star detail with identities and controversies |
Activity & Stats
| Method | Path | Description |
|---|---|---|
| GET | /activity | Star activity feed. Filter: ?star_id=N |
| GET | /pulse | Live platform stats: active, breaking, hottest |
Export (Pro/Enterprise)
| Method | Path | Description |
|---|---|---|
| GET | /export/topics | Full dataset (NDJSON) |
| GET | /export/stats | Aggregate statistics |
Real-time (WebSocket)
Connect via WebSocket at wss://scand.ai/api/ws to receive live events.
Event format: Each message is a JSON object with type and data fields:
// noise_update — noise level changed for a topic
{ "type": "noise_update", "data": { "slug": "openai-safety-team", "noise_level": 72.5, "previous": 65.1, "state": "controversy" } }
// state_change — topic transitioned to a new state
{ "type": "state_change", "data": { "slug": "openai-safety-team", "from": "spike", "to": "controversy", "noise_level": 72.5 } }
// new_topic — a new controversy was detected
{ "type": "new_topic", "data": { "slug": "meta-ai-labor", "title": "Meta AI Team Labor Dispute", "state": "trending", "noise_level": 35.0 } }Connection example:
const ws = new WebSocket('wss://scand.ai/api/ws');
ws.onmessage = (event) => {
const msg = JSON.parse(event.data);
console.log(msg.type, msg.data);
};Alerts
| Method | Path | Description |
|---|---|---|
| GET | /alerts | List your alert preferences |
| POST | /alerts | Create an alert: { "email": "...", "frequency": "daily", "min_noise": 50, "categories": ["safety"] } |
| DELETE | /alerts/:id | Delete an alert preference |
Error Responses
All errors return a JSON object with an error field. Common HTTP status codes:
| Status | Meaning | Example |
|---|---|---|
| 400 | Bad Request | { "error": "missing url or events" } |
| 401 | Unauthorized | { "error": "invalid_api_key" } |
| 403 | Forbidden | { "error": "pro_or_enterprise_required", "hint": "Upgrade at scand.ai/pricing" } |
| 404 | Not Found | { "error": "not_found" } |
| 429 | Rate Limited | { "error": "rate_limit_exceeded", "remaining_day": 0, "remaining_month": 150 } |
Rate-limited responses include X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset (Unix timestamp) headers.
Webhooks
Pro and Enterprise users can register webhook endpoints to receive push notifications.
Events
topic.created— new controversy detectedtopic.state_change— state transition (trending → spike → controversy → scandal)noise.spike— noise level jumped significantlycontent.breaking— breaking content from S-tier source
Security
Each webhook gets a unique signing secret. Verify payloads using the X-Scandal-Signature header (HMAC-SHA256).
POST /api/webhooks
{
"url": "https://your-app.com/webhook",
"events": ["topic.state_change", "noise.spike"]
}
// Response: { "id": 1, "secret": "your-signing-secret" }Signature Verification
Verify the X-Scandal-Signature header to ensure payloads are authentic.
Node.js:
const crypto = require('crypto');
function verifyWebhook(body, signature, secret) {
const expected = crypto
.createHmac('sha256', secret)
.update(body)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
);
}
// Express example
app.post('/webhook', express.raw({ type: '*/*' }), (req, res) => {
const sig = req.headers['x-scandal-signature'];
if (!verifyWebhook(req.body, sig, process.env.WEBHOOK_SECRET)) {
return res.status(401).send('Invalid signature');
}
const event = JSON.parse(req.body);
console.log('Event:', event.type, event.data);
res.sendStatus(200);
});Python:
import hmac, hashlib
def verify_webhook(body: bytes, signature: str, secret: str) -> bool:
expected = hmac.new(
secret.encode(), body, hashlib.sha256
).hexdigest()
return hmac.compare_digest(signature, expected)
# Flask example
@app.route('/webhook', methods=['POST'])
def webhook():
sig = request.headers.get('X-Scandal-Signature', '')
if not verify_webhook(request.data, sig, WEBHOOK_SECRET):
return 'Invalid signature', 401
event = request.get_json()
print(f"Event: {event['type']}", event['data'])
return '', 200Embeddable Widgets
Add controversy data to your site:
Badge (no API key required)

Ticker (scrolling headlines)
Topic Card
Data Model
Topic States
Topics progress through states based on activity: trending → spike → controversy → scandal → resolved
Noise Level (0-100)
Composite score from 7 factors: reach, engagement, star power, duration, cross-platform presence, polarity, and industry impact. Decays over time.
Categories
safety | ethics | ip_copyright | labor | regulation | corporate | military | other
Rate Limits
| Tier | Price | Daily | Monthly | Webhooks | Export |
|---|---|---|---|---|---|
| Anonymous | Free | 30 | — | — | — |
| Free | $0 | 100 | 3,000 | — | — |
| Pro | $29/mo | 10,000 | 300,000 | 5 | Yes |
| Enterprise | $99/mo | Unlimited | Unlimited | 50 | Yes |
Changelog
- v1.0.0 (March 2026) — Public API launch: topics, stars, activity, pulse, WebSocket, webhooks, exports, embeds