Docs

Multiple brands in the AIMentionTracker API

One key can cover many brands, so the API makes you say which one you mean. Here is why that is a refusal rather than a default, and how to give a client a key that cannot wander.

Card required, nothing charged for 7 days

Find the brand id first

Everything on this page starts with one request. The id it returns is what every other endpoint wants in ?brand= — on every endpoint in the API reference, and as --brand in the CLI.

curl
curl -H "X-API-Key: $AMT_API_KEY" https://app.aimentiontracker.ai/api/v1/brands
data is an array. Each row: (● = always present)
FieldTypeMeaning
id●stringThe workspace public id. Pass as ?brand=.
name●stringBrand name.
domainstring | nullThe brand's own domain, null before setup finishes.
configured●booleanFalse means setup was never finished. Data endpoints return 404 brand_not_configured for one of these rather than a page of zeroes, which would read as measured absence.
active_prompts●integerPrompts currently running for this brand.
marketobject—
market.location_codeinteger | nullDataForSEO location code.
market.language_codestring | nullISO language code.
last_collected_atstring | nullISO timestamp of the most recent stored answer.
GET /brands — response
{
  "data": [
    {
      "id": "wsp_zD6cssxGlJyF",
      "name": "Acme",
      "domain": "acme.com",
      "configured": true,
      "active_prompts": 5,
      "market": {
        "location_code": 2840,
        "language_code": "en"
      },
      "last_collected_at": "2026-09-30T04:12:00.000Z"
    },
    {
      "id": "wsp_094oxa5e",
      "name": "Acme Labs",
      "domain": null,
      "configured": false,
      "active_prompts": 0,
      "market": {
        "location_code": 2840,
        "language_code": "en"
      },
      "last_collected_at": null
    }
  ],
  "meta": {
    "generated_at": "2026-09-30T09:00:00.000Z",
    "pinned": false,
    "note": "Pass one of these ids as ?brand= on every other endpoint."
  }
}

Why omitting ?brand= is an error

When your organisation has one brand, leave ?brand= off and the API uses it. When it has two or more, omitting it returns 400 brand_required with a message naming how many there are.

We could have defaulted to the first brand, or the oldest, or the one most recently collected. Every one of those produces the same failure: a dashboard, a weekly email or a client report carrying one brand's numbers under another brand's name, with nothing anywhere in the response saying which one it actually described.

That error is invisible after the fact. Nobody finds it by reading the output, because the output looks exactly like a correct one. The cost of refusing is one extra request while you build. The cost of guessing is a wrong number nobody catches, in your customer's name, which is the specific thing this product exists to not do.

The fix
# 400 brand_required
curl -H "X-API-Key: $AMT_API_KEY" "https://app.aimentiontracker.ai/api/v1/visibility?days=30"

# 200
curl -H "X-API-Key: $AMT_API_KEY" "https://app.aimentiontracker.ai/api/v1/visibility?days=30&brand=wsp_zD6cssxGlJyF"
$ amt whoami
{"error":{"code":"brand_required","message":"This organisation has 2 brands. Pass ?brand=<id>; GET /v1/brands lists them. Run `amt brands` for the ids, then pass --brand.","status":400}}
The refusal, as a client actually receives it. The message names how many brands exist and the command that lists them.

Treat it as a setup step rather than an error to surface: call /brands once, keep the id, pass it from then on. Every response also carries meta.brand, so a client can label output with the brand the API says it described rather than the one it believes it asked for.

Pinned keys, for agencies

A key bound to one brand at the moment it is minted.

An organisation key can read every brand in the organisation. That is right for your own automation and wrong for anything you hand to a client or run per-client in CI, because the only thing separating one client's data from another's is a query parameter.

Pin the key instead, at API keys in the app. A pinned key:

  • Reads only its brand. Any other ?brand= is refused.
  • Cannot enumerate siblings. GET /brands returns a list of one. It does not learn that the organisation has others, so probing ids tells it nothing.
  • Says so. meta.pinned is true, and GET /me returns key.pinned_to_brand: true, so a client can display the constraint instead of inferring it.
  • Needs no ?brand=. The parameter is ignored rather than required, so the same code works pinned or not.

The practical shape for an agency: one pinned key per client, stored with that client's job. A misconfigured job then fails loudly with no_such_brand instead of quietly reporting on the wrong company. The same constraint is worth applying to a key you hand to an AI assistant through the MCP server.

Multi-brand AI visibility tracking in four steps

  1. 1

    List once, cache the ids

    GET /brands at startup. Store id against your own customer record, not the name — names change, ids do not.

  2. 2

    Skip what is not configured

    Rows with configured: falsehave no data. Show “setup unfinished”, never a zero.

  3. 3

    Pass ?brand= on every request

    Even when there is one brand today. The day a second is added, nothing breaks.

  4. 4

    Label from meta.brand

    Take the name on the output from the response, not from your own variable.

Knowing when a brand has data

last_collected_at is the timestamp of the most recent stored answer, and active_prompts is how many prompts are currently running. Together they answer the question a client actually has when a number looks low: is this brand quiet, or is it not collecting?

A brand with active_prompts: 0 is not being measured. A brand whose last_collected_atis three days old on a schedule that runs daily is worth flagging in your own interface before anyone reads a rate off it. Neither of those is an error from the API's point of view, which is exactly why a client should look.

Questions about brands and API keys

Answered from the implementation.

Can one AIMentionTracker API key read several brands?
Yes. A key belongs to the organisation, so by default it can read every brand in it. Pass ?brand=<id> on each request to choose. A key can also be pinned to a single brand at the moment it is minted, and then it can read only that one.
Why does the API return 400 brand_required instead of picking a brand?
Because a silent default files one client's numbers under another's, and nothing in the response says so. The report looks fine. It stays looking fine until somebody checks. Refusing costs one extra request at integration time and removes a whole class of error that is invisible after the fact.
How do pinned API keys work for agencies?
Pin a key to one brand when you mint it and it becomes a credential you can hand to that client, or run a per-client job with, that cannot be pointed anywhere else by changing a query parameter. GET /brands on a pinned key returns a list of one — it does not even learn that siblings exist — and meta.pinned is true.
How do I find a brand id?
GET /brands. The id field is what every other endpoint wants in ?brand=. It is an opaque public id, not a database integer, so it is safe to store and safe to put in a URL.
What happens if a brand has not finished setup?
It appears in GET /brands with configured: false, and every data endpoint returns 404 brand_not_configured for it. We return an error rather than a successful page of zeroes, because zeroes read as measured absence — a claim that the engines were asked and never named you.
Can I compare two brands I track in one request?
Not across brands, but you do not usually need to. GET /visibility returns a brands array for the brand you asked about, which includes the competitors configured against it with role: "competitor". Comparing two brands you separately own means two requests.

7-day trial

Track every brand you are responsible for

One organisation, as many brands as you manage, and a key per client that cannot be pointed at the wrong one.

Card required, nothing charged for 7 days.

On every plan

  • Every answer stored in full, with its citations.
  • Every rate shown with the number of checks behind it.
  • Cited-but-not-named reported as its own state.