# LLM Pulse API — llms-full.txt Last generated: 2026-06-06 Full machine-readable reference for the LLM Pulse REST API + MCP integration. Generated from the same canonical source the support agent reads, so it stays in sync with the live service. ## Where to find more - Short index (this doc's sibling): https://llmpulse.net/api-docs/llms.txt - OpenAPI 3.0 spec: https://llmpulse.net/openapi.json - Swagger 2.0 spec: https://llmpulse.net/swagger.json - Postman collection: https://llmpulse.net/LLMPulse_API_v1.postman_collection.json - Human-readable docs (in-app, with playground): https://llmpulse.net/api-docs - MCP endpoint: POST https://api.llmpulse.ai/api/v1/mcp (same Bearer token) ## Authentication & conventions See the "Authentication" and "Conventions" sections of llms.txt for auth, rate limits, pagination, date handling, metric aliases, and error codes. The body below is the complete endpoint reference. --- # LLM Pulse API - Complete Endpoint Reference ## Metrics Endpoints These endpoints return aggregated analytics data from the metrics system. ### GET /api/v1/metrics/timeseries Returns time-series data for one or more metrics, broken down by actor (project and/or competitors). **Parameters:** - `project_id` (required) - `metrics` (optional, default: `mentions`): Comma-separated list of metrics - `granularity` (optional, default: `day`): `day`, `week`, or `month`. When set to `week` or `month`, the API returns one aggregated data point per period (per week starting Monday, or per month) instead of daily data points - `range` (optional): Number of days (7, 30, 90, 365). Alternative to from/to - `from`, `to` (optional): ISO8601 date range - `model`, `collection_id`, `country_code`, `language_code`, `prompt` (optional filters) - `prompt_type` (optional): Filter by search intent — `informational`, `navigational`, `commercial`, `transactional` - `brand_kind` (optional): Filter by brand kind — `brand` (your own brand/products), `brand_other` (competitors or other brands), `non_brand` (generic, no brand named) - `competitors` (optional): Comma-separated competitor IDs to include - `include_project` (optional, default: `true`): Include project brand in results **Available metrics:** - `mentions` - Count of brand mentions - `citations` - Count of brand citations - `responses` - Count of prompt executions (responses) - `mention_rate` - (mentions / responses) × 100, also known as Mention Rate - `visibility` - Alias for mention_rate (kept for backward compatibility) - `weighted_visibility` - Position-weighted visibility score - `ai_visibility_score` - Alias for weighted_visibility - `avg_position` - Average citation position - `avg_mention_position` - Average mention position - `citation_rate` - (citations / responses) × 100 - `net_sentiment` - (very_positive + positive − negative − very_negative) count - `sentiment_very_positive` - % of mentions with very positive sentiment - `sentiment_positive` - % of mentions with positive sentiment - `sentiment_neutral` - % of mentions with neutral sentiment - `sentiment_negative` - % of mentions with negative sentiment - `sentiment_very_negative` - % of mentions with very negative sentiment **Response:** ```json { "project_id": 1, "from": "2025-01-01T00:00:00Z", "to": "2025-01-31T23:59:59Z", "granularity": "day", "filters": { "metrics": ["mentions", "mention_rate"], "granularity": "day", "model": null, "collection_id": null, "country_code": null, "language_code": null, "prompt": null, "competitors": [], "include_project": true }, "series": { "mentions": [ { "actor": { "type": "project", "id": 1, "name": "My Brand", "domain": "mybrand.com" }, "metric": "mentions", "data": [ { "date": "2025-01-01T00:00:00Z", "value": 10 }, { "date": "2025-01-02T00:00:00Z", "value": 15 } ] }, { "actor": { "type": "competitor", "id": 2, "name": "Competitor A", "domain": "competitor.com" }, "metric": "mentions", "data": [ { "date": "2025-01-01T00:00:00Z", "value": 5 }, { "date": "2025-01-02T00:00:00Z", "value": 8 } ] } ], "mention_rate": [ ... ] }, "request_id": "uuid" } ``` ### GET /api/v1/metrics/summary Same as timeseries but adds aggregated summary statistics and position distribution. **Parameters:** Same as timeseries. **Additional response fields:** ```json { "series": { ... }, "summary": { "mentions": [ { "actor": { "type": "project", "id": 1, "name": "My Brand", "domain": "mybrand.com" }, "total": 150, "min": 0, "max": 25, "last": 18 } ] }, "position_distribution": { "position_1_count": 45, "position_2_count": 30, "position_3_plus_count": 25, "total_mentions": 100, "percentages": { "position_1": 45.0, "position_2": 30.0, "position_3_plus": 25.0 } } } ``` **Summary calculation:** - For count metrics (mentions, citations, responses): `total` = SUM of all values - For average metrics (avg_position, avg_mention_position, net_sentiment): `total` = AVERAGE - `min` / `max` = minimum/maximum daily value in the period - `last` = most recent day's value ### GET /api/v1/metrics/prompt_summary Returns paginated per-prompt aggregated metrics. Useful for dashboards that need metrics broken down by individual prompt. **Parameters:** - `project_id` (required) - `range` (optional): Number of days to look back. Alternative to from/to - `from`, `to` (optional): ISO8601 date range - `breakdown` (optional): Add dimensions to the output. Currently supports: `model`. When `breakdown=model`, each row includes a `model` field and metrics are broken down per (prompt, model) combination - `model`, `collection_id`, `country_code`, `language_code`, `prompt` (optional filters) - `prompt_type`, `brand_kind` (optional): Categorization filters — same values as `/metrics/timeseries` - `sort` (optional, default: `responses`): Sort field — `responses`, `mentions`, `citations`, `mention_rate` (or `visibility`), `citation_rate`, `avg_mention_position`, `avg_position` - `sort_dir` (optional, default: `desc`): Sort direction — `asc` or `desc` - `page` (optional, default: 1): Page number - `per_page` (optional, default: 50, max: 100): Results per page **Response (default, no breakdown):** ```json { "project_id": 1, "from": "2025-01-01T00:00:00Z", "to": "2025-01-31T23:59:59Z", "filters": { ... }, "sort": "responses", "sort_dir": "desc", "page": 1, "per_page": 50, "total": 115, "data": [ { "prompt_id": 123, "prompt_text": "best CRM software", "responses": 50, "mentions": 30, "citations": 15, "visibility": 60.0, "mention_rate": 60.0, "citation_rate": 30.0, "avg_mention_position": 2.35, "avg_position": 3.1 } ], "request_id": "uuid" } ``` **Response (with breakdown=model):** ```json { "project_id": 1, "from": "2025-01-01T00:00:00Z", "to": "2025-01-31T23:59:59Z", "filters": { ... }, "breakdown": "model", "sort": "responses", "sort_dir": "desc", "page": 1, "per_page": 50, "total": 230, "data": [ { "prompt_id": 123, "prompt_text": "best CRM software", "model": "chatgpt", "responses": 50, "mentions": 30, "citations": 15, "visibility": 60.0, "mention_rate": 60.0, "citation_rate": 30.0, "avg_mention_position": 1.85, "avg_position": 2.5 }, { "prompt_id": 123, "prompt_text": "best CRM software", "model": "perplexity", "responses": 40, "mentions": 25, "citations": 10, "visibility": 62.5, "mention_rate": 62.5, "citation_rate": 25.0, "avg_mention_position": 2.65, "avg_position": 4.0 } ], "request_id": "uuid" } ``` **Notes:** - Only brand metrics are returned (competitor_id IS NULL) — no competitor breakdown per prompt - All metrics are always returned (responses, mentions, citations, mention_rate, visibility, citation_rate, avg_mention_position, avg_position) - Standard filters (model, collection_id, country_code, language_code, prompt, prompt_type, brand_kind) still apply - `breakdown=model` can be combined with the `model` filter — e.g., `breakdown=model&model=chatgpt` would return only the ChatGPT rows per prompt - Without `breakdown`, total counts distinct prompts; with `breakdown=model`, total counts distinct (prompt, model) pairs ### GET /api/v1/metrics/sov Share of Voice — your brand's share of mentions compared to competitors. **Parameters:** Same as timeseries. **Response:** ```json { "project_id": 1, "from": "2025-01-01T00:00:00Z", "to": "2025-01-31T23:59:59Z", "granularity": "day", "over_time": [ { "actor": { "type": "project", "id": 1, "name": "My Brand" }, "data": [ { "date": "2025-01-01T00:00:00Z", "value": 45.5 }, { "date": "2025-01-02T00:00:00Z", "value": 47.3 } ] }, { "actor": { "type": "competitor", "id": 2, "name": "Competitor A" }, "data": [ ... ] } ], "current": [ { "actor": { ... }, "share": 45.5 }, { "actor": { ... }, "share": 35.2 } ], "breakdown": [ { "rank": 1, "actor": { ... }, "share": 45.5 }, { "rank": 2, "actor": { ... }, "share": 35.2 }, { "rank": 3, "actor": { ... }, "share": 12.1 }, { "rank": 4, "actor": { ... }, "share": 7.2 }, { "rank": 5, "actor": { "name": "Others" }, "share": 0.0, "others": true } ], "others": [] } ``` **SoV calculation:** - Per day: `(actor_mentions / total_mentions_all_actors) × 100` - `current`: SoV on the last day with data - `breakdown`: Top 4 actors individually + "Others" aggregate - `others`: Individual actors that fell into the "Others" group ### GET /api/v1/metrics/top_sources Lists domains most frequently cited in AI responses. **Additional parameters:** - `sort` (optional, default: `total_responses`): `total_responses`, `avg_mention_rate`, or `avg_visibility` - `query` (optional): Search domain by name (case-insensitive partial match) - `prompt_type`, `brand_kind` (optional): Categorization filters — same values as `/metrics/timeseries` - `page`, `per_page` (pagination) **Response:** ```json { "project_id": 1, "from": "...", "to": "...", "sort": "total_responses", "page": 1, "per_page": 20, "total": 150, "data": [ { "domain": "reddit.com", "total_responses": 45, "avg_visibility": 15.5, "avg_mention_rate": 15.5 } ] } ``` --- ### GET /api/v1/metrics/agent_traffic Aggregated AI bot traffic for a project (server-side bot crawlers — GPTBot, PerplexityBot, ClaudeBot, etc.). **Scale plan or above required**; lower tiers get `ERR_PLAN_REQUIRED`. **Parameters:** - `project_id` (required) - `range`, `from`, `to` — date range (defaults to last 30 days) - `bot` — filter by bot slug (e.g. `gptbot`, `claudebot`, `perplexitybot`) - `company` — filter by company (e.g. `openai`, `anthropic`, `google`) - `group_by` — `bot` (default) or `company` - `granularity` — `day` (default), `week`, `month` **Response:** ```json { "project_id": 1, "from": "2026-04-01", "to": "2026-04-27", "group_by": "bot", "granularity": "day", "totals": { "gptbot": 240, "claudebot": 88 }, "timeseries": { "gptbot": { "2026-04-26": 32, "2026-04-25": 28 }, "claudebot": { "2026-04-26": 12 } }, "request_id": "..." } ``` --- ## Dimensions Endpoints These endpoints return lists of entities and raw data records. ### GET /api/v1/dimensions/projects Lists all projects for the authenticated user. **Response:** ```json { "projects": [ { "id": 1, "name": "My Brand" }, { "id": 2, "name": "Another Brand" } ], "request_id": "uuid" } ``` ### GET /api/v1/dimensions/projects/:id Detailed info about a specific project. **Response:** ```json { "id": 1, "name": "My Brand", "url": "https://mybrand.com", "description": "Brand monitoring project", "matching_names": ["My Brand", "mybrand", "MB"], "industry": "saas", "business_model": "b2b_saas", "primary_products": "Product A, Product B", "target_audience": "Enterprises", "brand_voice": "Professional", "country_code": "US", "language_code": "en", "paused": false, "google_play_id": "com.mybrand.app", "app_store_id": "com.mybrand.app", "created_at": "2025-01-01T00:00:00Z", "stats": { "prompts_count": 50, "competitors_count": 5, "collections_count": 3 } } ``` ### GET /api/v1/dimensions/competitors Lists competitors for a project. **Parameters:** - `project_id` (required) - `include_project_brand` (optional, default: `false`): when `true`, prepends the main tracked brand into the same array with `actor_type=project` and `is_own=true`. The project's `domain` is normalized to a bare domain (scheme-less, no trailing slash) for parity with competitor entries. **Response:** ```json { "project_id": 1, "competitors": [ { "id": 1, "name": "Competitor A", "domain": "competitor.com" } ] } ``` **Response with `include_project_brand=true`:** ```json { "project_id": 1, "competitors": [ { "id": 1, "name": "My Brand", "domain": "mybrand.com", "actor_type": "project", "is_own": true }, { "id": 2, "name": "Competitor A", "domain": "competitor.com", "actor_type": "competitor", "is_own": false } ] } ``` ### GET /api/v1/dimensions/competitors/:id Detailed competitor info. **Response:** ```json { "id": 2, "project_id": 1, "brand_name": "Competitor A", "domain": "competitor.com", "matching_names": ["Competitor A", "CompA"], "google_play_id": "com.competitor.app", "app_store_id": "com.competitor.app", "color": "#FF5733", "created_at": "2025-01-01T00:00:00Z" } ``` ### GET /api/v1/dimensions/collections (alias: /tags) Lists tags/collections for a project. ```json { "project_id": 1, "collections": [ { "id": 1, "name": "Brand Monitoring" } ] } ``` ### GET /api/v1/dimensions/models Lists AI models that have execution data. ```json { "project_id": 1, "models": ["chatgpt", "perplexity", "gemini", "ai_overview", "ai_mode"] } ``` ### GET /api/v1/dimensions/locales Lists countries and languages with data. ```json { "project_id": 1, "countries": ["US", "GB", "DE"], "languages": ["en", "de", "es"] } ``` ### GET /api/v1/dimensions/prompts Lists prompts with pagination and filtering. **Parameters:** `project_id` (required), `page`, `per_page`, standard filters (incl. `prompt_type`, `brand_kind`), `from`, `to` **Response per item:** ```json { "id": 1, "prompt_text": "What are the best AI visibility tools?", "collection_id": 5, "collection_ids": [5, 12], "country_code": "US", "language_code": "en", "prompt_type": "commercial", "brand_kind": "non_brand", "last_executed_at": "2025-01-31T12:00:00Z" } ``` Note: `collection_id` is the primary tag. `collection_ids` is an array of ALL tags assigned to this prompt (a prompt can belong to multiple tags). ### GET /api/v1/dimensions/prompt_executions Lists prompt execution records. **Parameters:** `project_id` (required), `page`, `per_page`, standard filters, `from`, `to` **Response per item:** ```json { "id": 1001, "prompt_id": 1, "executed_at": "2025-01-31T12:00:00Z", "duration_ms": 2500, "success": true, "model": "chatgpt", "fan_out_queries": ["best CRM software 2025", "top CRM for startups"], "has_mention": true, "has_citation": false, "mentions_count": 1, "citations_count": 0 } ``` ### GET /api/v1/dimensions/mentions Lists brand mention records (project brand only). **Response per item:** ```json { "id": 2001, "name": "My Brand", "prompt_id": 1, "prompt_execution_id": 1001, "created_at": "2025-01-31T12:00:00Z" } ``` ### GET /api/v1/dimensions/citations Lists brand citation records (project brand only). **Response per item:** ```json { "id": 3001, "name": "My Brand", "domain": "mybrand.com", "prompt_id": 1, "prompt_execution_id": 1001, "url": "https://mybrand.com/page", "position": 1, "created_at": "2025-01-31T12:00:00Z" } ``` ### GET /api/v1/dimensions/competitor_mentions Lists competitor mention records. **Additional parameter:** `competitors` (optional, CSV of competitor IDs) **Response per item:** ```json { "id": 4001, "competitor_id": 2, "name": "Competitor A", "domain": "competitor.com", "prompt_execution_id": 1001, "created_at": "2025-01-31T12:00:00Z" } ``` ### GET /api/v1/dimensions/competitor_citations Lists competitor citation records. **Additional parameter:** `competitors` (optional, CSV of competitor IDs) **Response per item:** ```json { "id": 5001, "competitor_id": 2, "name": "Competitor A", "domain": "competitor.com", "prompt_execution_id": 1001, "url": "https://competitor.com/page", "position": 1, "created_at": "2025-01-31T12:00:00Z" } ``` ### GET /api/v1/dimensions/all_mentions Lists all mentions (brand + competitor) in a single unified endpoint. Each record includes an `actor_type` field (`project` or `competitor`). **Additional parameter:** `competitors` (optional, CSV of competitor IDs to filter competitor portion) **Response per item (project):** ```json { "id": 2001, "actor_type": "project", "name": "My Brand", "prompt_id": 1, "prompt_execution_id": 1001, "created_at": "2025-01-31T12:00:00Z" } ``` **Response per item (competitor):** ```json { "id": 4001, "actor_type": "competitor", "competitor_id": 2, "name": "Competitor A", "domain": "competitor.com", "prompt_id": 1, "prompt_execution_id": 1001, "created_at": "2025-01-31T12:00:00Z" } ``` ### GET /api/v1/dimensions/all_citations Lists all citations (brand + competitor) in a single unified endpoint. Each record includes an `actor_type` field (`project` or `competitor`). **Additional parameter:** `competitors` (optional, CSV of competitor IDs to filter competitor portion) **Response per item (project):** ```json { "id": 3001, "actor_type": "project", "name": "My Brand", "domain": "mybrand.com", "prompt_id": 1, "prompt_execution_id": 1001, "url": "https://mybrand.com/page", "position": 1, "created_at": "2025-01-31T12:00:00Z" } ``` **Response per item (competitor):** ```json { "id": 5001, "actor_type": "competitor", "competitor_id": 2, "name": "Competitor A", "domain": "competitor.com", "prompt_id": 1, "prompt_execution_id": 1001, "url": "https://competitor.com/page", "position": 2, "created_at": "2025-01-31T12:00:00Z" } ``` ### GET /api/v1/dimensions/sources Lists all extracted URLs from AI responses (not just brand URLs). **Additional parameter:** `source_type` (optional): `owned`, `competitor`, `third_party` **Response per item:** ```json { "id": 5001, "prompt_execution_id": 1001, "domain": "reddit.com", "url": "https://reddit.com/r/technology/...", "position": 2, "source_type": "third_party", "is_google_play_link": false, "is_app_store_link": false } ``` **source_type values:** - `owned`: URLs belonging to the project brand (is_client = true) - `competitor`: URLs belonging to a competitor - `third_party`: URLs belonging to neither brand nor competitors ### GET /api/v1/dimensions/agent_bots Returns the static catalog of AI bots that Agent Analytics can identify (slugs, display names, companies, categories, Cloudflare verified-bot category mapping, descriptions). Useful for rendering filter UIs that mirror our internal classification. ```json { "bots": [ { "slug": "gptbot", "name": "GPTBot", "company": "openai", "category": "ai_crawler", "cf_verified_category": "AI Crawler", "description": "OpenAI ChatGPT training crawler" } ], "companies": ["openai", "anthropic", "google", "..."], "request_id": "uuid" } ``` ### GET /api/v1/dimensions/sentiments Lists available sentiment categories (NOT sentiment records — for that, use /api/v1/sentiments). ```json { "project_id": 1, "sentiments": [ { "metric": "sentiment_very_positive", "key": "very_positive", "label": "Very Positive", "color": "#16a34a" }, { "metric": "sentiment_positive", "key": "positive", "label": "Positive", "color": "#86efac" }, { "metric": "sentiment_neutral", "key": "neutral", "label": "Neutral", "color": "#d1d5db" }, { "metric": "sentiment_negative", "key": "negative", "label": "Negative", "color": "#fca5a5" }, { "metric": "sentiment_very_negative", "key": "very_negative", "label": "Very Negative", "color": "#dc2626" } ] } ``` --- ## Citation Intelligence Endpoints These endpoints expose the richer cited-page intelligence already available in the web app: grouped URL/domain/host views, cached page metadata, mention evidence inside cited pages, occurrence lists, and sanitized cached content. ### GET /api/v1/citation_intelligence/groups Returns grouped citation intelligence by `url`, `domain`, or `host`. **Parameters:** - `project_id` (required) - `view` (optional, default: `url`): `url`, `domain`, or `host` (invalid values → `ERR_INVALID_PARAM`) - `page`, `per_page` (optional pagination) - `order` (optional): one of `group_key`, `total_responses`, `total_citations`, `citation_rate`, `avg_citation_position`, `first_seen_at`, `last_seen_at` - `direction` (optional): `asc` or `desc` - `model` (optional): one of `chatgpt`, `ai_mode`, `ai_overview`, `perplexity`, `gemini` - `collection_id`, `country_code`, `language_code`, `prompt`, `from`, `to` (optional filters) - `query` (optional): text filter against the grouped key - `source_type` (optional): `owned`, `competitor`, `third_party`, `social_media`, `own_domain`, `ugc`, `background` - `owned`/`competitor`/`third_party` match the response `source_type` field (based on `is_client`/`is_competitor` flags) - `social_media`/`own_domain`/`ugc`/`background` use classification lists - `sentiment` (optional): currently supports `negative` - `content_gap` (optional): `mentioned` or `gap` - Invalid enum values on `view`/`order`/`direction`/`model`/`source_type`/`sentiment`/`content_gap` return `ERR_INVALID_PARAM`. - No default date range — omit `from`/`to` to query all history. **Response highlights:** - grouped citation counts and citation rate - `avg_citation_position` ignores rows with `position = 0` - per-model breakdown - first/last seen timestamps - for `view=url`: `url_sha256`, page-cache metadata, and page mention summary - for `view=domain|host`: aggregate URL/page-cache counts for the group ### GET /api/v1/citation_intelligence/urls/:url_sha256 Returns URL-level detail for one cited page. **Parameters:** - `project_id` (required) - `url_sha256` (required path param, must be 64 hex chars) - same optional filters as `/citation_intelligence/groups` **Behavior notes:** - `ERR_NOT_FOUND` ("Cited URL not found") is only returned when the URL has never been cited by the project (unfiltered). - If the URL exists but filters return zero occurrences, the endpoint returns `200` with empty `stats` (counts = 0) so clients can distinguish "no data with this filter" from "URL doesn't exist". **Response highlights:** - URL/domain/host identifiers - total responses and citations for the URL - citation rate and average citation position (ignores rows with `position = 0`) - source-type counts (`owned` / `competitor` / `third_party`) - per-model counts - distinct raw URL variants - page-cache metadata - page mention evidence, including snippets and actor labels ### GET /api/v1/citation_intelligence/urls/:url_sha256/occurrences Returns paginated occurrences of a cited URL across prompt executions. **Parameters:** - `project_id` (required) - `url_sha256` (required path param, must be 64 hex chars) - same optional filters as `/citation_intelligence/groups` - `page`, `per_page` (optional pagination) **Behavior notes:** - Returns `ERR_NOT_FOUND` only when the URL has never been cited by the project. If filters match nothing, returns `200` with `total: 0` and `data: []`. **Response highlights:** - `answer_id` / `prompt_execution_id` (currently identical — same execution, retained for forward compatibility) - `prompt_id` and `prompt_text` - `model` and `executed_at` - `citation_position` - `source_type` (`owned` / `competitor` / `third_party`) — matches the `source_type` filter vocabulary - response excerpt plus `response_truncated` - fan-out queries when available ### GET /api/v1/citation_intelligence/urls/:url_sha256/content Returns sanitized cached page content for one cited URL. **Parameters:** - `project_id` (required) - `url_sha256` (required path param, must be 64 hex chars) **Behavior notes:** - Returns `ERR_NOT_FOUND` only when the URL has never been cited by the project. **Response highlights:** - page-cache metadata - mention evidence and snippets - full cached plain text - sanitized rendered HTML (scripts and unsafe markup removed) --- ## AI Model Insights Report Endpoints These endpoints expose the aggregate parts of the web app’s AI Model Insights report. All `ai_model_insights` endpoints use `executed_at` (not `created_at`) when filtering by date range, consistent with the rest of the API. **Actor shape** (used across all AI Model Insights matrices): ```json { "type": "project" | "competitor", "id": 107, "competitor_id": null | 667, "name": "LLM Pulse", "domain": "llmpulse.ai" } ``` - `type` = `"project"` for the tracked brand, `"competitor"` for everyone else. - `id` = project ID or competitor ID. - `competitor_id` is retained for back-compat: `null` for the project, the competitor ID otherwise. - `domain` is always bare (scheme-less, no trailing slash). ### GET /api/v1/reports/ai_model_insights/summary Returns the aggregate AI Model Insights summary. **Parameters:** - `project_id` (required) - `range` (optional, default: `28`) - `from`, `to` (optional ISO8601 range) - `granularity` (optional): `day`, `week`, or `month`. Invalid values → `ERR_INVALID_PARAM`. - `collection_id`, `country_code`, `language_code` (optional filters) - `competitors` (optional): CSV list of competitor IDs to include in actor matrices. Unknown IDs → `ERR_INVALID_PARAM`. **Response highlights:** - per-model mention counts and shares - per-model citation counts and shares - per-model brand net sentiment with raw positive/negative counts - per-model weighted visibility totals and shares - actor matrices for mentions, citations, sentiment, and weighted visibility (each actor uses the standard actor shape above) - raw numerators/denominators behind percentages ### GET /api/v1/reports/ai_model_insights/position_distribution Returns the position-distribution comparison used in the AI Model Insights report. **Parameters:** - `project_id` (required) - `range` (optional, default: `28`) - `from`, `to` (optional ISO8601 range) - `collection_id`, `country_code`, `language_code` (optional filters) - `granularity` (optional): `day`, `week`, or `month`. Invalid values → `ERR_INVALID_PARAM`. - `model` (optional): restrict the comparison to one model. Invalid values → `ERR_INVALID_PARAM`. - `brand1`, `brand2` (optional): competitor IDs for the comparison brands; omit `brand1` to compare the project brand. Unknown IDs → `ERR_INVALID_PARAM`. **Response highlights:** - one or two brand series, each using the standard actor shape - bucketed position totals (`Position 1`, `Position 2-3`, `Position 4-7`, `Position 8+`) - chart-ready time series per bucket - `max_stacked_value` for chart scaling ### GET /api/v1/reports/ai_model_insights/ai_overview_results Returns aggregate Google AI Overview result-availability data. **Parameters:** - `project_id` (required) - `range` (optional, default: `28`) - `from`, `to` (optional ISO8601 range, filters by `executed_at`) - `collection_id`, `country_code`, `language_code` (optional filters) - `granularity` (optional): `day`, `week`, or `month`. Invalid values → `ERR_INVALID_PARAM`. - `page`, `per_page` (optional pagination for the by-prompt table) **Response highlights:** - total AI Overview responses - responses with results (where `no_result = false`) - result rate - chart-ready trend data - paginated by-prompt result-rate table --- ## Answers Endpoint ### GET /api/v1/answers Lists successful prompt execution responses with the full AI response text. **Parameters:** `project_id` (required), standard filters, `from`, `to`, `page`, `per_page` **Response per item:** ```json { "id": 1001, "prompt_id": 1, "prompt_text": "What are the best AI visibility tools?", "model": "chatgpt", "response": "AI visibility tools include...", "response_truncated": false, "executed_at": "2025-01-31T12:00:00Z", "duration_ms": 2500, "mentions_count": 1, "citations_count": 3 } ``` Note: In list view, responses are truncated to 10,000 characters. Use the show endpoint for full responses. ### GET /api/v1/answers/:id Full details for a specific execution including all mentions, citations, sentiments, and sources. **Parameters:** `project_id` (required), `id` (execution ID in URL), `include_source_page_details` (optional, default: `false`) **Response:** ```json { "id": 1001, "prompt_id": 1, "prompt_text": "What are the best AI visibility tools?", "model": "chatgpt", "response": "Full AI response text...", "executed_at": "2025-01-31T12:00:00Z", "duration_ms": 2500, "success": true, "fan_out_queries": ["targeted advertising strategies", "best ad platforms 2025"], "mentions": [ { "id": 2001, "keywords": ["My Brand"], "position": 1 } ], "citations": [ { "id": 3001, "url": "https://mybrand.com", "position": 1 } ], "competitor_mentions": [ { "id": 4001, "competitor_id": 2, "competitor_name": "Competitor A", "keywords": ["CompA"], "position": 3 } ], "competitor_citations": [ { "id": 5001, "competitor_id": 2, "competitor_name": "Competitor A", "url": "https://competitor.com", "position": 2 } ], "sentiments": [ { "id": 6001, "analysis": "positive", "score": 0.5, "comment": "Tool mentioned favorably", "topics": "innovation, reliability", "competitor_id": null, "is_brand_sentiment": true } ], "sources": [ { "id": 7001, "url": "https://reddit.com/r/...", "url_sha256": "abc123...", "normalized_url": "https://reddit.com/r/...", "domain": "reddit.com", "position": 1, "is_owned": false, "is_competitor": false } ], "shopping_products": [ { "id": 8001, "competitor_id": 2, "competitor_name": "Competitor A", "model": "chatgpt", "product_id_external": "prod-123", "title": "Competitor Product", "url": "https://competitor.com/product", "domain": "competitor.com", "price": 29.99, "currency": "USD", "rating_value": 4.6, "rating_max": 5.0, "rating_votes_count": 120, "images": ["https://competitor.com/image.jpg"], "tag": "popular", "rank_group": 1, "is_owned": false } ], "brand_entities": [ { "id": 9001, "competitor_id": null, "competitor_name": null, "model": "chatgpt", "title": "My Brand", "category": "brand", "markdown": "Short description here", "urls": ["https://mybrand.com/about"], "domain": "mybrand.com", "is_owned": true } ], "locale": { "country_code": "US", "language_code": "en" } } ``` If `include_source_page_details=true`, each source also includes nested `page_details` with page-cache metadata and mention evidence from the cited page. This endpoint is also the closest equivalent to Peec's `get_chat`, because it now includes the full response, fan-out queries, cited sources, shopping products, and extracted brand entities. --- ## Sentiments Endpoint ### GET /api/v1/sentiments Lists individual sentiment analysis records with full details. **Parameters:** - `project_id` (required) - `competitor_id` (optional): Filter by specific competitor - `brand_only` (optional): Set to `true` to only include brand sentiments (competitor_id = NULL) - `analysis` (optional): Filter by sentiment: `very_positive`, `positive`, `neutral`, `negative`, `very_negative` - Standard filters: `model`, `collection_id`, `country_code`, `language_code`, `from`, `to` - `page`, `per_page` (pagination) **Response per item:** ```json { "id": 6001, "prompt_execution_id": 1001, "prompt_text": "What are the best AI visibility tools?", "model": "chatgpt", "analysis": "positive", "score": 0.5, "comment": "Tool is reliable and feature-rich", "topics": "reliability, features", "competitor_id": null, "competitor_name": null, "is_brand_sentiment": true, "executed_at": "2025-01-31T12:00:00Z", "created_at": "2025-01-31T12:05:00Z" } ``` **Sentiment score values:** - Very Positive = 1.0 - Positive = 0.5 - Neutral = 0.0 - Negative = -0.5 - Very Negative = -1.0 **Filtering logic:** - Only returns sentiments where `hidden = false` and `analysis IS NOT NULL` - `brand_only=true` filters to `competitor_id IS NULL` - Multiple sentiments can exist per execution (one per brand + one per competitor) - The `topics` field is the closest equivalent to topic metadata in competitor tools such as Peec. In LLM Pulse, topic-like labeling lives primarily in collections/tags plus per-sentiment `topics`. --- ## Recommendations Endpoints These endpoints expose the same recommendation runs that power the in-app recommendations experience, in read-only form for API and MCP clients. ### GET /api/v1/recommendations Lists recommendation runs for a project. **Parameters:** - `project_id` (required) - `recommendation_type` (optional): `ai_visibility`, `social_community`, `brand_building`, `sentiment_reputation` (Scale+). Invalid values → `ERR_INVALID_PARAM`. - `status` (optional): `pending`, `processing`, `completed`, `failed`. Invalid values → `ERR_INVALID_PARAM`. - `page`, `per_page` (pagination) **Response per item:** ```json { "id": 1101, "project_id": 1, "recommendation_type": "brand_building", "status": "completed", "error_message": null, "generated_at": "2025-01-31T12:15:00Z", "created_at": "2025-01-31T12:00:00Z", "updated_at": "2025-01-31T12:15:00Z", "total_recommendations": 3, "high_priority_count": 1, "summary": { "total_recommendations": 3, "high_priority_count": 1 }, "context": { "domain": "mybrand.com" } } ``` ### GET /api/v1/recommendations/:id Returns one recommendation run with its items and report data. **Parameters:** - `project_id` (required) - `id` (required path param) - `item_status` (optional): `active`, `completed`, `archived`. Invalid values → `ERR_INVALID_PARAM`. - `resolve_source_refs` (optional, default: `true`): when `true`, each item includes resolved source references from the stored report indexes **Response highlights:** - top-level recommendation metadata, summary, context, and raw `report_data` - `item_counts` grouped by `active`, `completed`, and `archived` - `items` array with title, priority, action steps, metadata, and source references - `resolved_source_refs` for each item when enabled --- ## Prompts Endpoint ### POST /api/v1/prompts Add prompts to a project in bulk. Validates against account prompt limits and skips duplicates. **Request body (JSON):** ```json { "project_id": 123, "prompts": ["best CRM software 2026", "top project management tools"], "country_code": "US", "language_code": "en" } ``` | Parameter | Required | Description | |-----------|----------|-------------| | `project_id` | Yes | Project ID | | `prompts` | Yes | Array of prompt texts (max 100 per request) | | `country_code` | Yes | ISO country code (e.g. US, GB, DE) | | `language_code` | Yes | ISO language code (e.g. en, es, de) | **Response (201):** ```json { "project_id": 123, "created": 1, "skipped": 1, "total_after": 47, "prompts_available": 3, "data": [ { "id": 456, "raw_input": "best CRM software 2026", "status": "created" }, { "id": 400, "raw_input": "top project management tools", "status": "exists" } ], "request_id": "uuid" } ``` **Notes:** - Prompts that already exist in the project (same text + locale) are skipped with status "exists" - Returns `ERR_LIMIT_REACHED` if the account doesn't have enough prompt slots - Newly created prompts are automatically queued for execution across all AI models - No hard limit on prompt text length, but AI providers work best with ~500 characters or less --- ## Content Intelligence Endpoints ### POST /api/v1/intelligence_tasks Create a content intelligence task. The task is processed asynchronously in the background. **Request body (JSON) — prompt-based mode:** ```json { "project_id": 123, "task_type": "create", "prompt_id": 456, "user_instructions": "Focus on enterprise features and include competitor comparison", "output_language_code": "en" } ``` **Request body (JSON) — agentic mode (no prompt):** ```json { "project_id": 123, "task_type": "brief", "custom_topic": "AI visibility tools for agencies", "user_instructions": "Target mid-size marketing agencies", "output_language_code": "en" } ``` | Parameter | Required | Description | |-----------|----------|-------------| | `project_id` | Yes | Project ID | | `task_type` | Yes | One of: `brief`, `create`, `update`, `pr_insights`, `custom` | | `prompt_id` | No | Prompt ID to base the task on. Omit for agentic mode | | `custom_topic` | No | Topic for agentic mode (required if no prompt_id and no user_instructions) | | `user_instructions` | No | Custom instructions to refine the output | | `output_language_code` | No | Output language (default: project language) | | `existing_content` | No | Existing content to optimize (for `update` type) | | `existing_content_url` | No | URL of existing content (for `update` type) | **Response (201):** ```json { "id": 789, "public_id": "abc123def", "project_id": 123, "task_type": "create", "title": "Create: best CRM software 2026", "status": "pending", "prompt_id": 456, "prompt_text": "best CRM software 2026", "agentic_mode": false, "user_instructions": "Focus on enterprise features...", "output_language_code": "en", "estimated_time": "3-5 minutes", "created_at": "2026-04-08T10:00:00Z", "request_id": "uuid" } ``` **Notes:** - Returns `ERR_QUOTA_EXCEEDED` if the monthly task limit is reached - Tasks are processed asynchronously (2-5 minutes). Poll `GET /intelligence_tasks/:id` for status - Task types: `brief` (content brief), `create` (write article), `update` (optimize existing), `pr_insights` (media opportunities), `custom` (custom analysis) ### GET /api/v1/intelligence_tasks List content intelligence tasks for a project. **Parameters:** | Parameter | Required | Description | |-----------|----------|-------------| | `project_id` | Yes | Project ID | | `task_type` | No | Filter by type (brief, create, update, pr_insights, custom) | | `status` | No | Filter by status (pending, processing, completed, blocked, etc.) | | `page` | No | Page number (default: 1) | | `per_page` | No | Results per page (default: 20, max: 100) | **Response:** ```json { "project_id": 123, "page": 1, "per_page": 20, "total": 5, "data": [ { "id": 789, "public_id": "abc123def", "task_type": "create", "title": "Enterprise CRM Comparison Guide", "status": "completed", "prompt_id": 456, "prompt_text": "best CRM software 2026", "word_count": 2450, "created_at": "2026-04-08T10:00:00Z", "processed_at": "2026-04-08T10:03:00Z" } ], "request_id": "uuid" } ``` ### GET /api/v1/intelligence_tasks/:id Get a single intelligence task with full details and result data (when completed). **Parameters:** `project_id` (required), `:id` (task ID in URL) **Response:** ```json { "id": 789, "public_id": "abc123def", "project_id": 123, "task_type": "create", "title": "Enterprise CRM Comparison Guide", "status": "completed", "prompt_id": 456, "prompt_text": "best CRM software 2026", "agentic_mode": false, "custom_topic": null, "user_instructions": "Focus on enterprise features...", "output_language_code": "en", "word_count": 2450, "result_data": { "...": "full generated content as JSON" }, "error_message": null, "created_at": "2026-04-08T10:00:00Z", "processed_at": "2026-04-08T10:03:00Z", "request_id": "uuid" } ``` **Notes:** - `result_data` is only included when status is `completed` - `error_message` is only included when status is `blocked` (failed) - Accepts task ID (numeric) or public_id (string token) --- ## Write endpoints (mutations) All endpoints in this section require an API key with the `read_write` scope. A token with the `read` scope receives `403 ERR_INSUFFICIENT_SCOPE`. Mutation endpoints also share a tighter rate limit (60 req/min/key) in addition to the global 300 req/min/key budget. ### POST /api/v1/competitors Add a competitor to a project. **Request body (JSON):** ```json { "project_id": 123, "brand_name": "OpenAI", "domain": "openai.com", "matching_names": ["ChatGPT"] } ``` | Parameter | Required | Description | |-----------|----------|-------------| | `project_id` | Yes | Project ID | | `brand_name` | Yes | Competitor brand name | | `domain` | Yes | Primary domain (URL is accepted and normalised) | | `matching_names` | No | Extra brand aliases for mention matching (brand_name is auto-included) | **Response (201):** ```json { "project_id": 123, "competitor": { "id": 42, "brand_name": "OpenAI", "domain": "openai.com", "color": "#000000", "matching_names": ["OpenAI", "ChatGPT"] }, "competitors_remaining": 4, "total_competitors": 11, "request_id": "uuid" } ``` **Notes:** - Duplicates (same domain in the same project) return `ERR_INVALID_PARAM`. - Plan-cap reached returns `ERR_QUOTA_EXCEEDED`. - Triggers `Competitors::RecalculateAssociationsJob` for historical association backfill. ### POST /api/v1/collections Create a tag (Collection) in a project. Optionally attach existing prompts in the same call. **Request body (JSON):** ```json { "project_id": 123, "name": "pricing", "description": "Pricing related prompts", "prompt_ids": [10, 11, 12] } ``` | Parameter | Required | Description | |-----------|----------|-------------| | `project_id` | Yes | Project ID | | `name` | Yes | Tag name (case-insensitive unique per project) | | `description` | No | Optional description | | `prompt_ids` | No | Prompt IDs to attach (must belong to the project) | **Response (201):** ```json { "project_id": 123, "collection": { "id": 5, "name": "pricing", "description": "Pricing related prompts" }, "prompts_attached": 3, "total_collections": 7, "request_id": "uuid" } ``` ### POST /api/v1/prompts/assign_tags Attach tags to existing prompts in bulk. Idempotent: re-running with the same input does not duplicate assignments. **Request body (JSON):** ```json { "project_id": 123, "prompt_ids": [10, 11, 12], "tag_ids": [5], "tag_names": ["pricing"], "create_missing": false } ``` | Parameter | Required | Description | |-----------|----------|-------------| | `project_id` | Yes | Project ID | | `prompt_ids` | Yes | Prompt IDs to tag | | `tag_ids` | One-of | Tag IDs to attach | | `tag_names` | One-of | Tag names (case-insensitive lookup) | | `create_missing` | No | When true, unknown `tag_names` are created on the fly (default false) | **Response (200):** ```json { "project_id": 123, "prompts_targeted": 3, "tags_attached": [{ "id": 5, "name": "pricing" }], "new_links_created": 3, "skipped_already_linked": 0, "missing_tag_names": [], "ignored_prompt_ids": [], "request_id": "uuid" } ``` ### POST /api/v1/annotations Mark a date in the project timeseries with a title + description. Requires the **Growth** plan or above (`ERR_PLAN_REQUIRED` otherwise). **Request body (JSON):** ```json { "project_id": 123, "title": "Launched PR campaign", "annotation_date": "2026-05-19", "description": "Outreach to tech press", "color": "#2563eb", "annotation_category_id": 4 } ``` | Parameter | Required | Description | |-----------|----------|-------------| | `project_id` | Yes | Project ID | | `title` | Yes | Annotation headline | | `annotation_date` | No | ISO YYYY-MM-DD (defaults to today) | | `description` | No | Optional longer description | | `color` | No | Hex color (e.g. `#2563eb`) | | `annotation_category_id` | No | Must belong to the project | **Response (201):** ```json { "project_id": 123, "annotation": { "id": 7, "title": "Launched PR campaign", "annotation_date": "2026-05-19", "description": "Outreach to tech press", "color": "#2563eb", "annotation_category_id": 4 }, "request_id": "uuid" } ``` ### POST /api/v1/technical_geo_reports Run the full technical GEO analysis bundle (crawlability, schema, content readiness, discoverability, site structure, robots.txt, llms.txt, AI visibility) for a URL + country. **Request body (JSON):** ```json { "project_id": 123, "url": "https://example.com/pricing", "country_code": "US" } ``` | Parameter | Required | Description | |-----------|----------|-------------| | `project_id` | Yes | Project ID | | `url` | Yes | URL to analyse | | `country_code` | No | ISO country code (defaults to the project country) | **Response (201):** ```json { "project_id": 123, "url": "https://example.com/pricing", "country_code": "US", "created_reports": ["crawlability", "schema", "content_readiness", "discoverability", "site_structure", "robots_txt", "llms_txt", "ai_visibility"], "failed_reports": [], "request_id": "uuid" } ``` **Notes:** - Each report runs in a separate background job. Track progress via the reports section of the app. - A long-running, BrightData/OpenAI-heavy action — expect 3-10 minutes to full completion. --- ## Ping Endpoint ### GET /api/v1/ping Health check. Validates API key and optionally checks project access. **Parameters:** `project_id` (optional) **Response:** ```json { "ok": true, "user_id": 123, "project": { "id": 1, "name": "My Brand" }, "request_id": "uuid" } ``` --- ## MCP Endpoint ### POST /api/v1/mcp (and GET for the SSE handshake) Single JSON-RPC 2.0 entry point that implements the Model Context Protocol. AI clients (ChatGPT, Claude, Gemini, Cursor, Windsurf, Zed, custom MCP integrations) call this with `tools/list`, `tools/call` and resource methods to access the same data exposed by the REST API. **Availability:** - **OAuth 2.1**: available on **every LLM Pulse plan**, including Starter, Growth and the 14-day free trial. No API key required. The client discovers the flow via `GET /.well-known/oauth-authorization-server` (RFC 8414) and `GET /.well-known/oauth-protected-resource` (RFC 9728). Dynamic Client Registration is supported at `POST /oauth/register` (RFC 7591) with PKCE-S256. - **Bearer-token API key** (`Authorization: Bearer llmpulse_…`): available on **Scale plan and above**. Intended for headless integrations (Zapier, n8n, CI scripts). - Also accepts `?api_key=…` query-string for legacy MCP clients that cannot send Authorization headers — treat as higher risk because URLs are more likely to be logged. **Per-tool plan gating:** Tools declare `requires_plan` (e.g., `:growth`, `:scale`) and are filtered out of `tools/list` for users without access. Direct calls to a gated tool return `ERR_PLAN_REQUIRED`. Connecting via OAuth itself is free; only the tools you can call depend on your plan. **OAuth scopes:** `mcp:read` (default) and `mcp:write` (required for the write tools — `create_prompts`, `create_intelligence_task`, `create_competitor`, `create_collection`, `assign_prompt_tags`, `create_annotation`, `launch_recommendations`, `create_technical_geo_report`). **Discovery endpoints (read-only, public):** - `GET /.well-known/oauth-authorization-server` — OAuth 2.1 server metadata - `GET /.well-known/oauth-protected-resource` — Resource metadata advertised by the MCP server - `GET /oauth/jwks.json` — RS256 public keys for verifying issued JWT access tokens **OAuth endpoints:** - `POST /oauth/register` — Dynamic Client Registration (returns `client_id` + metadata) - `GET /oauth/authorize` — Consent page (PKCE-S256 required, `scope=mcp:read mcp:write`) - `POST /oauth/token` — `authorization_code` and `refresh_token` grants. JWT access tokens with 1-hour TTL; rotating refresh tokens with 30-day TTL. **Setup guide:** `https://llmpulse.ai/app/api_keys/mcp` — open on every plan. **Available tools:** see `integrations.md` for the full list, or call `tools/list` after connecting — the server returns only the tools your plan can use. --- ## MCP tools (live) Tools registered in `Mcp::LlmPulseServer::TOOLS` at the time this document was generated. The MCP server filters `tools/list` per user plan; tools that fail a plan check return `ERR_PLAN_REQUIRED`. - `assign_prompt_tags` - `create_annotation` - `create_collection` - `create_competitor` - `create_intelligence_task` - `create_prompts` - `create_technical_geo_report` - `get_agent_traffic` - `get_ai_model_position_distribution` - `get_ai_model_summary` - `get_ai_overview_results` - `get_answer` - `get_cited_url_content` - `get_cited_url_details` - `get_competitor_details` - `get_intelligence_task` - `get_project_details` - `get_prompt_summary` - `get_recommendation` - `get_sov` - `get_summary` - `get_timeseries` - `get_top_sources` - `launch_recommendations` - `list_agent_bots` - `list_all_citations` - `list_all_mentions` - `list_answers` - `list_citation_groups` - `list_citations` - `list_cited_url_occurrences` - `list_collections` - `list_competitor_citations` - `list_competitor_mentions` - `list_competitors` - `list_detailed_sentiments` - `list_intelligence_tasks` - `list_locales` - `list_mentions` - `list_models` - `list_projects` - `list_prompt_executions` - `list_prompts` - `list_recommendations` - `list_sentiments` - `list_sources` - `list_tags`