Skip to content
APIs & developers

ChatGPT Brand Monitoring API: Track Brand Mentions in ChatGPT via API

Build ChatGPT brand monitoring with an API: create an entity, add prompts, poll status, then read responses, competitors and trends. Python and Node examples.

Alessandro Benigni · Co-founder & CMO, GeoGen Updated 3 min read

Key takeaways

  • To monitor ChatGPT brand mentions through an API, create a tracked entity, add prompts, wait for the first check, then read responses, competitors and the visibility trend.
  • The GeoGen API is a REST API at api.geogen.io/v1 authenticated with a workspace key that starts with wsk_.
  • Poll the tracking-status endpoint to know when new data is ready, rather than polling the data endpoints.
  • Limits are 100 reads and 20 writes per minute per key, and 10 entity creations per hour per workspace.
  • The same data is available from the GeoGen CLI and from an MCP server for Claude, Cursor and other agents.
GeoGen public API request and JSON response for AI visibility data

A ChatGPT brand monitoring API runs your prompts against ChatGPT on a schedule and returns whether each answer mentions your brand, who else it names and which sources it cites. With the GeoGen API you create an entity, add prompts, poll tracking status, then read responses, competitors and the visibility trend, all as JSON.

What do you need before you start?

  • A GeoGen workspace and a workspace API key (it starts with wsk_). Create the key from your workspace in the GeoGen app.
  • Python 3.9+ with requests, or Node 18+ (which has fetch built in).
  • A domain to track and 5 to 10 prompts a buyer would ask ChatGPT.

Every request goes to https://api.geogen.io/v1 with an Authorization: Bearer wsk_... header. The full spec is at docs.geogen.io/openapi.json, and the GeoGen API page summarizes the endpoint groups.

Step 1: How do you find the ChatGPT model ID?

Entities are created with a list of model UUIDs, so start with GET /v1/models. Each item has id, uuid, name and description.

import os
import time
import requests

BASE = "https://api.geogen.io/v1"
HEADERS = {"Authorization": f"Bearer {os.environ['GEOGEN_API_KEY']}"}


def call(method, path, **kwargs):
    """Request helper that backs off on HTTP 429."""
    for _ in range(5):
        r = requests.request(method, f"{BASE}{path}", headers=HEADERS, timeout=30, **kwargs)
        if r.status_code == 429:
            time.sleep(int(r.headers.get("Retry-After", 30)))
            continue
        r.raise_for_status()
        return r.json()
    raise RuntimeError("Rate limited too many times")


models = call("GET", "/models")["data"]
chatgpt = next(m for m in models if "chatgpt" in m["name"].lower())
print(chatgpt["name"], chatgpt["uuid"])

The Node equivalent:

const BASE = "https://api.geogen.io/v1";
const headers = {
  Authorization: `Bearer ${process.env.GEOGEN_API_KEY}`,
  "Content-Type": "application/json",
};

async function call(method, path, body) {
  for (let attempt = 0; attempt < 5; attempt++) {
    const res = await fetch(`${BASE}${path}`, {
      method,
      headers,
      body: body ? JSON.stringify(body) : undefined,
    });
    if (res.status === 429) {
      const wait = Number(res.headers.get("Retry-After") ?? 30);
      await new Promise((r) => setTimeout(r, wait * 1000));
      continue;
    }
    if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
    return res.json();
  }
  throw new Error("Rate limited too many times");
}

const { data: models } = await call("GET", "/models");
const chatgpt = models.find((m) => m.name.toLowerCase().includes("chatgpt"));
console.log(chatgpt.name, chatgpt.uuid);

Step 2: How do you create an entity with prompts?

An entity is a tracked brand. POST /v1/entities requires name, domain and models. If you pass a prompts array, those prompts are used and auto-generation is skipped. Each prompt is a plain string or an object with its own language and geolocation. Tracking starts immediately unless you set startTracking to false.

