{
  "openapi": "3.1.1",
  "info": {
    "title": "Botlum API",
    "description": "Read what your Botlum dashboard shows: projects, prompts, visibility, answers and their sources.\n\n**Authentication.** Send `Authorization: Bearer <key>` with an API key from a workspace's settings\n(it reads that workspace only), or a token of a signed-in person.\n\n**Limits.** 60 requests a minute per key or person; over that the API answers 429 with `Retry-After`.\n\n**Stability.** Version 1 only adds fields. Ratios are 0..1 and null when nothing was measured — unknown is not zero.\nProvider names, model versions and costs are never part of a response.",
    "version": "1"
  },
  "servers": [
    {
      "url": "http://dash.botlum.com/"
    }
  ],
  "paths": {
    "/api/v1/workspaces": {
      "get": {
        "tags": [
          "Public API v1"
        ],
        "summary": "List workspaces",
        "description": "With an API key: the key's workspace. Signed in: every workspace you can open.",
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/v1/workspaces/{workspaceSlug}/projects": {
      "get": {
        "tags": [
          "Public API v1"
        ],
        "summary": "List projects",
        "description": "Each project is a brand with its domains, other names and competitors. Archived projects are left out unless asked for.",
        "parameters": [
          {
            "name": "includeArchived",
            "in": "query",
            "schema": {
              "type": "boolean"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/v1/workspaces/{workspaceSlug}/projects/{projectId}": {
      "get": {
        "tags": [
          "Public API v1"
        ],
        "summary": "Get a project",
        "parameters": [
          {
            "name": "projectId",
            "in": "path",
            "required": true,
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/v1/workspaces/{workspaceSlug}/projects/{projectId}/prompts": {
      "get": {
        "tags": [
          "Public API v1"
        ],
        "summary": "List the project's prompts",
        "description": "Every prompt with its text, country, language, status, topic and tags.",
        "operationId": "v1.slots.list",
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/v1/workspaces/{workspaceSlug}/projects/{projectId}/topics": {
      "get": {
        "tags": [
          "Public API v1"
        ],
        "summary": "List the project's topics",
        "description": "Topics group prompts; their order is the owners'.",
        "operationId": "v1.topics.list",
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/v1/workspaces/{workspaceSlug}/projects/{projectId}/visibility/overview": {
      "get": {
        "tags": [
          "Public API v1"
        ],
        "summary": "Visibility overview",
        "description": "Visibility of the brand and its competitors over the range, with counts, the change against the previous range of the same length (null when the two are not comparable, with reasons), share of voice, sentiment and a per-engine breakdown.",
        "operationId": "v1.visibility.overview",
        "parameters": [
          {
            "name": "from",
            "in": "query",
            "description": "Required. First day, yyyy-MM-dd.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "to",
            "in": "query",
            "description": "Required. Last day, yyyy-MM-dd. A range covers at most 92 days.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "engines",
            "in": "query",
            "description": "Comma-separated engine keys, e.g. `chatgpt,gemini`.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "countries",
            "in": "query",
            "description": "Comma-separated ISO 3166-1 alpha-2 country codes.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "languages",
            "in": "query",
            "description": "Comma-separated language codes.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "topics",
            "in": "query",
            "description": "Comma-separated topic ids; `none` means prompts without a topic.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "tags",
            "in": "query",
            "description": "Comma-separated prompt tags; a prompt with any of them counts.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "branded",
            "in": "query",
            "description": "`true` for prompts that name the brand, `false` for the others.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/v1/workspaces/{workspaceSlug}/projects/{projectId}/visibility/series": {
      "get": {
        "tags": [
          "Public API v1"
        ],
        "summary": "Visibility by day",
        "description": "One point per day and engine, and per day across all engines, for one brand.",
        "operationId": "v1.visibility.series",
        "parameters": [
          {
            "name": "from",
            "in": "query",
            "description": "Required. First day, yyyy-MM-dd.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "to",
            "in": "query",
            "description": "Required. Last day, yyyy-MM-dd. A range covers at most 92 days.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "engines",
            "in": "query",
            "description": "Comma-separated engine keys, e.g. `chatgpt,gemini`.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "countries",
            "in": "query",
            "description": "Comma-separated ISO 3166-1 alpha-2 country codes.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "languages",
            "in": "query",
            "description": "Comma-separated language codes.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "topics",
            "in": "query",
            "description": "Comma-separated topic ids; `none` means prompts without a topic.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "tags",
            "in": "query",
            "description": "Comma-separated prompt tags; a prompt with any of them counts.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "branded",
            "in": "query",
            "description": "`true` for prompts that name the brand, `false` for the others.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "brand",
            "in": "query",
            "description": "`project` (default) or `competitor:{id}`.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/v1/workspaces/{workspaceSlug}/projects/{projectId}/visibility/matrix": {
      "get": {
        "tags": [
          "Public API v1"
        ],
        "summary": "Visibility by prompt, country or topic",
        "description": "Rows by prompt, country or topic, one cell per engine, with a daily line for each row.",
        "operationId": "v1.visibility.matrix",
        "parameters": [
          {
            "name": "from",
            "in": "query",
            "description": "Required. First day, yyyy-MM-dd.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "to",
            "in": "query",
            "description": "Required. Last day, yyyy-MM-dd. A range covers at most 92 days.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "engines",
            "in": "query",
            "description": "Comma-separated engine keys, e.g. `chatgpt,gemini`.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "countries",
            "in": "query",
            "description": "Comma-separated ISO 3166-1 alpha-2 country codes.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "languages",
            "in": "query",
            "description": "Comma-separated language codes.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "topics",
            "in": "query",
            "description": "Comma-separated topic ids; `none` means prompts without a topic.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "tags",
            "in": "query",
            "description": "Comma-separated prompt tags; a prompt with any of them counts.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "branded",
            "in": "query",
            "description": "`true` for prompts that name the brand, `false` for the others.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "brand",
            "in": "query",
            "description": "`project` (default) or `competitor:{id}`.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "by",
            "in": "query",
            "description": "`prompt` (default), `country` or `topic`.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/v1/workspaces/{workspaceSlug}/projects/{projectId}/visibility/competitors": {
      "get": {
        "tags": [
          "Public API v1"
        ],
        "summary": "Brands side by side",
        "description": "The brand and each competitor over the range, overall and per engine.",
        "operationId": "v1.visibility.competitors",
        "parameters": [
          {
            "name": "from",
            "in": "query",
            "description": "Required. First day, yyyy-MM-dd.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "to",
            "in": "query",
            "description": "Required. Last day, yyyy-MM-dd. A range covers at most 92 days.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "engines",
            "in": "query",
            "description": "Comma-separated engine keys, e.g. `chatgpt,gemini`.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "countries",
            "in": "query",
            "description": "Comma-separated ISO 3166-1 alpha-2 country codes.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "languages",
            "in": "query",
            "description": "Comma-separated language codes.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "topics",
            "in": "query",
            "description": "Comma-separated topic ids; `none` means prompts without a topic.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "tags",
            "in": "query",
            "description": "Comma-separated prompt tags; a prompt with any of them counts.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "branded",
            "in": "query",
            "description": "`true` for prompts that name the brand, `false` for the others.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/v1/workspaces/{workspaceSlug}/projects/{projectId}/visibility/discovered-brands": {
      "get": {
        "tags": [
          "Public API v1"
        ],
        "summary": "Brands the answers name that the project does not track",
        "description": "Most frequent first, with how many answers named each and on which engines.",
        "operationId": "v1.visibility.discovered-brands",
        "parameters": [
          {
            "name": "from",
            "in": "query",
            "description": "Required. First day, yyyy-MM-dd.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "to",
            "in": "query",
            "description": "Required. Last day, yyyy-MM-dd. A range covers at most 92 days.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "engines",
            "in": "query",
            "description": "Comma-separated engine keys, e.g. `chatgpt,gemini`.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "countries",
            "in": "query",
            "description": "Comma-separated ISO 3166-1 alpha-2 country codes.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "languages",
            "in": "query",
            "description": "Comma-separated language codes.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "topics",
            "in": "query",
            "description": "Comma-separated topic ids; `none` means prompts without a topic.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "tags",
            "in": "query",
            "description": "Comma-separated prompt tags; a prompt with any of them counts.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "branded",
            "in": "query",
            "description": "`true` for prompts that name the brand, `false` for the others.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/v1/workspaces/{workspaceSlug}/projects/{projectId}/prompts/{slotId}/answers/{engine}/{measuredFor}": {
      "get": {
        "tags": [
          "Public API v1"
        ],
        "summary": "One answer",
        "description": "The answer one engine gave to one prompt on one day (yyyy-MM-dd): its text, the sources it cited, the searches the engine ran, which brands it named and how, and where they stood on the results page.",
        "operationId": "v1.visibility.evidence",
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/v1/workspaces/{workspaceSlug}/projects/{projectId}/prompts/{slotId}/searches": {
      "get": {
        "tags": [
          "Public API v1"
        ],
        "summary": "Searches the engines ran for a prompt",
        "description": "The web searches engines reported running while answering this prompt.",
        "operationId": "v1.sources.queries",
        "parameters": [
          {
            "name": "from",
            "in": "query",
            "description": "Required. First day, yyyy-MM-dd.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "to",
            "in": "query",
            "description": "Required. Last day, yyyy-MM-dd. A range covers at most 92 days.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "engines",
            "in": "query",
            "description": "Comma-separated engine keys, e.g. `chatgpt,gemini`.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "countries",
            "in": "query",
            "description": "Comma-separated ISO 3166-1 alpha-2 country codes.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "languages",
            "in": "query",
            "description": "Comma-separated language codes.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "topics",
            "in": "query",
            "description": "Comma-separated topic ids; `none` means prompts without a topic.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "tags",
            "in": "query",
            "description": "Comma-separated prompt tags; a prompt with any of them counts.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "branded",
            "in": "query",
            "description": "`true` for prompts that name the brand, `false` for the others.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/v1/workspaces/{workspaceSlug}/projects/{projectId}/sources/domains": {
      "get": {
        "tags": [
          "Public API v1"
        ],
        "summary": "Cited domains",
        "description": "Domains the answers cite, how often, and for which brands.",
        "operationId": "v1.sources.domains",
        "parameters": [
          {
            "name": "from",
            "in": "query",
            "description": "Required. First day, yyyy-MM-dd.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "to",
            "in": "query",
            "description": "Required. Last day, yyyy-MM-dd. A range covers at most 92 days.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "engines",
            "in": "query",
            "description": "Comma-separated engine keys, e.g. `chatgpt,gemini`.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "countries",
            "in": "query",
            "description": "Comma-separated ISO 3166-1 alpha-2 country codes.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "languages",
            "in": "query",
            "description": "Comma-separated language codes.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "topics",
            "in": "query",
            "description": "Comma-separated topic ids; `none` means prompts without a topic.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "tags",
            "in": "query",
            "description": "Comma-separated prompt tags; a prompt with any of them counts.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "branded",
            "in": "query",
            "description": "`true` for prompts that name the brand, `false` for the others.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/v1/workspaces/{workspaceSlug}/projects/{projectId}/sources/pages": {
      "get": {
        "tags": [
          "Public API v1"
        ],
        "summary": "Cited pages",
        "description": "Pages the answers cite, how often, and for which brands.",
        "operationId": "v1.sources.pages",
        "parameters": [
          {
            "name": "from",
            "in": "query",
            "description": "Required. First day, yyyy-MM-dd.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "to",
            "in": "query",
            "description": "Required. Last day, yyyy-MM-dd. A range covers at most 92 days.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "engines",
            "in": "query",
            "description": "Comma-separated engine keys, e.g. `chatgpt,gemini`.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "countries",
            "in": "query",
            "description": "Comma-separated ISO 3166-1 alpha-2 country codes.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "languages",
            "in": "query",
            "description": "Comma-separated language codes.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "topics",
            "in": "query",
            "description": "Comma-separated topic ids; `none` means prompts without a topic.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "tags",
            "in": "query",
            "description": "Comma-separated prompt tags; a prompt with any of them counts.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "branded",
            "in": "query",
            "description": "`true` for prompts that name the brand, `false` for the others.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/v1/workspaces/{workspaceSlug}/projects/{projectId}/sources/gap": {
      "get": {
        "tags": [
          "Public API v1"
        ],
        "summary": "Source gap",
        "description": "Domains cited in answers that name competitors but not the brand: where to get mentioned.",
        "operationId": "v1.sources.gap",
        "parameters": [
          {
            "name": "from",
            "in": "query",
            "description": "Required. First day, yyyy-MM-dd.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "to",
            "in": "query",
            "description": "Required. Last day, yyyy-MM-dd. A range covers at most 92 days.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "engines",
            "in": "query",
            "description": "Comma-separated engine keys, e.g. `chatgpt,gemini`.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "countries",
            "in": "query",
            "description": "Comma-separated ISO 3166-1 alpha-2 country codes.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "languages",
            "in": "query",
            "description": "Comma-separated language codes.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "topics",
            "in": "query",
            "description": "Comma-separated topic ids; `none` means prompts without a topic.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "tags",
            "in": "query",
            "description": "Comma-separated prompt tags; a prompt with any of them counts.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "branded",
            "in": "query",
            "description": "`true` for prompts that name the brand, `false` for the others.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/v1/workspaces/{workspaceSlug}/projects/{projectId}/sources/answers": {
      "get": {
        "tags": [
          "Public API v1"
        ],
        "summary": "Answers citing a domain",
        "description": "The answers behind one cited domain or page.",
        "operationId": "v1.sources.answers",
        "parameters": [
          {
            "name": "from",
            "in": "query",
            "description": "Required. First day, yyyy-MM-dd.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "to",
            "in": "query",
            "description": "Required. Last day, yyyy-MM-dd. A range covers at most 92 days.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "engines",
            "in": "query",
            "description": "Comma-separated engine keys, e.g. `chatgpt,gemini`.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "countries",
            "in": "query",
            "description": "Comma-separated ISO 3166-1 alpha-2 country codes.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "languages",
            "in": "query",
            "description": "Comma-separated language codes.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "topics",
            "in": "query",
            "description": "Comma-separated topic ids; `none` means prompts without a topic.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "tags",
            "in": "query",
            "description": "Comma-separated prompt tags; a prompt with any of them counts.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "branded",
            "in": "query",
            "description": "`true` for prompts that name the brand, `false` for the others.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "domain",
            "in": "query",
            "description": "The cited domain.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "page",
            "in": "query",
            "description": "A cited page, instead of a domain.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "gap",
            "in": "query",
            "description": "`true` for answers that name competitors but not the brand.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "description": "How many answers, at most.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    },
    "/api/v1/workspaces/{workspaceSlug}/projects/{projectId}/export/{kind}.csv": {
      "get": {
        "tags": [
          "Public API v1"
        ],
        "summary": "CSV export",
        "description": "`visibility`, `sources` or `brands` over the range, as the dashboard's CSV download.",
        "operationId": "v1.export.csv",
        "parameters": [
          {
            "name": "from",
            "in": "query",
            "description": "Required. First day, yyyy-MM-dd.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "to",
            "in": "query",
            "description": "Required. Last day, yyyy-MM-dd. A range covers at most 92 days.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "engines",
            "in": "query",
            "description": "Comma-separated engine keys, e.g. `chatgpt,gemini`.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "countries",
            "in": "query",
            "description": "Comma-separated ISO 3166-1 alpha-2 country codes.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "languages",
            "in": "query",
            "description": "Comma-separated language codes.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "topics",
            "in": "query",
            "description": "Comma-separated topic ids; `none` means prompts without a topic.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "tags",
            "in": "query",
            "description": "Comma-separated prompt tags; a prompt with any of them counts.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "branded",
            "in": "query",
            "description": "`true` for prompts that name the brand, `false` for the others.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearer": {
        "type": "http",
        "description": "A workspace API key (`bl_…`) or a signed-in person's token.",
        "scheme": "bearer"
      }
    }
  },
  "security": [
    {
      "bearer": [ ]
    }
  ],
  "tags": [
    {
      "name": "Public API v1"
    }
  ]
}