> ## Documentation Index
> Fetch the complete documentation index at: https://api.simkl.org/llms.txt
> Use this file to discover all available pages before exploring further.

# API Analytics

> Live per-app request log — see exactly what your code sends to api.simkl.com, filter it, export it.

Every registered Simkl app has a built-in **Analytics view** with **up to 24 hours** of individual requests your app made against `api.simkl.com`, plus a **7-day rollup** of daily request totals. It's the fastest way to confirm what your code is actually sending — without setting up your own logger.

## Opening it

<Steps>
  <Step title="Developer dashboard" icon="user-gear">
    Open **[simkl.com/settings/developer/](https://simkl.com/settings/developer/)** while logged in. The page lists every app you've registered.
  </Step>

  <Step title="Your app's row" icon="hand-pointer">
    Click the app to land on its page (URL: `simkl.com/settings/developer/{APP_ID}/`). This is also where your `Client ID` and `Client Secret` live.
  </Step>

  <Step title="Debug link" icon="bug">
    Click **Debug** (top-right of the app card). The analytics view opens in a new tab.
  </Step>
</Steps>

<Note>
  The page is opened via a short-lived signed link. The link **expires after 1 hour** — close and re-open the Debug link from your developer dashboard for a fresh session. The dashboard page is what authorizes you; the analytics URL itself isn't bookmarkable.
</Note>

## What you can do here

* **Confirm your code is actually hitting Simkl.** Issue a request from your app, hit Refresh — if it doesn't appear, your client never reached us (wrong host, blocked by a proxy, or missing the [required headers](#headers-required-to-show-up) so we can't attribute it to your app).
* **Verify your sync loop matches the [two-phase pattern](/guides/sync) the Sync guide requires.** Apps that don't follow it download the whole user library every poll and get rate-limited — sometimes blocked. The Debug view makes both halves easy to audit:
  1. Filter on `Path equals /sync/activities` — this is the cheap "did anything change?" check that has to fire **first** on every poll. If it isn't there, your loop is broken.
  2. Filter on `Path contains /sync/all-items` and look at the **Query** column for every row. Each one should include `date_from=<your_last_saved_timestamp>` so Simkl returns only the delta. A `/sync/all-items` row with no `date_from` means your client is asking for the whole library — fix it before it gets throttled.
* **See exactly what your OAuth library / SDK puts on the wire.** Wrappers and middleware obscure this; the Debug view shows the literal `client_id`, `app-name`, `app-version`, and `User-Agent` your library sent — useful for diagnosing `empty_field` and `grant_error` responses.
* **See the HTTP status Simkl returned, even when your client only shows a generic error.** Some HTTP libraries surface "Network error" or "Bad Request" without the actual status code. The **Edge** and **Origin** columns show `200` / `401` / `403` / `412` etc. for every request — pair the code with [/conventions/errors](/conventions/errors) to map it back to the meaning. Response bodies aren't recorded in the Debug view, but for `GET` requests the **Link** column re-fires the same URL in a new browser tab so you can read the live response body. `POST` / `PUT` / `DELETE` bodies still have to come from your own logging.
* **Spot retry loops, runaway timers, and duplicate calls.** The red **Burst** column and **High Traffic Burst Detected** banner surface "N requests in 1 second from the same IP" patterns automatically — find which path your code is over-firing before the auto-blocker does.
* **Tell edge cache hits from real backend responses.** When `Edge=200` and `Origin` is blank, the response came from the edge cache without touching the backend — useful for understanding why a stale value sometimes persists after a write.
* **Compare staging vs prod, or app A vs app B.** Filter on `User agent` or `Client IP` to slice traffic by deployment / machine and confirm each environment is sending what you think it is.
* **Export a CSV** of the current filtered view to drop into a spreadsheet, share with support, or attach to a bug report.

## Controls

| Control | What it does |
| - | - |
| **TIME** | Look-back window. Anywhere from `1h` to `24h`. Default `6h`. |
| **LIMIT** | Max events per load. Default `100`, max `1000`. |
| **Load** | Apply the current TIME + LIMIT and pull the events. |
| **Refresh** | Re-pull with the same settings. |
| **Mask** | One-click redact of sensitive query-string values (`code`, `token`, `access_token`, `refresh_token`, `redirect`, `redirect_uri`, `pin`) as `***` so screenshots and shared views don't leak secrets. Off by default. |
| **Copy ID** | Copies your `client_id` to the clipboard. |
| **Export** | Downloads the current (filtered) event list as CSV. |

## What each column shows

