universedocs

Prompt Universe API

Build a representative, versioned query universe for a category and market: the set of questions people ask AI. You start an asynchronous build, poll it, lock an immutable version, then read its manifest and queries by intent. Prompt Universe decides what to ask; running those queries and measuring visibility is a separate step.

Overview

The API is small and workspace-scoped. Every response is wrapped in a { data, meta } envelope; errors return a top level error object (see Errors). Builds run asynchronously on a worker, so creation returns immediately and you poll for progress.

Base URL

base url
https://api.universe.searchestra.com

Conventions

  • All request and response bodies are application/json.
  • Successful responses carry a top level data field, optionally with meta. Errors carry a top level error field.
  • Every /v1/* endpoint requires the X-API-Key header. /healthz and /metrics do not.
  • Identifiers are UUIDs; versions are semantic (MAJOR.MINOR.PATCH, for example 1.0.0).
  • Timestamps are UTC, ISO 8601 (for example 2026-08-19T13:47:09Z).
Scope boundary. Prompt Universe never runs the queries against AI platforms and never computes a visibility metric. It also never accepts a measurement result as a reason to regenerate (the information firewall): a locked universe is an input to measurement, not an output of it.

Lifecycle

A universe moves through a fixed lifecycle. Each build produces candidate queries, selects a representative subset with full intent coverage, and, once you are satisfied, gets locked into an immutable version.

lifecycle
create universe  ──▶  build (async pipeline)  ──▶  status: ready
     │                                                  │
     │                                            lock version ──▶  1.0.0 (immutable)
     │                                                  │
  refresh ◀───────────  new build  ◀── scheduled_review │ structural_market_change
                                                         ▼
                                        read manifest + queries (by intent)

A locked version never changes. To evolve a universe you trigger a refresh, which starts a new build that locks a new version, preserving the old one for historical comparison.

Quickstart

Create a universe, wait for the build, lock it, then read the queries. Replace un_your_key with the key issued to your workspace (see Authentication).

1 · Start a build

requestcurl
curl -X POST https://api.universe.searchestra.com/v1/universes \
  -H "X-API-Key: un_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "category": "project management software",
    "market": "TR",
    "language": "tr",
    "requested_query_count": 120
  }'
response201
{
  "data": {
    "id": "b3f1c2a4-...",
    "universe_id": "9a71e0d2-...",
    "status": "queued",
    "requested_query_count": 120,
    "created_at": "2026-08-19T13:47:09Z"
  }
}

2 · Poll the build

requestcurl
curl https://api.universe.searchestra.com/v1/universes/9a71e0d2-.../builds/b3f1c2a4-.../status \
  -H "X-API-Key: un_your_key"
response200
{
  "data": {
    "status": "running",
    "progress": { "stage": 4, "total_stages": 7, "percentage": 57 },
    "queries_target": 120,
    "queries_selected": 0,
    "candidates_generated": 210
  }
}

3 · Lock the version

requestcurl
curl -X POST https://api.universe.searchestra.com/v1/universes/9a71e0d2-.../builds/b3f1c2a4-.../lock \
  -H "X-API-Key: un_your_key"
response200
{
  "data": {
    "version": "1.0.0",
    "build_id": "b3f1c2a4-...",
    "universe_id": "9a71e0d2-...",
    "query_count": 120,
    "taxonomy_version": "1.0.0",
    "baseline": { "reset_required": false, "compatible_with_previous": null },
    "locked_at": "2026-08-19T13:59:41Z"
  }
}

4 · Read the queries

requestcurl
curl "https://api.universe.searchestra.com/v1/universes/9a71e0d2-.../versions/1.0.0/queries?intent=recommendation" \
  -H "X-API-Key: un_your_key"

Authentication

Every /v1/* request must carry an API key in the X-API-Key header. Keys begin with the un_ prefix and identify a workspace, not a user.

header
X-API-Key: un_your_key

Keys are provisioned per workspace by the workspaceinit command. The raw value is shown once, at creation, and cannot be recovered; the server stores only a hash. A workspace can hold several keys, and revoking one does not affect the others.

Diagnosing a key. Call GET /v1/me with your key to confirm which workspace it resolves to. A missing or revoked key returns 401 unauthorized.

Create a universe

POST/v1/universes

Creates a new universe and starts its first build asynchronously. Returns 201 with the build; the pipeline continues on a worker. Poll build status for progress.

Request body

FieldTypeNotes
category requiredstringThe category to sample, for example project management software.
market requiredstringMarket / geography code, for example TR.
language requiredstringLanguage the queries are written in, for example tr.
requested_query_count requiredintegerTarget size of the locked universe. Typically in the normal range (100–150).
control_plane optionalobjectSampling controls: intent_policy (balanced or observed_weighted), generation_budget, auto_lock, evidence_dataset_ids.
validation_context optionalobjectBrand context used only to exclude first-party bias, never to steer generation. Carries brand_name, aliases, domains.
Information firewall. validation_context.brand_name is used to keep the universe brand-neutral, not to generate flattering queries. A target brand is never fed into generation.

Build status

GET/v1/universes/{id}/builds/{build_id}/status

Returns the build's progress. Poll this until status reaches a terminal state (for example ready or failed).

response200
{
  "data": {
    "status": "ready",
    "progress": { "stage": 7, "total_stages": 7, "percentage": 100 },
    "queries_target": 120,
    "queries_selected": 120,
    "candidates_generated": 240
  }
}

To inspect the universe and its builds together, call GET /v1/universes/{id}. A per-build health summary is available at .../builds/{build_id}/health.

Lock a version

POST/v1/universes/{id}/builds/{build_id}/lock

Freezes a ready build into an immutable, semantically versioned universe. After locking, the version's manifest and queries can never change.

If a universe has exactly one active build, the alias POST /v1/universes/{id}/lock locks it without a build id. Locking a build that is not ready, or a universe with multiple active builds via the alias, returns 409 conflict.

baseline. The lock response includes a baseline block. When reset_required is true, downstream measurement must start a fresh baseline from this version and must not merge it with earlier trends. See Versioning & baseline.

Get the manifest

GET/v1/universes/{id}/versions/{version}/manifest

Returns the version's Universe Manifest: the immutable description of how this universe was sampled — taxonomy, intent distribution, coverage and provenance. This is the reference a measurement run pins to.

The alias GET /v1/universes/{id}/manifest returns the manifest of the latest locked version. Intent coverage is also summarised at GET /v1/universes/{id}/coverage.

List queries

GET/v1/universes/{id}/versions/{version}/queries

Returns the queries of a locked version. Every query carries complete, stable intent metadata, so you can segment measurement per intent.

Query parameters

NameTypeNotes
intent optionalstringFilter by one of informational, comparison, recommendation, transactional. See Intents.

The alias GET /v1/universes/{id}/queries lists queries from the latest locked version.

Refresh a universe

POST/v1/universes/{id}/refresh

Triggers a new build to evolve the universe. The old version stays locked and comparable; the new build locks a new version.

FieldTypeNotes
refresh_trigger requiredstringscheduled_review (a periodic review) or structural_market_change (the category itself shifted).
Forbidden triggers. A measurement outcome (for example "visibility dropped") is never a valid refresh reason. Such fields are rejected: a measurement system must not modify its own instrument.

Evidence datasets

POST/v1/evidence/datasets

Optional. Create a dataset of observed demand (real questions seen in the wild), then ingest items into it. When a build uses intent_policy: observed_weighted, these datasets ground the sampling so proportions reflect what the market actually asks.

create dataset
curl -X POST https://api.universe.searchestra.com/v1/evidence/datasets \
  -H "X-API-Key: un_your_key" \
  -H "Content-Type: application/json" \
  -d '{ "name": "TR PM search logs Q3", "source_summary": "anonymised site search" }'

Ingest items with POST /v1/evidence/datasets/{id}/items, then reference the dataset id in a universe's control_plane.evidence_dataset_ids.

Intents

Every query is classified into exactly one intent. Coverage across all four is planned on purpose, not left to chance.

IntentWhat the asker wants
informationalTo understand the category or a concept ("how does a kanban board work?").
comparisonTo weigh options against each other ("X vs Y for a small team").
recommendationTo be pointed to a good choice ("best tool for a 10-person startup").
transactionalTo act: pricing, plans, limits ("cheapest plan with SSO").

Versioning & baseline

Versions are immutable and semantic. A measurement run references a specific version, which guarantees historical comparability: the exact question set behind last quarter's numbers still exists, unchanged.

  • Immutable: once locked, a version's manifest and queries never change.
  • Baseline disclosure: a lock reports whether it is compatible_with_previous. When reset_required is true, measurement must begin a new baseline and not merge across the boundary.
  • Refresh: evolving a universe means a new version, never an edit to an old one.

Errors

Errors return an error object with a code, a message and optional details.

error envelope
{ "error": { "code": "unauthorized", "message": "X-API-Key required." } }
StatuscodeWhen
400validation_failedMissing or malformed field (for example no category, or an unknown refresh_trigger).
401unauthorizedMissing, invalid or revoked API key.
403forbiddenThe key's workspace may not perform this action.
404not_foundUnknown universe, build or version.
409conflict / duplicateLocking a build that is not lockable, or a conflicting concurrent change.
422quota_exceededGeneration budget (LLM calls or cost) for this workspace is spent.
429rate_limitedToo many requests in a short window. Retry shortly.
503unavailableA provider is not configured or temporarily unavailable.
500internalUnexpected internal failure. The real cause is only in server logs.

Budgets & limits

Because a build calls LLMs, each build runs under explicit guards, so a universe cannot cost or run without bound.

GuardScopeOn limit
Max LLM calls per buildper buildbuild stops; 422 quota_exceeded on create
Max cost (USD) per buildper buildcost guard halts the pipeline
Max durationper buildbuild is reaped and marked failed
Requests per minuteper API key429 rate_limited
First-party contribution capper buildown-domain evidence is capped, to prevent self-bias

Budgets scale with your plan. Exact defaults (query count, cost, duration) are set per deployment.