> For the complete documentation index, see [llms.txt](https://docs.multiset.ai/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.multiset.ai/fundamentals/rest-api-docs/analytics.md).

# Analytics

Read localization query logs and spatial heatmaps over the REST API.

These endpoints expose the data behind the Analytics section of the Developer Portal, so you can pull the same numbers into your own dashboards, reports, or scheduled jobs. Two endpoints are available:

* **Query logs** — every localization request recorded for your account, newest first, with an aggregate recall summary.
* **Heatmap** — where inside a map or map set those requests came from, pre-binned and ready to render.

For the portal UI built on top of these, see [Analytics and Usage](/fundamentals/images-and-media.md).

{% hint style="info" %}
Both endpoints accept the codes you already use elsewhere in the API — `mapCode`, `mapSetCode`, and `objectCode`.
{% endhint %}

### Access

Both endpoints need a Bearer token, and are available to:

* **User tokens** with any role that carries read permission.
* **M2M clients** holding the `query` scope. See [Authentication](/fundamentals/rest-api-docs/authentication.md) for issuing an M2M token.

If you are calling from a browser, your site's domain must be registered against the account first, otherwise the request is rejected before it reaches the endpoint. See [Configuring Allowed Domains (CORS)](/fundamentals/credentials/configuring-allowed-domains-cors.md).

A code that does not belong to your account returns `404`, the same as one that does not exist.

### Query Logs

Returns individual localization requests with their timestamp, whether a pose was found, and — on successful requests — the resolved position, rotation, and confidence. Results are paginated, newest first.

Scope the log to a map, a map set, or an object:

| Target  | Parameter    | Example             |
| ------- | ------------ | ------------------- |
| Map     | `mapCode`    | `MAP_XZPGP5EPHGBX`  |
| Map set | `mapSetCode` | `MSET_7QJW2XKD8PLM` |
| Object  | `objectCode` | `OBJ_4KDMQ2WRTB91`  |

If you scope to more than one target, they are applied in the order map → map set → object and only the first takes effect. Supplying none returns every request made against a map. When neither date is given, the range defaults to the **last 30 days**.

Each returned record carries the code of whatever it was queried against — `mapCode`, `mapSetCode`, or `objectCode` — alongside its name, so you can group and label results without a second lookup.

A record that resolved a pose carries `position`, `rotation`, and `confidence`; one that did not omits those three fields entirely:

```json
[
  {
    "timestamp": "2026-08-31T16:42:11.804Z",
    "poseFound": true,
    "mapCode": "MAP_XZPGP5EPHGBX",
    "mapName": "Office Floor 3",
    "position": { "x": 2.4823, "y": 0.1193, "z": 8.0660 },
    "rotation": { "x": 0.0294, "y": 0.7673, "z": -0.0174, "w": 0.6402 },
    "confidence": 0.4592,
    "isRightHanded": false
  },
  {
    "timestamp": "2026-08-31T16:39:57.112Z",
    "poseFound": false,
    "mapCode": "MAP_XZPGP5EPHGBX",
    "mapName": "Office Floor 3"
  }
]
```

{% hint style="warning" %}
**Poses are not all in the same coordinate frame.** Each record is stored in whichever frame its own query asked for, so a single page can mix left-handed (Unity) and right-handed (ARKit, ARCore, WebXR) poses. Read `isRightHanded` on each record before comparing or plotting positions — `false`, the default, means left-handed Y-up. See [Coordinate Systems](/fundamentals/localization/coordinate-systems.md).
{% endhint %}

{% hint style="warning" %}
`summary` is **not** narrowed by the filter. It always reports account-wide totals for the date range, while `totalCount` and `logs` respect the filter — so the two disagree whenever you scope to a map, map set, or object. Use `totalCount` for the filtered count, and the heatmap endpoint's `summary` for per-map recall.
{% endhint %}

{% openapi src="/files/PxgzD3msG0ff8dkqLEeb" path="/account/analytics/query-api" method="get" %}
[analytics.yaml](https://3163433004-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FokTDI7QVY04Zvb1pQ8Ry%2Fuploads%2Fgit-blob-3c7d78553ed23ae8630cbfe0f7d9ec627369612f%2Fanalytics.yaml?alt=media)
{% endopenapi %}

### Localization Heatmap

Returns the spatial density of successful localizations. Only requests that resolved a pose contribute a position.

The single path segment takes either a **`mapCode`** or a **`mapSetCode`**, and the prefix decides which:

| You pass | You get                                                         |
| -------- | --------------------------------------------------------------- |
| `MAP_…`  | The heatmap for that one map.                                   |
| `MSET_…` | The combined heatmap for every query made against that map set. |

The response identifies its subject accordingly — `mapId`, `mapCode`, and `mapName` for a map; `mapSetId`, `mapSetCode`, and `mapSetName` for a map set.

Rather than shipping raw points, the API bins them into a uniform voxel grid and collapses each occupied cell to its centroid, so a few thousand requests arrive as a few hundred weighted points. Each point carries:

| Field              | Meaning                                                                                                                 |
| ------------------ | ----------------------------------------------------------------------------------------------------------------------- |
| `x`, `y`, `z`      | Centroid of the voxel, in render space.                                                                                 |
| `weight`           | Raw number of requests that fell in that voxel.                                                                         |
| `normalizedWeight` | Log-scaled density from 0 to 1, where the busiest voxel in the response is `1.0`. Feed this straight into a color ramp. |

`normalizedWeight` is log-scaled rather than linear so that low-traffic areas stay visible next to a dominant hotspot. A few entries from `positions` show the effect — a 522-to-1 spread in raw `weight` lands at 1.0 against 0.11, instead of collapsing to near zero:

```json
[
  { "x": 2.4901, "y": 0.1204, "z": 8.0617, "weight": 522, "normalizedWeight": 1 },
  { "x": 1.2837, "y": 1.6118, "z": -3.9072, "weight": 37, "normalizedWeight": 0.5758 },
  { "x": -3.0155, "y": 1.5522, "z": -6.8801, "weight": 1, "normalizedWeight": 0.1107 }
]
```

It is scoped to a single response — the busiest voxel is always 1.0 — so values are **not** comparable between two calls. To compare periods, use the raw `weight`.

Note also that `summary.total` counts every request, while only the `poseFoundTrue` ones contribute a position, so the weights sum to less than the total.

Control the resolution with `binSize` (metres, `0.05`–`10`, default `0.25`). Smaller bins give finer detail and more points; larger bins give coarser, cheaper output. When neither date is given, the range defaults to the **last 7 days**.

{% hint style="info" %}
**Coordinate space.** Unlike the query logs, this endpoint returns a single frame: every position is **right-handed (Three.js) render space**. Records captured left-handed — Unity, and anything that queried without `isRightHanded` — are converted server-side by negating `x` before binning, so both sources merge into one consistent set and no conversion is needed client-side. Binning them un-normalised would place one physical spot in two mirrored cells. See [Coordinate Systems](/fundamentals/localization/coordinate-systems.md) for the wider convention.
{% endhint %}

{% openapi src="/files/PxgzD3msG0ff8dkqLEeb" path="/account/analytics/heatmap/{code}" method="get" %}
[analytics.yaml](https://3163433004-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FokTDI7QVY04Zvb1pQ8Ry%2Fuploads%2Fgit-blob-3c7d78553ed23ae8630cbfe0f7d9ec627369612f%2Fanalytics.yaml?alt=media)
{% endopenapi %}


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.multiset.ai/fundamentals/rest-api-docs/analytics.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