| Column | Meaning |
| - | - |
| **Time** | When the request hit Simkl. Defaults to **New York time** (EDT/EST); the column header shows the timezone label so you know what you're looking at. |
| **Burst** | Red `N` badge when N≥3 requests landed in the same second from the same IP. Single requests show nothing. |
| **Method** | `GET`, `POST`, etc. |
| **Scheme** | `https` (or `http` if your client is misconfigured). |
| **Path** | The endpoint hit. `/oauth/pin`, `/sync/all-items/...`, etc. |
| **Query** | Querystring as sent. Click the **Mask** control above to redact sensitive parameters (`code`, `token`, `access_token`, `refresh_token`, `redirect`, `redirect_uri`, `pin`) as `***` before screenshotting or sharing the view. Off by default. |
| **App Ver** | Value your client sent in the `app-version` query parameter. Column only appears if any visible event has it. |
| **Link** | Opens the full request URL in a new browser tab — re-fires the same `GET` against `api.simkl.com` and shows the response body inline. Handy for `GET` rows where you want to see the actual JSON your code received. (Doesn't replay POST / PUT / DELETE — the browser address bar can only issue GETs.) |
| **Edge** | HTTP status from Simkl's edge layer. |
| **Origin** | HTTP status from Simkl's backend. Usually the same as Edge; differs when the response was served from the edge cache without touching the backend (Edge `200`, Origin blank). |
| **Origin latency (ms)** | How long the backend took to produce the response, when measured. |
| **Cache status / Cache response** | Whether the request hit the edge cache, and what status it cached. |
| **Client IP** | A short opaque ID like `e-JgphVReMb9` — not your real IP. Same machine = same ID, so you can group "all the calls from this user" without exposing addresses. |
| **Country** | Where the request came from. |
| **User agent** | The `User-Agent` header your client sent — useful for telling staging and prod apart. |

## Burst detector

When 3+ requests from the same IP land in the same second, the page surfaces a red **High Traffic Burst Detected** banner at the top of the table plus a `X% BURST` badge near the event count. The Burst column highlights the specific rows that fired it.

The three usual culprits:

* **Retry loops without backoff** — fix with exponential backoff + jitter.
* **Parallel requests** — fire calls **sequentially**, awaiting each response before issuing the next. Don't `Promise.all` the same endpoint, don't let a new poll tick start before the previous one returned.
* **UI effects re-firing every render** — gate your `useEffect` / equivalent on a stable dependency.

Repeatedly tripping the banner can get your `client_id` throttled or temporarily blocked. Fix the loop before the auto-blocker fires — recovery isn't automatic.

## Filters

The **Filter results...** box does a free-text match across all visible columns.

**+ Add filter** opens a composer. Pick a column, an operator (`equals`, `not equals`, `contains`, `not contains`, `starts with`, `ends with`, `in`, `not in`), and a value. The value field autocompletes from what's actually in the data. Stack multiple filters — they AND together.

A few recipes:

* **Just failures** → `Origin contains 4` (catches 4xx)
* **One endpoint** → `Path equals /sync/history`
* **One country** → `Country equals US`
* **One machine** → `Client IP equals e-JgphVReMb9` (click the IP in any row to filter to its sibling requests)

**Exclude detail pages** is a one-click filter that hides per-record detail endpoints (typically the chattier ones), so the burst detector and the table focus on your sync/scrobble calls.

## Headers required to show up

<Warning>
  **Requests without identifying parameters don't appear in your analytics.** Simkl matches events to your app by the `client_id` it sees in the request — so every API call has to include:

  * `client_id` query parameter
  * `app-name` and `app-version` query parameters
  * `User-Agent` header in the form `my-app-name/1.0`

  See [Headers](/conventions/headers) for the full reference. The minimum that works:

  ```bash theme={"theme":{"light":"github-light","dark":"vesper"}}
  curl "https://api.simkl.com/<endpoint>?client_id=YOUR_CLIENT_ID&app-name=my-app-name&app-version=1.0" \
    -H "User-Agent: my-app-name/1.0"
  ```

  Calls without these get attributed to the anonymous bucket and never surface in your Debug view.
</Warning>

## The view your users get

The Analytics view is scoped to **your** app. Your users have the mirror image of it at [Connected Apps](https://simkl.com/settings/connected-apps/), which shows them every integration they have connected — yours among them.

| Analytics (this page) | Connected Apps (your user) |
| - | - |
| One app: yours | Every app they have connected |
| Individual requests, up to 24 hours, plus a 7-day rollup | Daily totals per app — today, yesterday, all-time |
| No view of their overall allowance | Today's requests against their daily allowance, and what is left |
| — | Each app's auth version and what it is permitted to do |

That matters for [AUTH V2](/api-reference/auth-v2), where the daily allowance belongs to the **user** and is [shared across every app they run](/resources/rate-limits#auth-v2-per-user). If a user reports that your app stopped syncing and your own numbers look normal, the cause is often another integration spending their allowance — which you cannot see and they can.

Ask them to open that page and sort by today's requests. It also names the apps responsible, so the conversation moves straight to the right place.

## See also

<CardGroup cols={2}>
  <Card title="Get an API key" icon="key" href="https://simkl.com/settings/developer/">
    Developer dashboard — register a new app or jump to your existing apps' Debug views.
  </Card>

  <Card title="Connected Apps" icon="user-check" href="https://simkl.com/settings/connected-apps/">
    What your users see: their daily allowance, and every app spending it.
  </Card>

  <Card title="Required headers" icon="list-check" href="/conventions/headers">
    The `client_id` / `app-name` / `app-version` / `User-Agent` reference.
  </Card>

  <Card title="Rate limits" icon="gauge-high" href="/resources/rate-limits">
    Per-app daily quotas, per-IP throttling, the 20-second write lock.
  </Card>

  <Card title="Errors" icon="triangle-exclamation" href="/conventions/errors">
    What each error envelope means when the Debug view shows a 4xx / 5xx row.
  </Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.