Every registered Simkl app has a built-in Analytics view with up to 24 hours of individual requests your app made againstDocumentation 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.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
Developer dashboard
Open simkl.com/settings/developer/ while logged in. The page lists every app you’ve registered.
Your app's row
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.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.
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 so we can’t attribute it to your app).
- Verify your sync loop matches the two-phase pattern 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:
- 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. - Filter on
Path contains /sync/all-itemsand look at the Query column for every row. Each one should includedate_from=<your_last_saved_timestamp>so Simkl returns only the delta. A/sync/all-itemsrow with nodate_frommeans your client is asking for the whole library — fix it before it gets throttled.
- Filter on
- 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, andUser-Agentyour library sent — useful for diagnosingempty_fieldandgrant_errorresponses. - 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/412etc. for every request — pair the code with /conventions/errors to map it back to the meaning. Response bodies aren’t recorded in the Debug view, but forGETrequests the Link column re-fires the same URL in a new browser tab so you can read the live response body.POST/PUT/DELETEbodies 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=200andOriginis 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 agentorClient IPto 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 aX% 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.allthe 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.
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)
Headers required to show up
See also
Get an API key
Developer dashboard — register a new app or jump to your existing apps’ Debug views.
Required headers
The
client_id / app-name / app-version / User-Agent reference.Rate limits
Per-app daily quotas, per-IP throttling, the 20-second write lock.
Errors
What each error envelope means when the Debug view shows a 4xx / 5xx row.