created = call(
    "POST",
    "/entities",
    json={
        "name": "Acme Hosting",
        "domain": "acmehosting.example",
        "models": [chatgpt["uuid"]],
        "language": "en",
        "geolocation": "us",
        "prompts": [
            "What is the best web hosting for a small business?",
            "Which hosting providers are best for WordPress?",
            {"prompt": "Meilleur hébergement web pour une PME ?", "language": "fr", "geolocation": "fr"},
        ],
    },
)
entity_id = created["data"]["id"]
print(created["promptsGenerated"], created["trackingStarted"])
const created = await call("POST", "/entities", {
  name: "Acme Hosting",
  domain: "acmehosting.example",
  models: [chatgpt.uuid],
  language: "en",
  geolocation: "us",
  prompts: [
    "What is the best web hosting for a small business?",
    "Which hosting providers are best for WordPress?",
    { prompt: "Meilleur hébergement web pour une PME ?", language: "fr", geolocation: "fr" },
  ],
});
const entityId = created.data.id;
console.log(created.promptsGenerated, created.trackingStarted);

To add prompts later, use POST /v1/entities/addprompt with entityId and either prompt (one) or prompts (a bulk array of {prompt, language, geolocation}):

call("POST", "/entities/addprompt", json={
    "entityId": entity_id,
    "prompts": [{"prompt": "Cheapest reliable VPS hosting?"}],
})

Write prompts the way buyers ask, not the way marketers describe a category. Branded prompts tell you less than non-branded, high-intent ones such as “best X for Y”.

Step 3: How do you know when data is ready?

Poll GET /v1/entities/tracking-status. It returns scheduled, lastCheckCompletedAt (null until the first check finishes), nextCheckScheduledAt, isProcessing and frequencyHours. First data typically appears within minutes.

def wait_for_first_check(entity_id, every=20, attempts=30):
    for _ in range(attempts):
        status = call("GET", "/entities/tracking-status", params={"entityId": entity_id})["data"]
        if status["lastCheckCompletedAt"] and not status["isProcessing"]:
            return status
        time.sleep(every)
    raise TimeoutError("First check not finished yet")

print(wait_for_first_check(entity_id))
async function waitForFirstCheck(entityId, everyMs = 20000, attempts = 30) {
  for (let i = 0; i < attempts; i++) {
    const { data } = await call("GET", `/entities/tracking-status?entityId=${entityId}`);
    if (data.lastCheckCompletedAt && !data.isProcessing) return data;
    await new Promise((r) => setTimeout(r, everyMs));
  }
  throw new Error("First check not finished yet");
}
console.log(await waitForFirstCheck(entityId));

Once tracking runs daily, store lastCheckCompletedAt and only fetch data when it changes. That keeps you far below the read limit.

Three reads cover most dashboards. All accept entityId, a period of 7d, 14d or 30d, and an optional comma-separated models filter.

Responses with mention status. GET /v1/responses takes mentionStatus (all, mentioned or not-mentioned), limit and offset, and returns data plus pagination (total, limit, offset). The OpenAPI spec leaves the fields of each response item unspecified, so print one item and inspect it before you map fields. GET /v1/responses/details takes responseId and entityId and returns the mentions, citations and query fan-outs for a single response.

missing = call("GET", "/responses", params={
    "entityId": entity_id,
    "models": chatgpt["uuid"],
    "period": "7d",
    "mentionStatus": "not-mentioned",
    "limit": 25,
})
print(missing["pagination"]["total"], "answers without your brand")
print(missing["data"][0])  # inspect the shape first

Competitors. GET /v1/competitors returns a leaderboard. Each row has rank, entityUuid, domain, name, visibility (percentage of responses mentioning it), mentions, avgPosition, avgSentiment and isCurrentEntity.

board = call("GET", "/competitors", params={"entityId": entity_id, "period": "30d", "limit": 10})
for row in board["data"]:
    flag = "<- you" if row["isCurrentEntity"] else ""
    print(row["rank"], row["name"], row["visibility"], flag)

