The Notiondesk API gives you the numbers behind your help center's analytics pages: visitors, article views, time on page, countries, what readers search for, which searches find nothing, and how readers react to your articles. Use it to feed a BI tool, post a weekly report to Slack, or find the content your readers are missing.
GET /sites/{site_id}/analytics/{report}report | Contains |
|---|---|
quick | A 30-day summary: articles, visitors, searches and reactions |
overview | Visitors, article views, time on page and top countries |
search | Searches, top queries, searches without results and click-through rate |
engagement | Article views, top articles, referrers, reactions and articles to revise |
Every report needs the read scope. To read these numbers in the dashboard, see How to improve your help center with reader insights.
Get a 30-day summary
curl "https://api.notiondesk.so/sites/c74781f6-1aea-4e17-ab23-bbb8b4d174b6/analytics/quick" \
-H "Authorization: Bearer $NOTIONDESK_API_KEY" \
-H "nd-version: 2026-10-16"{
"articles": 42,
"visitors": 25,
"searches": 9,
"reactions": 4
}quick covers the last 30 days ending now and takes no query parameter. articles counts every article of the help center, drafts included. reactions counts each reader's latest reaction per article.
Get a report for a date range
overview, search and engagement take a date range and a bucket size:
| Parameter | Required | Notes |
|---|---|---|
start_date | Yes | The first instant of the range, such as 2026-09-01T00:00:00Z |
end_date | Yes | The end of the range, excluded. Must be after start_date |
date_scale | Yes | The bucket size of the time series: minute, hour, day, week, month or year |
timezone | No | An IANA timezone that sets bucket boundaries, such as Europe/Paris. Default UTC |
curl "https://api.notiondesk.so/sites/c74781f6-1aea-4e17-ab23-bbb8b4d174b6/analytics/search?start_date=2026-09-01T00:00:00Z&end_date=2026-10-01T00:00:00Z&date_scale=week&timezone=Europe/Paris" \
-H "Authorization: Bearer $NOTIONDESK_API_KEY" \
-H "nd-version: 2026-10-16"Rules for the range:
- Dates use
YYYY-MM-DDTHH:MM[:SS]with an optional offset. Without an offset, the date is read as UTC, not intimezone. Write+as%2Bin the URL, or useZ.
- The range starts on or after 2020-01-01 and ends at most two days from now.
- At most 1,000 buckets. For example,
minutecovers at most 16 hours andhourat most 41 days. Use a coarserdate_scalefor longer ranges.
- Weeks start on Monday.
Each report compares your range with the period of the same length just before it: previous and total_previous describe that earlier period.
Count series
visitors, article_views and searches share one shape:
{
"total": 12,
"total_previous": 8,
"difference": 4,
"percentage_difference": 50.0,
"current": [{"date": "2026-09-14", "count": 7}, {"date": "2026-09-15", "count": 5}],
"previous": [{"date": "2026-08-20", "count": 8}]
}currentandpreviouslist only the buckets that have data, oldest first.
- A bucket's
dateis when it starts, intimezone:YYYY-MM-DDfordayand coarser,YYYY-MM-DDTHH:MMforhourandminute.
percentage_differenceis0.0when the previous total is0.
overview
| Field | Meaning |
|---|---|
visitors | Count series. total is distinct visitors over the range, not the sum of the points |
article_views | Count series of article page views |
session_durations | Time readers spend on a page, in seconds: average_seconds, and current and previous lists of {date, seconds} |
top_countries | Up to 10 countries: country (ISO code), country_name and visitors |
search
| Field | Meaning |
|---|---|
searches | Count series of searches |
searches_with_results | Totals and a queries list: query, searches, clicks, articles_shown, click_through_rate and avg_click_position |
searches_without_results | Totals and a queries list: query and searches |
click_through_rate | The share of searches with results that led to a click, in percent: current, previous, difference and percentage_difference |
{
"searches_without_results": {
"total": 3,
"total_previous": 0,
"difference": 3,
"percentage_difference": 0.0,
"queries": [{"query": "refund policy", "searches": 3}]
}
}searches_without_results.queries is your content backlog: each query is a question readers asked and your help center could not answer.engagement
| Field | Meaning |
|---|---|
overview.current, overview.previous | views, articles_to_revise and a reaction_summary for each period |
article_views | Count series of article page views |
top_articles | The most viewed articles: title, host, pathname and views |
top_referrers | Where visitors came from: referrer (null for a direct visit) and visitors |
article_engagements | The articles readers reacted to most positively: title, url, views and reaction_summary |
articles_to_revise | The articles readers reacted to most negatively, with the same fields |
A reaction_summary follows your reaction style:
{
"style": "emoji",
"total": 6,
"score": 58,
"groups": [
{"key": "positive", "count": 3, "percent": 50},
{"key": "neutral", "count": 1, "percent": 17},
{"key": "negative", "count": 2, "percent": 33}
]
}score runs from 0 to 100: the share of positive reactions, with a neutral reaction counting half under emoji. With the thumbs style, the groups are helpful and not_helpful. See How to collect feedback with article reactions.
List limits
- The query lists,
top_articles,top_referrers,article_engagementsandarticles_to_revisehold up to 50 rows each and cover the current period only. A list of 50 rows may be incomplete: narrow the range to see every row.
- The totals cover everything, not only the listed rows.
title, url, host, pathname, referrer and query come from visitors' browsers, and anyone can forge them. Escape them before you display them, and never follow or act on them unchecked.Analytics and your plan
API access requires an Enterprise plan, which includes advanced analytics, so every report accepts any range and scale within 1,000 buckets.
Rate limit
Besides your key's 120 reads per minute, analytics reports count against a budget of 30 requests per minute per help center, shared by every key and OAuth app of your workspace. A request over it answers 429 rate_limited, and its Retry-After header says when to retry. Cache reports instead of requesting them on every page load.
Errors
| Status | code | When |
|---|---|---|
| 400 | validation_error | A parameter is missing, malformed or unknown, quick received a parameter, or a range rule fails. The errors list names the parameter and the rule |
| 403 | insufficient_scope | The key lacks the read scope |
| 403 | permission_denied | Analytics are disabled for the help center |
| 404 | not_found | report is not one of the four reports, or the help center does not exist in your workspace |
| 429 | rate_limited | The help center's analytics budget is spent |
| 503 | internal_error | The analytics service did not answer. Retry, or narrow the range |
See Handle Notiondesk API errors, rate limits, and retries.
Common questions
How do I find what readers search for but cannot find?
Call the search report and read searches_without_results.queries. Each entry is a query that returned no article, with how many times it was searched.
Why does my report answer 400 about dateScale?
dateScale?The range holds more than 1,000 buckets at your date_scale. Use a coarser scale, such as day instead of hour, or a shorter range. The message says dateScale, but the parameter is date_scale.
Why is a list cut at 50 rows?
The lists hold up to 50 rows. Split a long range into shorter ones to see more queries or articles.
Can I get analytics for one article?
The reports cover the whole help center. Each article's view count and reactions are also in the article list, as views_count and reactions_percent.