GET/api/v1/brands/{brandId}/stability
Being named once is luck. This endpoint tells you whether a brand is in the stable core of an answer, meaning it is named week after week, or rotating through it.
Each name is classified by the share of weekly cycles it appeared in: core at 70% or more, emerging from 40%, carousel below that. A placement needs 4 cycles to mean anything, so check isWarmingUp before acting on it.
Parameters
| Name | In | Type | Description |
|---|---|---|---|
brandId | path | string (UUID) | A brand id from GET /api/v1/brands. |
Request
curl https://corecited.com/api/v1/brands/7d1c2b9e-4a3f-4e61-9b2a-0c5d8e7f6a14/stability \
-H "Authorization: Bearer $CORECITED_API_KEY"// Node 18+ (global fetch). Run server-side: the key must never reach a browser.
const res = await fetch("https://corecited.com/api/v1/brands/7d1c2b9e-4a3f-4e61-9b2a-0c5d8e7f6a14/stability", {
headers: { Authorization: `Bearer ${process.env.CORECITED_API_KEY}` },
});
const body = await res.json();
if (!res.ok) {
// Branch on body.error.code, never on the message text.
throw new Error(`${res.status} ${body.error.code}: ${body.error.message}`);
}
const { data } = body;
console.log(data);import os
import requests
res = requests.get(
"https://corecited.com/api/v1/brands/7d1c2b9e-4a3f-4e61-9b2a-0c5d8e7f6a14/stability",
headers={"Authorization": f"Bearer {os.environ['CORECITED_API_KEY']}"},
timeout=30,
)
body = res.json()
if not res.ok:
# Branch on body["error"]["code"], never on the message text.
raise RuntimeError(f"{res.status_code} {body['error']['code']}: {body['error']['message']}")
data = body["data"]
print(data)Response
200 OK with the fields below inside data. Numbers are JSON numbers, dates are strings, and a field listed as null-able is always present, holding null when there is no value.
| Field | Type | Description |
|---|---|---|
brand | object | The brand this response is about. |
brand.id | string | The brand's id. Use it in every /brands/{brandId}/… path. |
brand.name | string | The brand name as configured in CoreCited. |
brand.domain | string | The brand's website exactly as entered during setup. This is often a full URL such as https://www.example.com/, not a bare host. |
minCycles | number | Cycles needed before a placement means anything. Currently 4 (four weekly cycles). |
prompts | object[] | Active prompts that have at least one cycle of data. Paused prompts are left out. |
prompts[].promptId | string | The prompt's id. |
prompts[].promptText | string | The question as tracked. |
prompts[].engines | object[] | One entry per engine this prompt has stability data for, alphabetical by slug. |
prompts[].engines[].slug | string | Engine slug: chatgpt, claude, gemini, perplexity, ai_overview, ai_mode, deepseek, grok. Which ones appear depends on the plan and on what has run. |
prompts[].engines[].cyclesObserved | number | Weekly cycles this prompt has run on this engine. |
prompts[].engines[].isWarmingUp | boolean | True while cyclesObserved is below minCycles. Treat placements as provisional until then. |
prompts[].engines[].brandPlacement | "core" | "emerging" | "carousel" | "absent" | Where your brand sits. core: named in at least 70% of cycles. emerging: 40–70%. carousel: under 40%. absent: never named. |
prompts[].engines[].brandStability | number | Your brand's share of cycles, 0–1. 0 when absent. |
prompts[].engines[].core | object[] | Names in the durable core (at least 70% of cycles), most stable first, at most 5. |
prompts[].engines[].core[].entity | string | The brand name as the engine wrote it. |
prompts[].engines[].core[].stability | number | Share of observed cycles this name appeared in, 0–1. |
prompts[].engines[].carousel | object[] | Every other name seen, including emerging ones, most stable first. |
prompts[].engines[].carousel[].entity | string | The brand name as the engine wrote it. |
prompts[].engines[].carousel[].stability | number | Share of observed cycles this name appeared in, 0–1. |
Example
200 OK
{
"data": {
"brand": {
"id": "7d1c2b9e-4a3f-4e61-9b2a-0c5d8e7f6a14",
"name": "Acme Resume",
"domain": "https://www.acmeresume.com/"
},
"minCycles": 4,
"prompts": [
{
"promptId": "c0a8f2d4-1b6e-4c3a-8f9d-2e7b5a1c4d90",
"promptText": "What is the best resume builder for career changers?",
"engines": [
{
"slug": "claude",
"cyclesObserved": 6,
"isWarmingUp": false,
"brandPlacement": "carousel",
"brandStability": 0.333,
"core": [
{
"entity": "Zety",
"stability": 1
},
{
"entity": "Enhancv",
"stability": 0.833
}
],
"carousel": [
{
"entity": "Kickresume",
"stability": 0.5
},
{
"entity": "Acme Resume",
"stability": 0.333
}
]
}
]
}
]
}
}Errors
| Status | Code | When |
|---|---|---|
| 401 | missing_key | No key was sent in Authorization or x-api-key. |
| 401 | invalid_key | The key is malformed, unknown, or has been revoked. |
| 402 | plan_required | The workspace is not on a plan that includes API access. This is checked on every request, so it also happens after a downgrade. |
| 404 | not_found | The brand id is not a UUID, does not exist, or belongs to another workspace. These cases are deliberately indistinguishable. |
| 429 | rate_limited | The workspace used up its requests for the current minute. All keys share one limit. |
| 500 | internal | Something failed on our side. The response carries no detail by design. |
Every error has the same body. See Errors for what to do about each one.
