Skip to main content
Generate a population of richly detailed, humanlike personas from one natural-language prompt. You describe who you want and how many — nothing else — and get back a population that holds together like a real one: believable individuals whose traits vary and correlate the way they would in the real world, each member internally consistent. You never declare fields, distributions, or formats. A single prompt such as “10 League of Legends players from around the globe” is enough on its own: the API does the hard modeling of what makes that population realistic and returns one that actually looks the part — the right mix of regions and ranks, with the detail (like higher ranks playing more) that a believable population has. It hands that model back alongside the personas as a blueprint (see The population), so what it inferred is yours to inspect, not a black box. This endpoint is asynchronous. The POST returns 200 OK right away with an id; you then GET the population repository route with that id until it is ready. Generation grounds the request in real-world data and writes a persona for each member, so it can run for minutes — see the note on grounding below.

When to call it

Up front, once — then reuse what it returns. Whatever the population is for: a simulated user base to test a product against, a research cohort, a survey panel, a cast of characters, a persona for an agent to adopt. You generate it before the thing that uses it runs, and read the personas from your own storage afterwards. Generation grounds the request in real-world data and writes a persona per member, so it takes minutes. Nothing on a request path — no message loop, no timer, no per-user call — should be waiting on it. The same holds for enhance and validate: you run them while you are authoring or refining a population, store the result, and read it back at runtime. See When to call each API.

Authorization

string
required
Your bearer token: Bearer <token>. See Authentication.

Request body

The request is intentionally minimal. These three fields are the entire contract — there is nothing else to send.
string
required
A non-empty natural-language description of who to generate. This is the only signal the API needs; it infers the field structure from the prompt.
integer
default:"1"
How many personas to generate. Must be at least 1. Large counts are capped; a request over the limit returns VALIDATION_ERROR. The population size is this number; there is no separate batch endpoint.
string
default:"off"
How hard to ground the population in real-world data before generating. One of:
  • off — generate from the prompt alone, with no external lookups.
  • web — enrich the inferred field model with a quick live lookup so distributions and details reflect current real-world data.
  • research — run deeper research before generating, for the strongest real-world grounding and cited sources.
Request

Start a population

The POST returns 200 OK straight away. It does not wait for the personas; it gives you an id used with the population repository route.
string
The population’s identifier. Use it to poll for the result.
string
Always pending in this response — the population has been accepted and queued.
200 OK

Grounding and timing

Generation grounds the request in real-world data and then writes a persona for each member, so it does not return on the POST. research grounding runs deeper lookups and can take minutes, and time grows with count. That is why this endpoint hands back an id to poll rather than holding the connection open. Poll on an interval of a few seconds and do not block a user-facing request on a research population.

Poll for the result

Call GET with the returned id until status is succeeded or failed.
string
The population’s identifier, the same value you started.
string
Where the population is in its lifecycle. One of:
  • pending — accepted and queued; generation has not started yet.
  • running — grounding and generation are under way; progress updates.
  • succeeded — finished; result holds the population.
  • failed — finished; error contains a stable failure category.
pending and running are not terminal — keep polling. succeeded and failed are terminal — stop polling.
object
How far generation has got, present while running. produced is how many personas are written so far and total is how many were requested.
object
The generated population, present only when status is succeeded. Its shape is described under The population below.
string
A stable failure category such as provider_error. Present only when status is failed.
While the population is still building, the poll reports running and carries a progress object so you can show how far along it is:
200 OK (running)

The population

When status is succeeded, the poll carries the population under result: the rendered personas, the inferred blueprint the personas were sampled from, and fidelity reports showing how closely the realized population matches that blueprint. The fields below describe the shape of that result. Each persona is dynamic. There is no fixed persona shape: the blueprint declares the field set, and every persona carries exactly those fields as a flat fields map of name to string value. For the League of Legends prompt the API might infer region, rank, main_role, hours_per_week, name, and backstory; a different prompt yields a different field set.
object[]
The generated personas. Each one carries the blueprint’s fields plus a ready-to-use system_prompt and a formatted markdown sheet — all three are always present.
object
The inferred field model the population was sampled from — the API’s reasoning about what makes this population realistic, returned so you can inspect it and read the field set the personas use.
object
How varied the population is. Present for multi-persona populations.
object[]
Per-field fidelity: for each root categorical field, how closely the realized population matches the blueprint’s marginal distribution.
When the population succeeds, this object is the value of result ({ "id": ..., "status": "succeeded", "result": <the object below> }):
200 OK (succeeded)
(personas and marginals are truncated above; a real population contains count personas and one manifest per root categorical field.)

When generation fails

If generation fails after the population starts, the poll still returns 200 OK with status: "failed" and an error instead of a result:
200 OK (failed)
Branch on the poll’s status and error, not on the HTTP status of the poll — a failed population is reported with 200 OK. Start a new population to retry; do not keep polling a failed one.

Errors

Errors arrive in two places: a bad request is rejected at the POST, and a failure during generation surfaces on the poll. The POST returns one of these before any population is started: A rejected POST returns the standard error envelope. For example, an empty prompt:
422 Unprocessable Entity
Once the POST returns 200, a later failure appears on the poll as status: "failed" with error: "provider_error" (a stable, opaque failure category). Start a fresh population to retry. See Errors for request errors.

Example

Start the population, then poll its repository route until it reaches a terminal status and read the personas from result.
Because the field set is inferred per prompt, read the keys you need from each persona’s fields map rather than hard-coding them — inspect blueprint.fields to see which fields a given population carries. The diversity and marginals reports are populated only for multi-persona populations (count > 1); a single persona omits them.

Next