The analyze API

POST /v1/analyze — the stateless synthesis over a set you provide. One call, synchronous, a few seconds. Authenticated with x-api-key.

Request

Provide a set of 3–50 companies, one of two ways:

Field Type Notes
texts { name, text }[] Raw clean text per company (≤ 50,000 chars each). Classified server-side. The simplest path.
companies ClassifiedCompany[] Pre-classified via the classify schema. Cheapest — skips server-side classification.
focal string Optional. A company name to center the read on.
axes FieldAxis[] Optional. Re-map the set onto a given frame instead of deriving one. The diff enabler — see below.

Provide either texts or companies (at least 3). A set of one or two can’t form a field.

curl -X POST https://api.stratless.com/v1/analyze \
  -H "x-api-key: sk_live_…" \
  -H "content-type: application/json" \
  -d '{
    "texts": [
      { "name": "Pinecone", "text": "…" },
      { "name": "Qdrant",   "text": "…" },
      { "name": "Weaviate", "text": "…" }
    ],
    "focal": "Pinecone"
  }'
import requests

res = requests.post(
    "https://api.stratless.com/v1/analyze",
    headers={"x-api-key": "sk_live_…"},
    json={
        "texts": [
            {"name": "Pinecone", "text": "…"},
            {"name": "Qdrant",   "text": "…"},
            {"name": "Weaviate", "text": "…"},
        ],
        "focal": "Pinecone",
    },
)
result = res.json()
const res = await fetch("https://api.stratless.com/v1/analyze", {
  method: "POST",
  headers: { "x-api-key": "sk_live_…", "content-type": "application/json" },
  body: JSON.stringify({
    texts: [
      { name: "Pinecone", text: "…" },
      { name: "Qdrant",   text: "…" },
      { name: "Weaviate", text: "…" },
    ],
    focal: "Pinecone",
  }),
})
const result = await res.json()

Response

A uniform envelope: { success, requestId, data, meta }. meta carries { count, cost } (cost = companies analyzed = credits spent). data is the field map:

Field Type Meaning
coherent boolean Is this one field, or a grab-bag? If false, stratless stops honestly rather than fabricate a field.
coherence_note string Why it’s coherent (or not).
axes FieldAxis[] Up to 3 determinant, independence-checked choices the market varies on.
axes_source 'derived' | 'provided' Whether stratless derived the frame or you passed one.
independence { score, label } How un-correlated the axes are across the set (ok / weak).
distribution Record<axis, Record<value, count>> Per-axis value counts — factual density.
companies PlacedCompany[] Each company’s coordinates, rivals, provenance, and confidence.
clusters Cluster[] Who occupies the same strategic position.
robustness { score, label } Cluster stability — two-signal agreement (high / moderate / low).
white_space Read[] Empty positions, each a hypothesis with the barrier named — never “opportunity.”
convergence Read[] Crowded positions.
focal FocalRead? Present when you passed focal.
dropped { name, page_type, note }[] Companies that couldn’t be placed, and why.
meta.calibration { remap, provisional } The receipt behind every confidence.score.

Types

FieldAxis     = { key, label, values: string[], rationale }
PlacedCompany = {
  name, coords: Record<string, string>,
  nearest: NearestRel[],
  basis, reliability,                        // provenance: what it was read from
  confidence: { label: 'high' | 'moderate' | 'low', score: number | null }
}
NearestRel    = { name, sim, confidence: 'high' | 'low' }  // high = direct, low = indirect
Read          = { where: Record<string, string>, note, barrier? }

Confidence is measured, not asserted

Each placement’s confidence.label is a bucket; its score is that bucket’s measured accuracy against expert-labeled maps — currently high ≈ 0.897, moderate ≈ 0.846. The score is a bucket frequency, not a per-placement probability, and it’s provisional until more fields are labeled. meta.calibration ships the receipt. A nearest rival is high (direct) only when the similarity clears a calibrated bar and is reciprocal — two witnesses.

Determinism and the diff

Everything runs at temperature 0. Pass a previous run’s axes back in, and both maps share a coordinate system — so you can diff them without storing anything. That’s the stateless delta: the caller holds the two runs, stratless holds nothing.

Errors

Errors use { success: false, requestId, error: { code, message, details? } }.

Status When
401 Missing or invalid API key.
402 Out of credits — details carries { limit, used, resetsAt }.
422 Invalid input (e.g. fewer than 3 companies).
429 Rate limited.