API Documentation
The SynPop REST API lets you simulate how US population segments respond to any statement, policy, or scenario. No auth required to read these docs.
https://synpop.aiยทAll endpoints are JSON over HTTPS.Authentication
Generate an API key from Settings โ API Keys. Keys are prefixed sp_live_ and linked to your credit balance. Pass the key as a Bearer token on every request.
Authorization: Bearer sp_live_xxxxxxxxxxxxxxxxxxxxKeys are scoped to your account. Keep them secret โ they carry your credit balance. You can revoke and regenerate keys at any time from Settings.
Endpoints
POST /api/v1/simulate
Run a synchronous simulation against one or more population segments. Returns results immediately, or a 202 processing status if a webhook_url is provided.
Request
curl -X POST https://synpop.ai/api/v1/simulate \
-H "Authorization: Bearer sp_live_..." \
-H "Content-Type: application/json" \
-d '{
"stimulus": "The federal minimum wage should be raised to $20/hour.",
"segments": ["young-urban-striving", "mid-career-rural", "retiree-comfortable"],
"mode": "dd",
"quality": "standard"
}'| Field | Type | Description |
|---|---|---|
| stimulus | string | Required. The statement to simulate (max 500 chars). |
| segments | string[] | Required. Array of 1โ12 segment slugs. See Segment Reference. |
| mode | string | "dd" (default) or "individual". dd is faster; individual runs N separate persona calls for more variance detail. |
| quality | string | "standard" (default) or "max". Max uses Opus-class models, 3ร credit cost. |
| geographic_filter | object | Optional. {"type": "state", "value": "TX"} or region. Restricts the simulated population to that geography. |
| webhook_url | string | Optional HTTPS URL. If set, returns 202 immediately and POSTs results when complete. See Webhooks. |
Response (200)
{
"study_id": "uuid",
"stimulus": "The federal minimum wage should be raised to $20/hour.",
"results": [
{
"segment": "young-urban-striving",
"distribution": {
"strongly_agree": 0.38,
"agree": 0.31,
"neutral": 0.14,
"disagree": 0.11,
"strongly_disagree": 0.06
},
"reasoning": "This segment strongly supports wage increases...",
"confidence": "high",
"cached": false,
"metadata": {
"profile_version": 3,
"conditioning_dimensions": ["age", "income", "education", "geography", "political_orientation"],
"economic_context_period": "Q1 2026",
"validated_domain": "consumer_financial_behavior",
"benchmark_accuracy": { "system_result": "PASS", "spearman_median": 0.85, "jsd_average": 0.025 },
"confidence": "high",
"confidence_note": null,
"cached": false,
"query_within_validated_domain": true
}
}
],
"credits_used": 20,
"credits_remaining": 980
}POST /api/v1/batch-simulate
Run up to 10 simulations in a single request. Each query in the batch is independent โ different stimuli, segments, or both. Credits are deducted per query; failed queries are refunded automatically.
Request
curl -X POST https://synpop.ai/api/v1/batch-simulate \
-H "Authorization: Bearer sp_live_..." \
-H "Content-Type: application/json" \
-d '{
"queries": [
{
"query_id": "q1",
"stimulus": "Remote work should be a legal right.",
"segments": ["high-income-urban-knowledge-worker", "mid-career-rural"]
},
{
"query_id": "q2",
"stimulus": "Student loan forgiveness is fair.",
"segments": ["young-urban-striving", "retiree-comfortable"]
}
],
"mode": "dd",
"quality": "standard"
}'| Field | Type | Description |
|---|---|---|
| queries | object[] | Required. Array of 1โ10 query objects, each with query_id, stimulus, and segments. |
| mode | string | Applied to all queries. "dd" or "individual". |
| quality | string | Applied to all queries. "standard" or "max". |
Response (200)
{
"batch_id": "uuid",
"results": [
{
"query_id": "q1",
"status": "complete",
"study_id": "uuid",
"results": [ /* same structure as /simulate results */ ],
"credits_used": 20
},
{
"query_id": "q2",
"status": "error",
"error": "Simulation failed",
"credits_used": 0
}
],
"total_credits_used": 20,
"credits_remaining": 960
}GET /api/v1/studies/{study_id}
Retrieve results for a study created via webhook mode. Poll this endpoint after receiving a 202 from /simulate.
curl https://synpop.ai/api/v1/studies/{study_id} \
-H "Authorization: Bearer sp_live_..."Response
// Processing (202):
{ "study_id": "uuid", "status": "processing", "created_at": "..." }
// Complete (200):
{
"study_id": "uuid",
"status": "complete",
"stimulus": "...",
"results": [ /* same as /simulate */ ],
"created_at": "..."
}
// Failed (200):
{ "study_id": "uuid", "status": "error", "created_at": "..." }GET /api/v1/segments
Returns the list of all active segments with their slugs, names, and demographic dimensions. Use this to enumerate valid slugs programmatically.
curl https://synpop.ai/api/v1/segments \
-H "Authorization: Bearer sp_live_..."{
"segments": [
{
"id": "uuid",
"name": "Young Urban Striving",
"slug": "young-urban-striving",
"description": "...",
"demographic_dimensions": {
"age_cohort": "18-34",
"income_tier": "middle",
"education_level": "college",
"geographic_type": "urban"
},
"display_order": 1
},
...
]
}Segment Reference
SynPop models 12 US demographic segments. Accuracy ratings are derived from the 2026-03-30 validation benchmark (NFWBS PUF 2016, Spearman median 0.85 across all segments, 8/12 passing at high confidence on consumer financial behavior).
| Slug | Name | Description | Accuracy |
|---|---|---|---|
| young-urban-striving | Young Urban Striving | Ages 18โ34, urban, college-educated, middle income. Optimistic, career-focused, progressive on social issues. | high |
| young-urban-struggling | Young Urban Struggling | Ages 18โ34, urban, some college or less, lower income. Financially stressed, skeptical of institutions. | low |
| young-rural | Young Rural | Ages 18โ34, rural, mixed education, lower-to-middle income. Traditional values, community-oriented. | high |
| mid-career-suburban-professional | Mid-Career Suburban Professional | Ages 35โ54, suburban, bachelor's or higher, upper-middle income. Home-owning, family-focused. | high |
| mid-career-working-class | Mid-Career Working Class | Ages 35โ54, mixed geography, high school or trade education, middle income. Pragmatic, skeptical of elites. | high |
| mid-career-rural | Mid-Career Rural | Ages 35โ54, rural, mixed education, lower-to-middle income. Self-reliant, conservative-leaning. | high |
| high-income-established | High-Income Established | Ages 45โ64, suburban or urban, advanced degree, high income. Financially secure, moderate or conservative. | high |
| high-income-urban-knowledge-worker | High-Income Urban Knowledge Worker | Ages 30โ50, urban, advanced degree, high income. Tech/finance/law. Progressive, cosmopolitan. | high |
| pre-retirement-middle-income | Pre-Retirement Middle Income | Ages 55โ64, suburban, varied education, middle income. Retirement-focused, health-conscious. | low |
| pre-retirement-constrained | Pre-Retirement Constrained | Ages 55โ64, rural or small-town, lower income. Financially anxious, skeptical of government. | low |
| retiree-comfortable | Retiree Comfortable | Ages 65+, suburban, some college or more, middle-to-high income. Active, civic-minded. | high |
| retiree-constrained | Retiree Constrained | Ages 65+, rural or small-town, lower income. Fixed-income stress, high healthcare concern. | low |
high โ โฅ4/5 benchmark questions passed for consumer financial behavior questions. low โ directionally informative; not recommended for precision research.
Credit Costs
Credits are purchased in advance and consumed per request. Unused credits expire 12 months after purchase. Failed queries are automatically refunded.
| Query Type | Credits |
|---|---|
| Single segment (standard quality) | 2 |
| Multi-segment 2โ12 (standard quality) | 20 |
| Single segment (max quality) | 6 |
| Multi-segment 2โ12 (max quality) | 60 |
| Batch query (per query, standard) | Same as above per query |
Error Codes
The request body is malformed or missing required fields.
Agent handling: Validate your JSON payload. Check required fields: stimulus, segments.
API key is missing, malformed, or inactive.
Agent handling: Verify your Authorization header is Bearer sp_live_<key>. Check key is active in Settings.
Insufficient credits to complete the request.
Agent handling: Check credits_available in the response. Purchase additional credits before retrying.
One or more segment slugs are invalid or not found.
Agent handling: Call GET /api/v1/segments to get valid slugs. Remove any unrecognized values.
Per-key (10 req/min), daily spend, or global circuit breaker limit reached.
Agent handling: Back off and retry after 60 seconds. For daily limits, wait until midnight UTC.
Simulation or infrastructure error on the SynPop side.
Agent handling: Retry with exponential backoff (2s, 8s, 30s). Escalate if persists.
Rate Limits
| Limit | Scope | Value |
|---|---|---|
| Per-key request rate | Per API key | 10 req / 60 s |
| Global request rate | All keys combined | 500 req / 60 s |
| Daily spend cap | Per API key | $50 / day |
| Global circuit breaker | All keys combined | $200 / hour |
| Batch size | Per request | 10 queries max |
Rate limit windows reset on a rolling 60-second basis. The global circuit breaker triggers if infrastructure Anthropic API spend exceeds $200/hour across all keys โ this is a safety mechanism and will resolve automatically when spend drops.
MCP Integration
SynPop ships an MCP server (synpop-mcp) that exposes simulate_population and get_segments as tools callable directly by Claude agents.
Claude Code / Claude Desktop
Add the following to your .claude/settings.json (Claude Code) or claude_desktop_config.json (Claude Desktop):
{
"mcpServers": {
"synpop": {
"command": "npx",
"args": ["synpop-mcp"],
"env": {
"SYNPOP_API_KEY": "sp_live_..."
}
}
}
}Once connected, agents can call:
// Simulate population response
simulate_population({
stimulus: "Should interest rates be raised to fight inflation?",
segments: ["mid-career-suburban-professional", "retiree-comfortable"],
mode: "dd"
})
// List available segments
get_segments()The MCP server auto-discovers the base URL from the SYNPOP_BASE_URL env var (defaults to https://synpop.ai).
Webhooks
Pass a webhook_url to POST /api/v1/simulate to receive results asynchronously. The endpoint returns 202 with a study_id immediately, then POSTs the full result payload to your URL when the simulation completes.
Webhook payload
POST https://your-endpoint.example.com/synpop-webhook
Content-Type: application/json
X-SynPop-Signature: sha256=<hex-digest>
{
"study_id": "uuid",
"status": "complete",
"stimulus": "...",
"results": [ /* same structure as /simulate */ ]
}Signature Verification
Every webhook request includes an X-SynPop-Signature header. The value is sha256= followed by an HMAC-SHA256 hex digest of the raw JSON body, keyed with your API key.
// Node.js verification example
import { createHmac } from 'crypto';
function verifyWebhook(rawBody, signatureHeader, apiKey) {
const expected = 'sha256=' + createHmac('sha256', apiKey)
.update(rawBody)
.digest('hex');
return signatureHeader === expected;
}
// Express example
app.post('/synpop-webhook', express.raw({ type: 'application/json' }), (req, res) => {
const sig = req.headers['x-synpop-signature'];
if (!verifyWebhook(req.body, sig, process.env.SYNPOP_API_KEY)) {
return res.status(401).send('Invalid signature');
}
const payload = JSON.parse(req.body);
// handle payload...
res.sendStatus(200);
});SynPop retries failed webhook deliveries up to 3 times with exponential backoff (0 s, 2 s, 8 s). Your endpoint should return a 2xx status within 10 seconds. On permanent failure, the study remains accessible via GET /api/v1/studies/{study_id}.