Visibility trend. GET /v1/trends/visibility returns {success, data: [{date, score}]}. Sentiment and citation trends live at /v1/trends/sentiment and /v1/trends/citations.

const trend = await call(
  "GET",
  `/trends/visibility?entityId=${entityId}&period=7d&models=${chatgpt.uuid}`
);
for (const { date, score } of trend.data) console.log(date, score);

const board = await call("GET", `/competitors?entityId=${entityId}&period=30d&limit=10`);
board.data.forEach((r) => console.log(r.rank, r.name, r.visibility, r.isCurrentEntity ? "<- you" : ""));

For cited sources, use GET /v1/entities/citations, then GET /v1/entities/citations/details with a citedEntityUuid to list the individual URLs behind a domain. The guide to tracking brand mentions in ChatGPT explains how to act on those results.

Can you skip the code? The CLI and MCP routes

CLI. Install it with npm install -g @geogenio/cli, run geogen login, then query the same data:

npm install -g @geogenio/cli
geogen login
geogen trends visibility --entity <id> --period 7d --json

Every command accepts --json, which makes it easy to pipe results into jq or a cron job.

MCP. The GeoGen MCP server lets Claude (Desktop or Code), Cursor, Windsurf, GitHub Copilot and OpenAI agents call the API as tools. Add it to your client with npx -y @geogenio/mcp-server and set GEOGEN_API_KEY:

{
  "mcpServers": {
    "geogen": {
      "command": "npx",
      "args": ["-y", "@geogenio/mcp-server"],
      "env": { "GEOGEN_API_KEY": "wsk_your_key" }
    }
  }
}

Then ask “How visible is Acme Hosting in ChatGPT this week, and who is ahead?” See the MCP server page for details.

What should you build with it?

  • A weekly Slack or email digest of visibility change and new competitors.
  • An alert when lastCheckCompletedAt moves and the visibility score drops.
  • A client report that merges visibility, citations and sentiment per brand.

If you are still choosing a data source, the comparison of AI visibility APIs covers the alternatives, and the API and developer hub lists related guides.

Frequently asked questions

Does ChatGPT have an API for brand monitoring?

No. OpenAI's API returns model output for prompts you send, not a report of how ChatGPT mentions your brand to users. Brand monitoring needs a service that runs your prompts on a schedule, stores each answer and reports mentions. GeoGen's API exposes that stored data for ChatGPT and 12 other engines.

How often is ChatGPT brand mention data updated?

GeoGen runs each tracked prompt every day, and first data appears minutes after you add a brand. The tracking-status endpoint returns when the last check completed, when the next one is scheduled and whether a check is running, so your code can sync on those timestamps.

How do I find the model ID for ChatGPT?

Call GET /v1/models with your API key. Each model in the data array has an id, a uuid and a name. Use the uuid in the models field when you create an entity, and in the models filter on read endpoints. Match on the name to pick ChatGPT.

What are the API rate limits?

Each API key can make 100 read requests and 20 write requests per minute, and a workspace can create 10 entities per hour. Exceeding a limit returns HTTP 429 with a retryAfter value and a Retry-After header. Higher limits are available on request.

Can I use the CLI or an AI agent instead of writing code?

Yes. Install the CLI with npm install -g @geogenio/cli and run commands such as geogen trends visibility, with a --json flag on every command. For agents, run the MCP server with npx -y @geogenio/mcp-server and your key in GEOGEN_API_KEY.

What does a mention check cost against my plan?

Runs consume workspace credits. One credit is one run of one prompt on an engine read as a user sees it, which includes ChatGPT, and engines queried through the model API cost more. Your plan sets the prompts and credits available each month.

Written by

Alessandro Benigni

Co-founder & CMO, GeoGen

Alessandro co-founded GeoGen and leads marketing and research. He has spent the last years measuring how AI engines choose the brands and sources they cite, across 15 engines and 30+ languages.

Be the brand AI recommends.

Start tracking how ChatGPT, Google AI Overviews, Gemini, Perplexity and 9 more AI engines talk about you. Results in minutes.