API Reference
The CrowdProof API lets you create and manage simulations programmatically. Integrate social simulation into your workflow, CI/CD pipeline, or custom applications.
Authentication
API requests accept two authentication methods:
- Session cookie (all plans) - set automatically when you log in via the browser.
- Bearer token (Enterprise plan) - pass an API key in the
Authorizationheader. Keys are created and managed at /dashboard/keys.
Authorization: Bearer cp_live_...Keys carry a scope: full (read and write) or read (read-only). A read-only key receives a 403 on anything that creates or mutates data. It works on every one of these routes, including both exports, so a pipeline that only pulls results never needs a full-scope key:
GET /api/sim/listGET /api/sim/{sim_id}GET /api/sim/{sim_id}/reportGET /api/sim/{sim_id}/report/exportGET /api/sim/{sim_id}/feed.csvGET /api/sim/{sim_id}/sentimentGET /api/sim/{sim_id}/factionsGET /api/sim/{sim_id}/validationGET /api/sim/{sim_id}/survey/resultsGET /api/sim/{sim_id}/survey/responses.csv
Create Simulation
Create and start a new simulation. Returns immediately; the simulation runs in the background.
Request Body
{
"seed_text": "We are raising prices by 15% starting next month.",
"audience_preset": "general_public",
"agent_count": 100,
"rounds": 10,
"platforms": ["pulse", "forum", "circle", "network", "board"],
"industry": "saas",
"webhook_url": "https://example.com/hooks/crowdproof"
}| Field | Type | Required | Description |
|---|---|---|---|
seed_text | string | Yes | The stimulus for agents to react to (1-10,000 chars). Can contain URLs. The run reads up to the first three links. A page it cannot fetch, and any extra link, stays as URL text. The report says which pages were read. |
audience_preset | string | No | general_public (default), tech_twitter, or small_business_owners |
audience_custom | string | No | Free-text audience description (max 1,000 chars). When set, the engine blends archetypes to match. |
audience_interviews | string | No | Raw interview notes, survey verbatims, or quotes from real people (max 8,000 chars). Used only alongside audience_custom: the blend grounds archetype selection in what those people actually said. |
interview_corpus_id | string | No | Id of a saved interview corpus to ground the audience in. Resolved server-side into audience_interviews; pasted notes win when both are sent. Corpora are created and managed in the dashboard, not through this API. |
agent_count | integer | No | 10-5,000, clamped to your plan limit. Omit the field to use 200, or 100 on Free. |
rounds | integer | No | 5-100, clamped to your plan limit. Omit the field to use 20, or 10 on Free. |
platforms | string[] | No | Subset of pulse, forum, circle, network, board. Your plan sets how many a run may enable (Free 1, Pro 3, Team and Enterprise 5). Asking for more than that returns 403 and starts nothing, so a run never quietly moves to rooms you did not pick. Omit the field to take whatever your plan allows. |
industry | string | No | One of: saas, consumer, ecommerce, fintech, healthcare, media, education, gaming, nonprofit, other |
webhook_url | string | No | Public https URL POSTed when the run completes or fails. |
notify_on_complete | boolean | No | Email the owner when the crowd finishes, with a report link, or when the run fails, with no report. Default false. |
Grounding an audience in real people. Send audience_custom to describe the audience in your own words, and add audience_interviews (or interview_corpus_id) to ground that blend in real interview notes. The engine weights archetypes, and can invent new ones, from what your respondents actually said instead of from the description alone. Without audience_custom the interview fields are ignored and the run uses audience_preset.
Response
{
"id": "a1b2c3d4-...",
"title": "Simulation 2026-07-13 21:05",
"status": "seeding",
"share_token": "abc123...",
"stream_url": "/api/sim/a1b2c3d4-.../stream"
}share_token is the public share-link token; build the read-only spectator/report URL as https://crowdproof.xyz/sim/{share_token}/share without a follow-up request.
Example
curl -X POST https://api.crowdproof.xyz/api/sim/create \
-H "Authorization: Bearer cp_live_..." \
-H "Content-Type: application/json" \
-d '{
"seed_text": "We are raising prices by 15% starting next month.",
"audience_preset": "general_public",
"agent_count": 100,
"rounds": 10
}'Create A/B Experiment
Launch two variant simulations from one request (Team and Enterprise plans). Both variants share an identical agent pool and follow network; only the seed text differs.
Request Body
{
"seed_a": "We are raising prices by 15% starting next month.",
"seed_b": "We are introducing a new premium tier with additional features.",
"audience_preset": "general_public",
"agent_count": 100,
"rounds": 10
}Consumes two monthly sim credits. Returns both simulation IDs.
This endpoint accepts the same optional fields as Create Simulation, including audience_custom, audience_interviews, interview_corpus_id, notify_on_complete, and webhook_url. The audience is blended once and shared by both variants, so only the seed text differs between them. Each variant emails a report link when that crowd finishes, or that there is no report when it fails. A webhook is sent per variant for every terminal outcome. The webhook payload also carries the shared experiment_id and the variant's variant_label, so a pipeline can pair the two callbacks without a further request.
List Simulations
List all simulations for the authenticated user.
Response
[
{
"id": "a1b2c3d4-...",
"title": "Price increase test",
"status": "completed",
"agent_count": 100,
"rounds": 10,
"current_round": 10,
"audience_preset": "general_public",
"topic_summary": "The crowd debated whether a 15% price hike is justified.",
"final_sentiment": -0.18,
"alignment_pct": 82,
"cost_usd": 0.45,
"created_at": "2026-07-01T10:30:00Z",
"completed_at": "2026-07-01T10:33:45Z"
}
]Get Simulation
Get the current status and details of a simulation.
Response
{
"id": "a1b2c3d4-...",
"title": "Price increase test",
"status": "completed",
"seed_content": "We are raising prices by 15% starting next month.",
"audience_preset": "general_public",
"agent_count": 100,
"rounds": 10,
"current_round": 10,
"platforms": ["pulse", "forum", "circle", "network", "board"],
"industry": "saas",
"topic_summary": "The crowd debated whether a 15% price hike is justified.",
"cost_usd": 0.45,
"share_token": "abc123...",
"created_at": "2026-07-01T10:30:00Z",
"completed_at": "2026-07-01T10:33:45Z"
}Example
curl https://api.crowdproof.xyz/api/sim/a1b2c3d4-... \
-H "Authorization: Bearer cp_live_..."Rename or Archive
Rename a simulation or toggle the archive flag.
Request Body
{
"title": "Updated title",
"archived": false
}Send title, archived, or both.
Delete Simulation
Permanently delete a finished simulation and its data (survey responses, injections, state). Returns 409 while the simulation is still active; stop it first.
Get Report
Get the full synthesis report for a completed simulation.
Response
{
"summary": "Price increase met with significant pushback...",
"stats": {
"total_agents": 100,
"total_posts": 620,
"total_actions": 2408,
"virality_index": 3.2
},
"outcome": {
"stance": { "positive_pct": 32, "neutral_pct": 23, "negative_pct": 45 },
"intent": { "likely_pct": 28, "maybe_pct": 35, "unlikely_pct": 37 }
},
"sentiment": {
"summary": { "start": 0.0, "end": -0.18, "min": -0.31, "max": 0.08, "total_change": -0.18, "volatility": 0.06 },
"history": [{ "round": 1, "overall": 0.02, "by_faction": {} }],
"shifts": [{ "round": 6, "direction": "negative", "change": -0.21 }]
},
"factions": {
"final": [{ "id": "faction_0", "name": "Loyal but Wary", "agent_count": 44, "avg_sentiment": -0.31, "trend": "stable" }],
"history": [{ "round": 5, "factions": [...] }]
},
"viral_content": {
"top_posts": [{ "id": "post_1", "agent_name": "Sarah", "content": "15% increase with 0% improvement?", "engagement": 47, "faction_id": "faction_0", "stance": -0.6, "platform": "pulse", "round": 3 }],
"viral_moments": [{ "round": 4, "content": "...", "engagement": 47, "std_devs": 2.8 }]
},
"key_arguments": [{ "faction": "Loyal but Wary", "argument": "No warning given.", "support_count": 44, "sentiment": -0.31, "agent_name": "Sarah" }],
"platforms": [{ "platform": "pulse", "posts": 310, "engagement": 1415, "avg_stance": -0.22 }],
"hashtags": [{ "tag": "PriceHike", "uses": 34, "engagement": 890, "avg_stance": -0.4, "first_round": 2 }],
"influencers": [{ "agent_name": "Sarah", "agent_handle": "sarah_m", "followers": 47, "posts": 12, "engagement": 234, "stance": -0.45 }],
"insights": ["..."],
"recommendations": ["..."]
}Example
curl https://api.crowdproof.xyz/api/sim/a1b2c3d4-.../report \
-H "Authorization: Bearer cp_live_..."Export Report as Markdown
When the crowd finishes, download the report as Markdown. A stopped or failed run has no report. Includes all report sections plus real-world validation data when available.
Example
curl -O -J https://api.crowdproof.xyz/api/sim/a1b2c3d4-.../report/export \
-H "Authorization: Bearer cp_live_..."Export Feed as CSV
Export the raw feed as CSV when the crowd has posted. A paused, stopped, or failed run with posts can too. Columns: round, timestamp, platform, agent name/handle/archetype, faction, content, engagement counts.
Example
curl -O -J https://api.crowdproof.xyz/api/sim/a1b2c3d4-.../feed.csv \
-H "Authorization: Bearer cp_live_..."Get Sentiment
Get the sentiment history for a simulation (round-by-round overall and per-faction values).
Get Factions
Get the current or final faction breakdown.
Inject Event
Inject a new event into a running simulation. Requires a session cookie (not API keys).
Request Body
{
"content": "BREAKING: Company releases statement clarifying the price change.",
"injection_type": "news",
"platform": "pulse",
"at_round": 8
}| Field | Type | Description |
|---|---|---|
content | string | The event content to inject. |
injection_type | string | post (anonymous user, default), news (news outlet), or influencer (verified account). Controls the synthetic author identity. |
platform | string | Optional. One of the sim's enabled platforms. Defaults to the first enabled one. |
at_round | integer | Optional. Schedule the injection at a future round instead of applying it immediately. Must be after the current round and no later than the run's last round, since the injection is applied at the start of the round it names. |
Response (immediate)
{
"post_id": "inject_abc12345",
"round": 5,
"platform": "pulse"
}Response (scheduled)
{
"scheduled": true,
"round": 8,
"injection_id": "..."
}Scheduled injections can be listed or cancelled:
List the simulation's injections (scheduled and applied).
Cancel a scheduled injection (409 once applied).
Pause and Resume
Pause a running simulation. Requires a session cookie.
Resume a paused simulation. Requires a session cookie.
Pausing is not how you end a run. A paused simulation still counts as active: it cannot be deleted, it never produces a report, and it keeps holding the plan credit it consumed. Use Stop below.
Stop Simulation
Stop a run that has not finished, for good. Accepts a session cookie or a full-scope API key.
Any status before the run finishes is accepted, including the start-up ones, so a run can be stopped from the moment it is created. During start-up, setup work already in flight finishes and the engine stops before round one. During a round, it stops at the next status check. A run that has already finished returns 409.
Response
{
"status": "cancelled"
}Example
curl -X POST https://api.crowdproof.xyz/api/sim/a1b2c3d4-.../cancel \
-H "Authorization: Bearer cp_live_..."The stopped run is terminal. It fires the completion webhook with "event": "simulation.cancelled", it can then be deleted, and the sim credit it consumed is not returned: the refund path covers a run the system broke, not one you ended. A stopped run has no report.
Stream Simulation (SSE)
Stream simulation events in real time via Server-Sent Events. Requires a session cookie.
Event Types
| Event | Description |
|---|---|
agent_action | An agent posted, replied, liked, reposted, etc. |
agent_quote | An agent quote-posted another post. |
agent_thread | An agent posted a multi-part thread. |
agent_react | A Circle reaction (love, haha, wow, angry, sad). |
agent_downvote | An agent downvoted a post (Forum). |
agent_award | An agent awarded a post (Forum). |
agent_hashtag | An agent posted with a hashtag. |
sentiment_update | Overall and per-faction sentiment for the round. |
faction_update | Faction membership changed. |
network_change | An agent followed or unfollowed another agent. |
agent_endorse | An agent endorsed another agent. |
viral_alert | A post crossed the virality threshold. |
sim_seeding | A start-up milestone before round 1 (step, detail): reading the stimulus, building the crowd, summarising the topic, mapping the knowledge graph. The current milestone is replayed when a stream connects, so a client that joins mid-setup reads where the run is instead of waiting for the next step. It is the only event that is replayed, and a reconnect during setup can therefore deliver the same milestone twice. |
round_start | A new round began (active agent count). |
round_complete | A round finished (duration, actions taken, faction shifts). |
synthesis_ready | The report is stored and ready to read. |
sim_paused / sim_resumed | The owner paused the run, and started it again. Sent once each, not once per second, so a viewer can tell a paused run from a stalled one. |
sim_complete | The simulation has finished (verdict, totals). |
sim_failed | The run failed or was cancelled (terminal signal). |
Completion Webhook
Pass a webhook_url when creating a simulation. CrowdProof will POST a JSON payload the moment the run finishes, so pipelines can react without polling. The URL must be a publicly reachable https endpoint; private and localhost hosts are rejected at creation time.
Payload
{
"event": "simulation.completed",
"simulation_id": "a1b2c3d4-...",
"title": "Price increase test",
"status": "completed",
"experiment_id": null,
"variant_label": null,
"verdict": "Strong positive shift in public opinion",
"report_url": "https://crowdproof.xyz/sim/a1b2c3d4-.../report",
"api_report_url": "https://api.crowdproof.xyz/api/sim/a1b2c3d4-.../report"
}The verdict is the run's one-line outcome, so a pipeline can branch on the result (positive / negative / polarizing / stable) without a second call to fetch the report. The report_url opens the owner's browser report, while api_report_url is the report endpoint a pipeline can fetch with the same bearer key used to create the simulation. Failed runs deliver"event": "simulation.failed","status": "failed", "verdict": null, and both report URLs as null (a failed run never produces a report). Failed runs also refund the consumed sim credit automatically.
A run the owner stops from the cockpit is terminal too, and delivers "event": "simulation.cancelled", "status": "cancelled", "verdict": null, and both report URLs as null. A stopped run writes no report and its sim credit is not returned, so treat any status other than completed as the end of the run rather than waiting for a later callback.
A run launched by an A/B experiment names its pair: experiment_id is the same value on both variants and variant_label is "A" or "B". Both variants carry the one webhook_url you registered, so these two fields are how a callback says which scenario it reports and which other callback it belongs with. A single run sends both as null.
Delivery and retries
Your endpoint should answer with any 2xx or 3xx status. If it times out, refuses the connection, or answers 5xx, 408, or 429, CrowdProof retries twice more (after 1 second, then 3 seconds). Any other 4xx is treated as a final answer and the delivery stops there.
Because a delivery is retried when the response is lost, the same callback can arrive more than once. Treat simulation_id plus event as the deduplication key, and answer quickly: do the real work after you respond.
Real-World Validation
Compare manually entered real survey shares against the simulated stance distribution.
{
"positive_pct": 35,
"neutral_pct": 25,
"negative_pct": 40,
"source": "Post-launch survey, n=200",
"respondents": 200
}Retrieve the stored validation result (alignment score, sim-vs-real shares, question-level and demographic breakdowns).
Create a public survey link for collecting real responses. Returns the survey URL.
Score the simulation against collected survey responses (minimum 5). Stores per-question, per-age-band, and per-wave alignment breakdowns.
API Key Management
Enterprise plan only. Requires session-cookie authentication.
Create a new API key. The plaintext secret is shown once in the response.
{
"name": "CI Pipeline",
"scopes": "full"
}scopes is "full" (default) or "read".
List your API keys (metadata only, never the plaintext).
Rotate a key: new secret, same key record. The old secret stops working immediately.
Revoke an API key permanently.
Rate Limits
API requests are subject to plan-based limits:
| Plan | Monthly simulations | Agents per sim | Rounds per sim | Platforms per sim |
|---|---|---|---|---|
| Free | 2 | 100 | 10 | 1 |
| Pro | 15 | 500 | 20 | 3 |
| Team | 50 | 2,000 | 30 | 5 |
| Enterprise | Unlimited | 5,000 | 50 | 5 |
A request over any of these limits returns 403 with the limit named, and no simulation credit is consumed.
Error Codes
| Code | Meaning |
|---|---|
| 400 | Bad request (invalid parameters, invalid webhook URL, invalid platform) |
| 401 | Invalid or missing authentication |
| 403 | Plan does not support this feature, or read-only key on a write route |
| 404 | Simulation not found |
| 409 | Conflict (e.g. deleting an active simulation, cancelling an applied injection) |
| 422 | Validation error in request body |
| 500 | Server error |
Next Steps
- Getting Started - Learn the basics
- Simulations - Understand simulation configuration
- Audiences - Explore audience presets and custom audiences
- Accuracy - See real-world validation results
Pay once for one full simulation: 500 people, 20 rounds, 3 platforms. Getting Started, Simulations, Audiences, and Accuracy stay above.