Analytics API
Ce contenu n’est pas encore disponible dans votre langue.
The Analytics API returns the month-by-month figures behind the Analytics page: transaction volume, rule hits by typology, rule performance, alert outcomes and real-time decision outcomes. Use it to feed a board pack, a management dashboard or your own data warehouse.
For today’s figures, use the Dashboard API instead.
Before you start
Section titled “Before you start”| Who can call it | A signed-in person with alerts.view (by default every compliance role; not the Administrator). API keys can’t call it |
| How to sign in with curl | As described in Rules API: before you start |
| Months | Calendar months in your workspace’s time zone, written YYYY-MM, exact in every time zone (half-hour and quarter-hour offsets included). The current month is always included and is incomplete until it ends |
| Money | Whole numbers of minor units in your base currency (pesewas for GHS): 48200000 means GHS 482,000.00 |
| Freshness | From the analytics store, normally seconds behind live and never more than 15 minutes: beyond that the answer comes from the main database. From the main database, live |
| Changes nothing | All five are reads. Nothing is written or audited |
Endpoints at a glance
Section titled “Endpoints at a glance”| Endpoint | Returns |
|---|---|
GET /api/v1/analytics/volume |
Completed transactions by month, channel and type |
GET /api/v1/analytics/typologies |
Rule hits by month and rule category |
GET /api/v1/analytics/rules |
The ten rules with the most hits, by month, with their closed and confirmed alerts |
GET /api/v1/analytics/alerts |
Alerts by the month they were raised, kind and outcome |
GET /api/v1/analytics/decisions |
Real-time decisions by month and outcome |
All five take the same query parameter and return the same window fields.
The months parameter
Section titled “The months parameter”| Parameter | Default | Meaning |
|---|---|---|
months |
12 |
How many months to cover, the current one included. 1 to 24; anything else is refused with 422 |
The answer never covers more than can be shown for your workspace right now: up to 24 months
from the analytics store, 3 from the main database. Asking
for more isn’t an error. You get the most available, months in the answer tells you how many
that was, and limit tells you why it is fewer than 24.
curl -s -b ~/.sp-cookies "$SP/api/v1/analytics/volume?months=6"The window fields
Section titled “The window fields”Every answer starts with the same fields, saying what it covers and where it was read:
{ "source": "clickhouse", "months": 6, "from": "2026-05-01T00:00:00Z", "max_months": 24, "timezone": "Africa/Accra", "base_currency": "GHS"}| Field | Meaning |
|---|---|
source |
clickhouse: read from the analytics store. postgres: read from the main database; limit says why |
months |
Months actually covered, the current one included |
from |
The first instant covered: midnight on the first day of the earliest month, in the workspace’s time zone |
max_months |
The most that can be shown for this workspace right now. 24 when the analytics store holds the workspace’s whole history; 3 from the main database; a number in between when the analytics store holds the workspace’s history from a later date only |
limit |
Why max_months is below 24. Absent when 24 months are available. See the table below |
timezone |
The workspace’s time zone, which decides where each month starts and ends |
base_currency |
The currency of every amount |
limit is one of:
limit |
Meaning |
|---|---|
not_configured |
The installation has no analytics store |
not_copied |
The analytics store is connected, but this workspace’s history hasn’t been copied to it |
copying |
A copy of this workspace’s history into the analytics store is running, or was interrupted |
partial |
The analytics store holds this workspace’s history from a later date only. With more than 3 whole months, source is clickhouse and max_months is what it holds; with 3 or fewer, source is postgres |
behind |
The analytics store is more than 15 minutes behind live |
unavailable |
The analytics store couldn’t be reached for this request |
Months with nothing to count are left out of points. Build the full list of months from
from and months if you need the gaps.
Volume
Section titled “Volume”GET /api/v1/analytics/volume
Section titled “GET /api/v1/analytics/volume”{ "source": "clickhouse", "months": 6, "from": "2026-05-01T00:00:00Z", "max_months": 24, "timezone": "Africa/Accra", "base_currency": "GHS", "points": [ { "month": "2026-05", "channel": "bank_transfer", "type": "transfer", "count": 1840, "amount_minor": 912400000 }, { "month": "2026-05", "channel": "mobile_money", "type": "deposit", "count": 22310, "amount_minor": 446200000 } ]}Figures are illustrative.
| Field | Meaning |
|---|---|
month |
YYYY-MM |
channel, type |
The transaction’s channel and type, as you sent them |
count |
Transactions with status completed that happened in that month |
amount_minor |
Their total value in the base currency, minor units |
Pending, failed, reversed and blocked transactions aren’t counted. Points are ordered by month, then channel, then type.
Typologies
Section titled “Typologies”GET /api/v1/analytics/typologies
Section titled “GET /api/v1/analytics/typologies”{ "source": "clickhouse", "months": 6, "from": "2026-05-01T00:00:00Z", "max_months": 24, "timezone": "Africa/Accra", "base_currency": "GHS", "points": [ { "month": "2026-05", "category": "structuring", "hits": 64, "suppressed": 3 }, { "month": "2026-05", "category": "velocity", "hits": 121, "suppressed": 0 } ]}| Field | Meaning |
|---|---|
category |
The rule’s category. Empty for a rule without one |
hits |
Every hit in that month by a live rule, including hits that an approved suppression kept from raising an alert |
suppressed |
How many of hits were suppressed. It is a part of hits, not an addition to it |
Hits by rules in shadow mode are in neither figure. hits is
counted the same way here, in rule performance and in the dashboard’s rule
figures, and is the same from either database. A hit is placed
in the month it was recorded. With the analytics store, the category is the rule’s category when
the hit was copied, so re-categorising a rule doesn’t move its old hits; from the main database it
is the rule’s category today.
Rule performance
Section titled “Rule performance”GET /api/v1/analytics/rules
Section titled “GET /api/v1/analytics/rules”{ "source": "clickhouse", "months": 6, "from": "2026-05-01T00:00:00Z", "max_months": 24, "timezone": "Africa/Accra", "base_currency": "GHS", "rules": [ { "key": "core.structuring", "name": "Repeated deposits just below a threshold", "hits": 212, "suppressed": 9, "closed_alerts": 90, "confirmed_alerts": 31, "months": [ { "month": "2026-05", "hits": 40, "suppressed": 2, "closed_alerts": 12, "confirmed_alerts": 5 }, { "month": "2026-06", "hits": 33, "suppressed": 0, "closed_alerts": 18, "confirmed_alerts": 4 } ] } ]}| Field | Meaning |
|---|---|
rules |
At most ten rules: those with the most hits over the window, most first |
key, name |
The rule’s key, such as core.structuring (not its library letter, such as B1), and its name |
hits |
Hits by the live rule, including suppressed ones; shadow hits are excluded |
suppressed |
How many of hits an approved suppression kept from raising an alert |
closed_alerts |
Alerts involving this rule that were closed in that month |
confirmed_alerts |
Of those, the alerts closed with any disposition other than false_positive |
months |
The same figures month by month. Months with none of them are left out |
The Analytics page’s “N% confirmed” is confirmed_alerts ÷ closed_alerts over the window. An
alert raised by several rules counts once for each. Note the two different months: a hit is counted
in the month it was recorded, an alert in the month it was closed.
Alert outcomes
Section titled “Alert outcomes”GET /api/v1/analytics/alerts
Section titled “GET /api/v1/analytics/alerts”{ "source": "clickhouse", "months": 6, "from": "2026-05-01T00:00:00Z", "max_months": 24, "timezone": "Africa/Accra", "base_currency": "GHS", "points": [ { "month": "2026-05", "kind": "aml", "outcome": "false_positive", "count": 48 }, { "month": "2026-05", "kind": "aml", "outcome": "escalated_to_case", "count": 17 }, { "month": "2026-05", "kind": "fraud", "outcome": "open", "count": 2 } ]}| Field | Meaning |
|---|---|
month |
The month the alert was raised |
kind |
aml, fraud, sanctions or other |
outcome |
Where the alert stands now. For a closed alert, its disposition: false_positive, no_action, duplicate or escalated_to_case, or other if it has none. Otherwise its status: open or in_case |
count |
Alerts |
Because outcome is the alert’s current state, the figures for a past month change as its alerts
are worked.
Decision outcomes
Section titled “Decision outcomes”GET /api/v1/analytics/decisions
Section titled “GET /api/v1/analytics/decisions”{ "source": "clickhouse", "months": 6, "from": "2026-05-01T00:00:00Z", "max_months": 24, "timezone": "Africa/Accra", "base_currency": "GHS", "points": [ { "month": "2026-05", "outcome": "allow", "count": 181204, "degraded": 12, "avg_latency_ms": 6.4 }, { "month": "2026-05", "outcome": "review", "count": 310, "degraded": 0, "avg_latency_ms": 7.9 } ]}| Field | Meaning |
|---|---|
outcome |
allow, step_up, review or block |
count |
Real-time decisions made in that month with that outcome |
degraded |
Of those, the decisions answered by the workspace’s failure policy because SpeculaPro couldn’t finish in time |
avg_latency_ms |
The average time SpeculaPro took to decide, in milliseconds, as measured inside SpeculaPro. It doesn’t include the network between your system and SpeculaPro |
Errors
Section titled “Errors”| Status | When |
|---|---|
401 |
Not signed in, or called with an API key |
403 |
Signed in without alerts.view |
404 |
The process you reached doesn’t run the api role |
422 |
months is below 1 or above 24 |
500 |
Neither database could answer. The body carries a reference to give your administrator |
Errors use the same problem format as the rest of the API.