{"openapi": "3.1.0", "info": {"title": "CEP Finder API", "version": "1.0.0", "description": "Give one keyword (category, product, service or brand) and it finds category entry points (CEPs) from real search paths, related searches, search volumes, SERPs and AI Overviews, then ranks them in a report. Marketers and brand managers use it to choose campaign situations and messages. Data comes from ListeningMind DaaS search behavior and Google results for Korea, Japan and the US. Workflow: list_runs → resolve_sense → start_build → get_job → get_report → get_cep_map → download_report_pdf → prepare_rerun → delete_run", "contact": {"email": "m-support@listeningmind.com"}}, "servers": [{"url": "https://daas-cep.listeningmind.ai"}], "paths": {"/api/resolve": {"post": {"operationId": "resolve_sense", "summary": "Preview meaning (Stage 0)", "description": "Fixes what the keyword means before a build and proposes the category anchor, seed keywords, entry-situation (trigger) candidates and exclude terms that a person can edit. Use it before start_build when the keyword may be ambiguous or when you want to edit seeds; pass the (edited) result as `sense` to start_build. One LLM call, no Data API credits, usually 15-40 seconds. Spends ListeningMind Data API credits or paid model calls. Over MCP a person approves it first (you get a confirm link); in the browser the page asks the person. Cost: one LLM call (Gemini), no Data API credits. Time: 15-40 seconds. Usually followed by: start_build.", "tags": ["run"], "x-lm-kind": "run", "x-lm-cost": "one LLM call (Gemini), no Data API credits", "responses": {"200": {"description": "Success"}, "default": {"description": "Error (application/problem+json or JSON with detail)"}}, "requestBody": {"required": true, "content": {"application/json": {"schema": {"type": "object", "properties": {"keyword": {"type": "string", "description": "Category, product, service or brand to analyze, e.g. '유산균' or 'electric toothbrush'."}, "topic": {"type": "string", "default": "", "description": "Optional field or domain to pin the meaning when the word has several meanings."}, "gl": {"type": "string", "enum": ["kr", "jp", "us"], "default": "kr", "description": "Market whose search data is used: kr (Korea), jp (Japan), us (United States)."}}, "required": ["keyword"]}}}}}}, "/api/estimate/mine": {"get": {"operationId": "get_estimate", "summary": "Your run estimate", "description": "Expected Data API credits and Lima for one run, for the signed-in member: {credits, low, high, lima, lima_low, lima_high, n, basis, depth, gl}. When the same member ran the same keyword, market and depth in the last 24 hours, it subtracts the expected reuse of that result (basis=cache, the plain figure under plain); otherwise it is the median of recent actual charges for the market and depth. The same figure the run's start response returns and the hub reserves. Signed-in members only. The public plain estimate is the app's own GET /api/estimate (open to everyone, not a hub tool). Read only, free.", "tags": ["read"], "x-lm-kind": "read", "x-lm-cost": "free", "responses": {"200": {"description": "Success"}, "default": {"description": "Error (application/problem+json or JSON with detail)"}}, "parameters": [{"name": "depth", "in": "query", "required": false, "description": "quick | standard | deep (default standard)", "schema": {"type": "string"}}, {"name": "gl", "in": "query", "required": false, "description": "market: kr | jp | us (default kr)", "schema": {"type": "string"}}, {"name": "keyword", "in": "query", "required": false, "description": "the keyword to be analyzed; needed for the cache discount", "schema": {"type": "string"}}]}}, "/api/build": {"post": {"operationId": "start_build", "summary": "Start CEP analysis", "description": "Starts a CEP Finder run that collects related searches, search paths, volumes and SERPs for the keyword, synthesizes category entry points (CEPs) and scores them into a report. Returns job_id and status (queued or running); poll get_job until done. Runs execute one at a time; if 3 jobs are already running or queued the server answers HTTP 429. Spends many Data API credits (see cost) and LLM calls; ask the person before calling. Spends ListeningMind Data API credits or paid model calls. Over MCP a person approves it first (you get a confirm link); in the browser the page asks the person. Cost: about 40,450-63,785 (median about 48,624) Data API credits for standard (the default), about 18,550-20,002 (median about 19,663) for quick, about 35,821-76,776 (median about 61,740) for deep, plus LLM calls (medians of recent Korean runs, read from the app's /api/estimate). Time: several minutes; deep takes longest. Usually followed by: get_job.", "tags": ["run"], "x-lm-kind": "run", "x-lm-cost": "about 40,450-63,785 (median about 48,624) Data API credits for standard (the default), about 18,550-20,002 (median about 19,663) for quick, about 35,821-76,776 (median about 61,740) for deep, plus LLM calls (medians of recent Korean runs, read from the app's /api/estimate)", "responses": {"200": {"description": "Success"}, "default": {"description": "Error (application/problem+json or JSON with detail)"}}, "requestBody": {"required": true, "content": {"application/json": {"schema": {"type": "object", "properties": {"keyword": {"type": "string", "description": "Category, product, service or brand to analyze. Required, must not be empty."}, "topic": {"type": "string", "default": "", "description": "Optional field or domain that pins the meaning of an ambiguous keyword."}, "gl": {"type": "string", "enum": ["kr", "jp", "us"], "default": "kr", "description": "Market whose search data is used: kr (Korea), jp (Japan), us (United States)."}, "focal_brand": {"type": "string", "default": "", "description": "Optional reference brand for strength, weakness and opportunity judgments. Empty means the whole market is viewed at the same level."}, "competitors": {"type": "array", "items": {"type": "string"}, "default": [], "description": "Optional competitor brands compared in brand links and triage. Empty means competitors are found from the data. (The UI field is comma-separated and split into this list.)"}, "depth": {"type": "string", "enum": ["quick", "standard", "deep"], "default": "standard", "description": "Amount of collection and the target number of CEPs. quick: about 6 CEPs, search paths for 1 trigger anchor, no brand links. standard: about 10 CEPs, 2 trigger anchors, brand links. deep: about 25 CEPs, 4 trigger anchors, stronger brand links; slowest and most expensive."}, "sense": {"type": "object", "description": "Optional confirmed Stage-0 result from resolve_sense, sent whole with the person's edits (the UI's 'run with these settings' button). When given, the run skips its own meaning step and uses exactly this meaning, anchor, seeds, triggers and excludes; when omitted the pipeline resolves the meaning itself.", "properties": {"sense": {"type": "string", "description": "One-line fixed meaning of the keyword."}, "category_anchor": {"type": "string", "description": "Generic category noun used as the search anchor; changes which category the CEPs are measured against."}, "seeds": {"type": "array", "items": {"type": "string"}, "description": "Anchor plus modifier seeds (price, spec, type, channel...). These feed related-search collection and become KBF (choice criteria) axes, not CEPs."}, "trigger_candidates": {"type": "array", "items": {"type": "string"}, "description": "Entry-situation search terms (the CEP backbone), 15-24 noun phrases searched before the category, e.g. gift occasions or life events. Search paths are collected for the top ones depending on depth."}, "excludes": {"type": "array", "items": {"type": "string"}, "description": "Homonyms and adjacent categories to screen out of the data."}, "input_type": {"type": "string", "enum": ["category", "product", "service", "brand"], "description": "What kind of thing the keyword is; product, service or brand inputs are analyzed at their category level."}, "brand_of_input": {"type": "string", "description": "Brand the input belongs to, when input_type is product or brand."}, "category_note": {"type": "string", "description": "Note about the chosen category (shown in the report)."}, "category_options": {"type": "array", "items": {"type": "string"}, "description": "Alternative categories for multi-category brands (display only)."}, "ambiguous": {"type": "boolean", "description": "Whether the keyword had several meanings (display only)."}, "alternatives": {"type": "array", "items": {"type": "string"}, "description": "Meanings that were not chosen (display only)."}}}, "lang": {"type": "string", "enum": ["ko", "ja", "en"], "default": "ko", "description": "Language of the report commentary. Search terms and data stay in the market language."}, "ui_lang": {"type": "string", "enum": ["ko", "ja", "en"], "default": "ko", "description": "Language of progress log messages only; does not change the result."}}, "required": ["keyword"]}}}}}}, "/api/jobs/{job_id}": {"get": {"operationId": "get_job", "summary": "Job status", "description": "Returns one job's status (queued, running, done, error, canceled), its progress log (stage, pct, msg, t), error text, and when done the run_id with report_url and json_url. Repeat until status is done, error or canceled, then read the result with get_report or get_cep_map. Read only, free. Waits up to about 45 s for 'status' to reach done, error, canceled; call again if not done. Usually followed by: get_report, get_cep_map.", "tags": ["read"], "x-lm-kind": "read", "x-lm-cost": "free", "responses": {"200": {"description": "Success"}, "default": {"description": "Error (application/problem+json or JSON with detail)"}}, "parameters": [{"name": "job_id", "in": "path", "required": true, "description": "Job id returned by start_build (12 hex chars). Jobs live in server memory and are lost on restart.", "schema": {"type": "string"}}]}}, "/api/jobs": {"get": {"operationId": "list_jobs", "summary": "Active jobs", "description": "Lists jobs that are running or queued plus up to 4 jobs finished in the last 10 minutes, with keyword, depth, percent, queue position, error and report_url, and the active count against the limit of 3. Use it to see whether a new build will be accepted or to find a job started elsewhere. Read only, free.", "tags": ["read"], "x-lm-kind": "read", "x-lm-cost": "free", "responses": {"200": {"description": "Success"}, "default": {"description": "Error (application/problem+json or JSON with detail)"}}}}, "/api/jobs/{job_id}/cancel": {"post": {"operationId": "cancel_job", "summary": "Cancel job", "description": "Cancels a job. A queued job is removed at once; a running job stops at its next progress report. Credits already spent are not returned. Returns job_id, status and cancel_requested. Changes saved data in the app; no credits.", "tags": ["action"], "x-lm-kind": "action", "x-lm-cost": "free", "responses": {"200": {"description": "Success"}, "default": {"description": "Error (application/problem+json or JSON with detail)"}}, "parameters": [{"name": "job_id", "in": "path", "required": true, "description": "Job id returned by start_build (12 hex chars). Jobs live in server memory and are lost on restart.", "schema": {"type": "string"}}]}}, "/runs/{run_id}/report.html": {"get": {"operationId": "get_report", "summary": "Open CEP report", "description": "Returns the saved HTML report of a finished run: CEP map, priorities, triage, brand links, time series, evidence and readiness. Pass lang to re-render the same saved data in another language at no credit cost; download=1 returns it as an attachment. Read only, free. Usually followed by: get_cep_map, download_report_pdf.", "tags": ["read"], "x-lm-kind": "read", "x-lm-cost": "free", "responses": {"200": {"description": "Success"}, "default": {"description": "Error (application/problem+json or JSON with detail)"}}, "parameters": [{"name": "run_id", "in": "path", "required": true, "description": "Saved run id, e.g. 20261001_141110_keyword_58b6ff. Get it from get_job (run_id) or list_runs.", "schema": {"type": "string"}}, {"name": "lang", "in": "query", "required": false, "description": "Optional. Re-render the report in this language from the saved CEP map (no credits).", "schema": {"type": "string", "enum": ["ko", "ja", "en"]}}, {"name": "download", "in": "query", "required": false, "description": "1 returns the HTML as a file download.", "schema": {"type": "integer", "enum": [0, 1], "default": 0}}]}}, "/runs/{run_id}/cep_map.json": {"get": {"operationId": "get_cep_map", "summary": "CEP map data (JSON)", "description": "Returns the machine-readable cep_map.json of a finished run: meta (keyword, topic, sense, market, credits spent), the CEP list with metrics and evidence, triage and readiness verdict. Use this instead of the HTML report when you need the numbers. Read only, free.", "tags": ["read"], "x-lm-kind": "read", "x-lm-cost": "free", "responses": {"200": {"description": "Success"}, "default": {"description": "Error (application/problem+json or JSON with detail)"}}, "parameters": [{"name": "run_id", "in": "path", "required": true, "description": "Saved run id, e.g. 20261001_141110_keyword_58b6ff. Get it from get_job (run_id) or list_runs.", "schema": {"type": "string"}}]}}, "/runs/{run_id}/report.pdf": {"get": {"operationId": "download_report_pdf", "summary": "Download PDF", "description": "Converts the run's report to PDF with headless Chromium (cached per paper size) and returns the file. Use for sharing or printing; the first conversion can take up to about a minute. Read only, free.", "tags": ["read"], "x-lm-kind": "read", "x-lm-cost": "free", "responses": {"200": {"description": "Success"}, "default": {"description": "Error (application/problem+json or JSON with detail)"}}, "parameters": [{"name": "run_id", "in": "path", "required": true, "description": "Saved run id, e.g. 20261001_141110_keyword_58b6ff. Get it from get_job (run_id) or list_runs.", "schema": {"type": "string"}}, {"name": "paper", "in": "query", "required": false, "description": "Paper size.", "schema": {"type": "string", "enum": ["a4", "letter"], "default": "a4"}}]}}, "/api/runs": {"get": {"operationId": "list_runs", "summary": "Run history", "description": "Lists saved runs, newest first, with run_id, input keyword, topic, sense, market (gl), report language, focal brand, competitors, number of CEPs, readiness, credits spent, depth and creation time. Use it to find an existing analysis before starting a new one. Read only, free. Usually followed by: get_report, prepare_rerun.", "tags": ["read"], "x-lm-kind": "read", "x-lm-cost": "free", "responses": {"200": {"description": "Success"}, "default": {"description": "Error (application/problem+json or JSON with detail)"}}}}, "/api/runs/{run_id}/rerun": {"post": {"operationId": "rerun_run", "summary": "Run again with the same inputs", "description": "Starts a new run with the stored inputs of an earlier run (the whole pipeline again, data is fetched fresh). Spends Data API credits like start_build. Use prepare_rerun to read the inputs first; poll get_job with the returned job_id. Spends ListeningMind Data API credits or paid model calls. Over MCP a person approves it first (you get a confirm link); in the browser the page asks the person. Cost: same as start_build. Time: 5-20 minutes. Usually followed by: get_job.", "tags": ["run"], "x-lm-kind": "run", "x-lm-cost": "same as start_build", "responses": {"200": {"description": "Success"}, "default": {"description": "Error (application/problem+json or JSON with detail)"}}, "parameters": [{"name": "run_id", "in": "path", "required": true, "description": "Run id from list_runs.", "schema": {"type": "string"}}]}}, "/api/runs/{run_id}": {"delete": {"operationId": "delete_run", "summary": "Delete run", "description": "Permanently deletes a saved run and its report files. Returns {ok: true}, or 404 if the run does not exist. Deletes data. A person approves it first.", "tags": ["delete"], "x-lm-kind": "delete", "x-lm-cost": "free", "responses": {"200": {"description": "Success"}, "default": {"description": "Error (application/problem+json or JSON with detail)"}}, "parameters": [{"name": "run_id", "in": "path", "required": true, "description": "Saved run id, e.g. 20261001_141110_keyword_58b6ff. Get it from get_job (run_id) or list_runs.", "schema": {"type": "string"}}]}}, "/validation": {"get": {"operationId": "get_validation_report", "summary": "Validation report", "description": "Returns the post-hoc validation report (HTML) built from all saved runs; it is regenerated when a newer run exists. Use it to judge how reliable CEP Finder outputs have been. Reads saved runs only. Read only, free.", "tags": ["read"], "x-lm-kind": "read", "x-lm-cost": "free", "responses": {"200": {"description": "Success"}, "default": {"description": "Error (application/problem+json or JSON with detail)"}}}}, "/_lm/report-robots/{run_id}": {"get": {"operationId": "get_report_robots", "summary": "Report search visibility", "description": "Returns whether search engines may index this report page (index or noindex), and why: a company member's run or one set by hand is index; a customer's run, and any run that is still going, failed or came back empty, is noindex. Read only; no credits. Read only, free.", "tags": ["read"], "x-lm-kind": "read", "x-lm-cost": "free", "responses": {"200": {"description": "Success"}, "default": {"description": "Error (application/problem+json or JSON with detail)"}}, "parameters": [{"name": "run_id", "in": "path", "required": true, "description": "The report's run id: the id in the report address (for ACO the project id, or project~step).", "schema": {"type": "string"}}]}}}, "externalDocs": {"url": "https://daas-cep.listeningmind.ai/agents.md", "description": "Agent guide"}}