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.

Base URL: 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_xxxxxxxxxxxxxxxxxxxx

Keys 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"
  }'
FieldTypeDescription
stimulusstringRequired. The statement to simulate (max 500 chars).
segmentsstring[]Required. Array of 1โ€“12 segment slugs. See Segment Reference.
modestring"dd" (default) or "individual". dd is faster; individual runs N separate persona calls for more variance detail.
qualitystring"standard" (default) or "max". Max uses Opus-class models, 3ร— credit cost.
geographic_filterobjectOptional. {"type": "state", "value": "TX"} or region. Restricts the simulated population to that geography.
webhook_urlstringOptional 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"
  }'
FieldTypeDescription
queriesobject[]Required. Array of 1โ€“10 query objects, each with query_id, stimulus, and segments.
modestringApplied to all queries. "dd" or "individual".
qualitystringApplied 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).

SlugNameDescriptionAccuracy
young-urban-strivingYoung Urban StrivingAges 18โ€“34, urban, college-educated, middle income. Optimistic, career-focused, progressive on social issues.high
young-urban-strugglingYoung Urban StrugglingAges 18โ€“34, urban, some college or less, lower income. Financially stressed, skeptical of institutions.low
young-ruralYoung RuralAges 18โ€“34, rural, mixed education, lower-to-middle income. Traditional values, community-oriented.high
mid-career-suburban-professionalMid-Career Suburban ProfessionalAges 35โ€“54, suburban, bachelor's or higher, upper-middle income. Home-owning, family-focused.high
mid-career-working-classMid-Career Working ClassAges 35โ€“54, mixed geography, high school or trade education, middle income. Pragmatic, skeptical of elites.high
mid-career-ruralMid-Career RuralAges 35โ€“54, rural, mixed education, lower-to-middle income. Self-reliant, conservative-leaning.high
high-income-establishedHigh-Income EstablishedAges 45โ€“64, suburban or urban, advanced degree, high income. Financially secure, moderate or conservative.high
high-income-urban-knowledge-workerHigh-Income Urban Knowledge WorkerAges 30โ€“50, urban, advanced degree, high income. Tech/finance/law. Progressive, cosmopolitan.high
pre-retirement-middle-incomePre-Retirement Middle IncomeAges 55โ€“64, suburban, varied education, middle income. Retirement-focused, health-conscious.low
pre-retirement-constrainedPre-Retirement ConstrainedAges 55โ€“64, rural or small-town, lower income. Financially anxious, skeptical of government.low
retiree-comfortableRetiree ComfortableAges 65+, suburban, some college or more, middle-to-high income. Active, civic-minded.high
retiree-constrainedRetiree ConstrainedAges 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 TypeCredits
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

400Bad Request

The request body is malformed or missing required fields.

Agent handling: Validate your JSON payload. Check required fields: stimulus, segments.

401Unauthorized

API key is missing, malformed, or inactive.

Agent handling: Verify your Authorization header is Bearer sp_live_<key>. Check key is active in Settings.

402Payment Required

Insufficient credits to complete the request.

Agent handling: Check credits_available in the response. Purchase additional credits before retrying.

422Unprocessable Entity

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.

429Rate Limited

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.

500Internal Server Error

Simulation or infrastructure error on the SynPop side.

Agent handling: Retry with exponential backoff (2s, 8s, 30s). Escalate if persists.

Rate Limits

LimitScopeValue
Per-key request ratePer API key10 req / 60 s
Global request rateAll keys combined500 req / 60 s
Daily spend capPer API key$50 / day
Global circuit breakerAll keys combined$200 / hour
Batch sizePer request10 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}.