{"openapi": "3.1.0", "info": {"title": "ACO API", "version": "1.0.0", "description": "ACO (Agentic Commerce Optimizer) scores one product detail page for SEO, AEO, GEO and agentic commerce and produces ready-to-paste fixes (JSON-LD, FAQ, rewritten copy, UCP templates, PIM feed). Search demand and intent come from the ListeningMind Data API, text generation and citation simulation from an LLM, and scores from a rule-based parser. E-commerce operators and consultants use it for the Korean, US and Japanese markets. It also offers a sitemap batch scan, a domain-level agent readiness scan and per-store product feeds. Workflow: list_jobs → diagnose_pdp → get_job → translate_result → build_ontology → get_ontology_job → reanalyze_job → get_job → download_feed_csv → record_outcome → list_stores → get_product_feed → start_batch_scan → scan_agent_readiness → delete_job", "contact": {"email": "m-support@listeningmind.com"}}, "servers": [{"url": "https://daas-aco.listeningmind.ai"}], "paths": {"/api/precheck": {"post": {"operationId": "check_page", "summary": "Check that a URL is a product page", "description": "Free check before diagnose_pdp: fetches the URL (or reads the pasted html) and says whether it is a product detail page. Returns {kind: pdp|home|list|other|error|blocked|unknown, stop, override, signals[{code,ko,en,ja}], msg{ko,en,ja}, pdp_score, status}. stop true means diagnose_pdp would stop without charge; override true means allow_non_pdp may be set. Uses no Data API or LLM. Read only, free. Usually followed by: diagnose_pdp.", "tags": ["read"], "x-lm-kind": "read", "x-lm-cost": "free", "responses": {"200": {"description": "Success"}, "default": {"description": "Error (application/problem+json or JSON with detail)"}}, "requestBody": {"required": false, "content": {"application/json": {"schema": {"type": "object", "properties": {"url": {"type": "string", "description": "Product page URL (http or https)."}, "html": {"type": "string", "description": "Rendered HTML of the page, instead of fetching url."}}, "required": []}}}}}}, "/api/diagnose": {"post": {"operationId": "diagnose_pdp", "summary": "Start PDP diagnosis", "description": "Starts a full diagnosis of one product detail page (PDP) for SEO, AEO, GEO and agentic commerce. Give a public url, or paste the rendered html (for pages that block bots or render with JS; url then only resolves images and links). Returns {job_id} at once; poll get_job until status is done. Uses ListeningMind Data API and several LLM calls; usually takes a few minutes. Identical settings (url or html, locale, lang, content_lang) within the cache period (6 hours by default) return the cached result instantly (stage 캐시 적중, result.cached true) unless vision or gsc_rows is set; use reanalyze_job for a forced fresh run. Before any paid step the page is checked: an error page (HTTP 4xx/5xx, error or blocked wording, empty page) ends with status error and error_code error_page; a home, listing or no-product page ends with error_code not_pdp and page_check (kind, signals). Neither is charged, scored or saved. Set allow_non_pdp true to diagnose a not_pdp page anyway (charged); error pages are never diagnosed. Use check_page first to see this for free. 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: Data API credits (keyword_info, cluster_finder, intent_finder, path_finder) plus several LLM calls. Cost follows the main keyword's search volume (the cluster and path lookups grow with it): about 10,500 credits (137 Lima) when the keyword has little or no search data, up to about 23,000 credits (299 Lima) at 3,000+ searches a month. The free precheck (/api/precheck) works out the main keyword, checks its volume (1 credit, cached 7 days) or reuses the measured use of the last run of the same product, and the start response carries that estimate_credits (the same value the confirm dialog shows); without a precheck the start assumes the high end. 0 when this member's own recent result is reused, the original cost for someone else's; 0 for pages that are not product pages (the run stops). Charged by actual use (credits.spent), with no cap; the estimate is multiplied by the hub's calibration factor from recent actual charges (estimate_calib in the start answer).. Usually followed by: get_job.", "tags": ["run"], "x-lm-kind": "run", "x-lm-cost": "Data API credits (keyword_info, cluster_finder, intent_finder, path_finder) plus several LLM calls. Cost follows the main keyword's search volume (the cluster and path lookups grow with it): about 10,500 credits (137 Lima) when the keyword has little or no search data, up to about 23,000 credits (299 Lima) at 3,000+ searches a month. The free precheck (/api/precheck) works out the main keyword, checks its volume (1 credit, cached 7 days) or reuses the measured use of the last run of the same product, and the start response carries that estimate_credits (the same value the confirm dialog shows); without a precheck the start assumes the high end. 0 when this member's own recent result is reused, the original cost for someone else's; 0 for pages that are not product pages (the run stops). Charged by actual use (credits.spent), with no cap; the estimate is multiplied by the hub's calibration factor from recent actual charges (estimate_calib in the start answer).", "responses": {"200": {"description": "Success"}, "default": {"description": "Error (application/problem+json or JSON with detail)"}}, "requestBody": {"required": false, "content": {"application/json": {"schema": {"type": "object", "properties": {"url": {"type": "string", "description": "Public product page URL (http or https). Required unless html is given. With html, it is only used to resolve images and links and to name the project."}, "html": {"type": "string", "description": "Rendered HTML of the product page (paste-HTML tab or bookmarklet), for pages the server cannot fetch. When given, it is analysed instead of a fresh fetch. Required unless url is given."}, "locale": {"type": "string", "description": "Market for all search data (demand, intents, CEPs, reference products, consumer prompts): kr, us or jp. Default kr. Changes keyword volumes, prompts and the citation simulation.", "enum": ["kr", "us", "jp"], "default": "kr"}, "lang": {"type": "string", "description": "Language of the generated analysis, diagnosis and commentary text: ko, en or ja. Default ko. Publishable copy follows content_lang instead.", "enum": ["ko", "en", "ja"], "default": "ko"}, "content_lang": {"type": "string", "description": "Language of publishable copy (rewritten copy, FAQ, JSON-LD text). ko, ja or en; any other value or omitted = detected from the page and market. Changes the fixes text, not the scores.", "enum": ["ko", "ja", "en"]}, "vision": {"type": "boolean", "description": "Read text inside product images with OCR/vision (ingredients, specs, nutrition locked in images). Adds facts that can change readiness, fixes and citation results; adds LLM cost. Default false. The UI shows this only on the URL tab.", "default": false}, "gsc_rows": {"type": "array", "description": "Optional Google Search Console query rows (objects with query, clicks, impressions, ctr, position) added as real query evidence (GSC intelligence stage). Not exposed in the web UI.", "items": {"type": "object"}}, "allow_non_pdp": {"type": "boolean", "description": "Diagnose a page that looks like a home, listing or non-product page anyway (charged as a normal run). Error pages are never diagnosed. Default false."}}, "required": []}}}}}}, "/api/batch": {"post": {"operationId": "start_batch_scan", "summary": "Batch scan product pages", "description": "Quick rule-based scan of many product pages from a sitemap or a URL list, ranked by search demand and score (biggest opportunity first). Returns {job_id}; poll get_job until done, the result has rows[] with url, keyword, overall, grade, geo_audit, agentic, geo, demand, priority. Fetches every page and one keyword_info call per page. 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 Data API keyword_info call per page; no LLM. Usually followed by: get_job.", "tags": ["run"], "x-lm-kind": "run", "x-lm-cost": "one Data API keyword_info call per page; no LLM", "responses": {"200": {"description": "Success"}, "default": {"description": "Error (application/problem+json or JSON with detail)"}}, "requestBody": {"required": false, "content": {"application/json": {"schema": {"type": "object", "properties": {"sitemap_url": {"type": "string", "description": "Sitemap URL. Product-like <loc> URLs are picked from it. Used only when urls is empty."}, "urls": {"type": "array", "description": "List of product page URLs.", "items": {"type": "string"}}, "locale": {"type": "string", "description": "Market for the per-page keyword_info demand used to rank pages: kr, us or jp. Default kr.", "enum": ["kr", "us", "jp"], "default": "kr"}, "lang": {"type": "string", "description": "UI language (ko, en, ja). Accepted but not used by the batch scan.", "enum": ["ko", "en", "ja"]}, "limit": {"type": "integer", "description": "Maximum pages to scan, 1-50 (values outside are clamped). Default 20; the web UI always sends 20.", "default": 20, "minimum": 1, "maximum": 50}}, "required": []}}}}}}, "/api/agent_scan": {"post": {"operationId": "scan_agent_readiness", "summary": "Scan domain agent readiness", "description": "Scans a whole domain (not one PDP) for agent operability with HTTP probes: discovery 20, access 30, operation 40, payment 10 points. Synchronous: returns {ok, host, score, grade, label, layers, items, priority, unmeasured, catalog}. No Data API credits, no LLM. 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: free (no Data API credits, no LLM); sends HTTP requests to the target site.", "tags": ["run"], "x-lm-kind": "run", "x-lm-cost": "free (no Data API credits, no LLM); sends HTTP requests to the target site", "responses": {"200": {"description": "Success"}, "default": {"description": "Error (application/problem+json or JSON with detail)"}}, "requestBody": {"required": true, "content": {"application/json": {"schema": {"type": "object", "properties": {"url": {"type": "string", "description": "Domain or URL of the store, e.g. www.example.com. https:// is added if missing."}}, "required": ["url"]}}}}}}, "/api/job/{jid}": {"get": {"operationId": "get_job", "summary": "Get job status and result", "description": "Reads one job: {id, status (running, done, error), stage, progress 0-100, partial (sections already finished), result, error}. Repeat until status is done or error. For a diagnosis the result holds scores, overall, grade, analysis, fixes (product_jsonld, faq_jsonld, ucp templates), prompts, citation, readiness, pim_csv, permalink and more. Read only, free. Waits up to about 45 s for 'status' to reach done, error; call again if not done. Usually followed by: translate_result, build_ontology, record_outcome, download_feed_csv.", "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": "jid", "in": "path", "required": true, "description": "Job id returned by diagnose_pdp, start_batch_scan or reanalyze_job (12 hex characters, e.g. 671bc1e9d902).", "schema": {"type": "string"}}]}, "delete": {"operationId": "delete_job", "summary": "Delete diagnosis", "description": "Deletes a stored diagnosis and its files. Returns {ok, deleted}. Needs the app token header when the server has one set (see auth). 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": "jid", "in": "path", "required": true, "description": "Job id returned by diagnose_pdp, start_batch_scan or reanalyze_job (12 hex characters, e.g. 671bc1e9d902).", "schema": {"type": "string"}}]}}, "/api/resolve": {"get": {"operationId": "resolve_project", "summary": "Resolve project run", "description": "Turns a project permalink (/p/{project}/{seq}) into a job id and lists every run of that project. Returns {ok, job_id, seq, project, product, runs[{seq, id, ts, overall, grade}]}; 404 with runs[] when the run is missing. Read only, free. Usually followed by: get_job.", "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": "project", "in": "query", "required": true, "description": "Project slug from the permalink, e.g. coupang-com-vp-products-9274162564.", "schema": {"type": "string"}}, {"name": "seq", "in": "query", "required": false, "description": "Run number (e.g. 1 or 01) or latest. Omit for the latest run.", "schema": {"type": "string"}}]}}, "/api/job/{jid}/translate": {"post": {"operationId": "translate_result", "summary": "Translate analysis", "description": "Translates the analysis text of a finished diagnosis into ko, en or ja and caches it. Publishable copy and measured queries are not translated. Returns {lang, patch, failed} (or identity:true when already in that language). Uses LLM calls. 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: LLM calls only (no Data API credits).", "tags": ["run"], "x-lm-kind": "run", "x-lm-cost": "LLM calls only (no Data API credits)", "responses": {"200": {"description": "Success"}, "default": {"description": "Error (application/problem+json or JSON with detail)"}}, "parameters": [{"name": "jid", "in": "path", "required": true, "description": "Job id returned by diagnose_pdp, start_batch_scan or reanalyze_job (12 hex characters, e.g. 671bc1e9d902).", "schema": {"type": "string"}}], "requestBody": {"required": false, "content": {"application/json": {"schema": {"type": "object", "properties": {"lang": {"type": "string", "description": "Target language.", "enum": ["ko", "en", "ja"], "default": "en"}}, "required": []}}}}}}, "/api/job/{jid}/diag_i18n": {"post": {"operationId": "get_diag_translation", "summary": "Diagnostic sections in the screen language", "description": "Returns the code-written diagnostic sections of one result (axis checks, AI Readability, LG GEO audit, food audit, gating, publish check) in English or Japanese: {lang, diag: {checks, ai_readability, geo_audit, food_audit, gating, publish_gate}}. Uses the app's translation memory first and caches the result per language. Published copy and measured search queries are never translated. 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": "jid", "in": "path", "required": true, "description": "Job id (12 hex characters).", "schema": {"type": "string"}}], "requestBody": {"required": true, "content": {"application/json": {"schema": {"type": "object", "properties": {"lang": {"type": "string", "enum": ["en", "ja"], "description": "Screen language."}}, "required": ["lang"]}}}}}}, "/api/job/{jid}/build_ontology": {"post": {"operationId": "build_ontology", "summary": "Build topic ontology", "description": "Builds a topic ontology for the job's keyword so the next reanalysis can use it. Reuses an existing ontology when one matches ({already: true, run_id, topic}); otherwise returns {ontology_job, topic, joined}. Poll get_ontology_job, then call reanalyze_job. 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,000 Data API finder credits for a new topic; free when an ontology already exists. Time: about 2 minutes. Usually followed by: get_ontology_job, reanalyze_job.", "tags": ["run"], "x-lm-kind": "run", "x-lm-cost": "about 40,000 Data API finder credits for a new topic; free when an ontology already exists", "responses": {"200": {"description": "Success"}, "default": {"description": "Error (application/problem+json or JSON with detail)"}}, "parameters": [{"name": "jid", "in": "path", "required": true, "description": "Job id returned by diagnose_pdp, start_batch_scan or reanalyze_job (12 hex characters, e.g. 671bc1e9d902).", "schema": {"type": "string"}}]}}, "/api/ontology_job/{oid}": {"get": {"operationId": "get_ontology_job", "summary": "Get ontology build status", "description": "Reads an ontology build started by build_ontology: {status, pct, msg, error}. Repeat until status is done or error. Read only, free. Waits up to about 45 s for 'status' to reach done, error; call again if not done. Usually followed by: reanalyze_job.", "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": "oid", "in": "path", "required": true, "description": "ontology_job id returned by build_ontology.", "schema": {"type": "string"}}]}}, "/api/job/{jid}/reanalyze": {"post": {"operationId": "reanalyze_job", "summary": "Reanalyze page", "description": "Re-runs the full diagnosis for a job's page, bypassing the cache, using the stored page snapshot (or the original URL). Use after an ontology build, or with vision true to add image OCR. Returns a new {job_id}; poll get_job. The new run is stored as the next run of the same project. Market, output language and source are taken from the original job; only vision can change. 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 diagnose_pdp (Data API credits and LLM calls). Usually followed by: get_job.", "tags": ["run"], "x-lm-kind": "run", "x-lm-cost": "same as diagnose_pdp (Data API credits and LLM calls)", "responses": {"200": {"description": "Success"}, "default": {"description": "Error (application/problem+json or JSON with detail)"}}, "parameters": [{"name": "jid", "in": "path", "required": true, "description": "Job id returned by diagnose_pdp, start_batch_scan or reanalyze_job (12 hex characters, e.g. 671bc1e9d902).", "schema": {"type": "string"}}], "requestBody": {"required": false, "content": {"application/json": {"schema": {"type": "object", "properties": {"vision": {"type": "boolean", "description": "Add image OCR/vision extraction. Default false.", "default": false}}, "required": []}}}}}}, "/api/job/{jid}/outcome": {"post": {"operationId": "record_outcome", "summary": "Record applied outcome", "description": "Marks a diagnosis as applied on the live page and optionally stores observed metrics, for the store rollup and feedback loop. Returns {ok, outcome}. 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": "jid", "in": "path", "required": true, "description": "Job id returned by diagnose_pdp, start_batch_scan or reanalyze_job (12 hex characters, e.g. 671bc1e9d902).", "schema": {"type": "string"}}], "requestBody": {"required": false, "content": {"application/json": {"schema": {"type": "object", "properties": {"applied": {"type": "boolean", "description": "True when the fixes were applied to the page."}, "note": {"type": "string", "description": "Optional free-text note."}, "metrics": {"type": "object", "description": "Optional observed metrics, GSC/GA shaped: clicks, impressions, ctr, position, conversions."}}, "required": []}}}}}}, "/api/job/{jid}/source.html": {"get": {"operationId": "get_source_snapshot", "summary": "Get page snapshot", "description": "Returns the captured product page HTML used for the diagnosis (scripts removed, base href set), as shown in the before/after preview. 404 when no snapshot was stored. 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": "jid", "in": "path", "required": true, "description": "Job id returned by diagnose_pdp, start_batch_scan or reanalyze_job (12 hex characters, e.g. 671bc1e9d902).", "schema": {"type": "string"}}]}}, "/api/job/{jid}/feed.csv": {"get": {"operationId": "download_feed_csv", "summary": "Download PIM feed CSV", "description": "Downloads the product attribute (PIM) feed generated for a finished diagnosis as CSV (pim_feed.csv). 404 {error: no_feed} when the result has none. 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": "jid", "in": "path", "required": true, "description": "Job id returned by diagnose_pdp, start_batch_scan or reanalyze_job (12 hex characters, e.g. 671bc1e9d902).", "schema": {"type": "string"}}]}}, "/api/jobs": {"get": {"operationId": "list_jobs", "summary": "List diagnosis history", "description": "Lists stored diagnoses, newest first: {items[{id, ts, project, project_seq, permalink, product, keyword, url, overall, grade, before_cited, after_cited, total, before_avg, after_avg, applied}]}. Shared by all users of this server. Read only, free. Usually followed by: get_job.", "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/stores": {"get": {"operationId": "list_stores", "summary": "List stores", "description": "Groups the history by domain: {items[{domain, count, applied, avg_overall, avg_lift, feed_url}]}. feed_url is the tokenized product feed path for get_product_feed. Read only, free. Usually followed by: get_product_feed.", "tags": ["read"], "x-lm-kind": "read", "x-lm-cost": "free", "responses": {"200": {"description": "Success"}, "default": {"description": "Error (application/problem+json or JSON with detail)"}}}}, "/feed/{store_id}/products.json": {"get": {"operationId": "get_product_feed", "summary": "Get store product feed", "description": "Returns the JSON product feed for one store domain (latest result per URL, up to 200): {store_id, generated, count, spec, products[{url, name, brand, keyword, updated, job_id, scores, overall, structured_data, conversational}]}. Needs the token from list_stores feed_url. 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": "store_id", "in": "path", "required": true, "description": "Store domain, e.g. www.oliveyoung.co.kr.", "schema": {"type": "string"}}, {"name": "token", "in": "query", "required": true, "description": "Feed token: the token query value in feed_url from list_stores.", "schema": {"type": "string"}}]}}, "/_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-aco.listeningmind.ai/agents.md", "description": "Agent guide"}}