Read organization analytics

Read engagement, membership and content-safety analytics for one organization over a period.

This is a POST because the query is structured — an array of metric objects, each with
its own properties — but it is a safe, idempotent read. Retries change nothing.

Analytics are computed asynchronously. A metric whose figures are not in the cache
answers 202 Accepted with Retry-After and names itself under meta.pending.
Re-issue the identical request after that many seconds, and keep doing so while the
response is a 202 or meta.pending is non-empty — a single query may run for
minutes, so one retry is not always enough. A 202 is not a failure, and a pending
metric must never be read as zero.

The endpoint is opt-in while in pilot: it answers only for organizations Padlet has
enrolled, and enrollment is granted on the organization's owner. Ask Padlet support to
enroll an organization.

Requires an API key belonging to an admin of the organization (a teacher, for a classroom).
Organization not enrolled, unknown id, or no access — every denial answers the same 404.

🚧

Opt-in while the endpoint is in pilot

This endpoint only answers for organizations Padlet has enrolled. Enrollment is granted on the organization's owner. Being an admin is not enough. Email [email protected] to ask for enrollment. Until then every call answers 404, the same response an unknown organization gets.

📘

Finding org_id

You do not compute it: read it from GET /me?include=organizations, which lists every wksp_ organization your key belongs to. This endpoint does not yet support org_ organizations on padlet.org.

📘

API key

You will need to include your API key found here.

Please include the key in your header. {"X-API-KEY": <api_key>}

The key must belong to an admin of the organization, or a teacher for a classroom.

🔄

A 202 means poll, not fail

Analytics are computed off the request path. A metric whose figures are not cached comes back as 202 Accepted with a Retry-After header and its name under meta.pending. Re-issue the identical request after that many seconds, and keep going while the response is a 202 or meta.pending is non-empty — a single query can run for minutes, so one retry is not always enough.

Never read a pending metric as zero — it is absent from data, not empty.

Reads engagement, membership and content-safety analytics for one organization over a period. The query is a structured array of metric objects, so this endpoint uses POST. It is still a safe, idempotent read, so a retry changes nothing. A retry does spend one of the hour's 60 requests, so wait for Retry-After rather than polling in a tight loop.

Request body

FieldTypeNotes
metricsarrayRequired. 1 to 25 entries. A request may resolve to at most 50 queries in total, so a wide breakdown across many metrics has to be split across calls
metrics[].namestringA metric from the catalog below
metrics[].<property>string or arraySee the catalog. An array breaks the metric down by that property — except userType, where an array is one role slice
dimensionsarray["month"] for a monthly series. Every metric in the call must have a monthly form
from, tostringYYYY-MM, inclusive. The range counts both endpoints and may span at most 24 of them, so to can be no more than 23 months after from. Send both or neither

Omitting from and to reads the organization's current billing term, or all time if it has none.

from cannot precede January of five years ago, and to cannot be later than the current month. A range inside the 24-month cap but outside that window is still a 400.

Metric catalog

MetricPropertiesMonthly
memberCountstatus: active, inactive, invited, joined, all · userType: rolesyes
newMemberCountyes
padletCountuserType: roles, monthly onlyyes
postCountuserType: roles, monthly onlyyes
commentCountuserType: roles, monthly onlyyes
reactionCountuserType: roles, monthly onlyyes
visitorCountyes
peerInteractionCountuserType: roles — requiredyes
wallEngagementMinutesuserType: roles, plus anonymousno
attachmentCountByCategoryuserType: roles — requiredno
scannedItemCountno
scannedItemCountByTypeno
avgScanTimeMsno
flaggedItemCountno
flaggedItemCountByCategoryno
moderationCatchRatePercentno
topPadletssortBy: posts, visitors — requiredno
topUserssortBy: posts, padlets — required · userType: rolesno
topMediamediaType: video, audio — requiredno
subscriptionno

userType takes organization roles, and which roles an organization has depends on its type:

OrganizationRoles
Schoolowner, admin, teacher, student, guest
Classroomowner, teacher, student
Teamowner, admin, maker, contributor, guest

The endpoint accepts any of these role names for any organization. It does not reject roles that organization type cannot have. Asking a classroom for admin returns a truthful zero, not an error. Send the roles the organization actually uses.

all must be sent alone. Mixed with role names — ["all", "teacher"] — it is read as a role and the request is a 400.

wallEngagementMinutes without userType, or with userType: all, covers the whole workspace. That includes anonymous visitors, not just members. Send an explicit role list to exclude them.

The six content-safety metrics run from scannedItemCount through moderationCatchRatePercent. They need content safety, and only schools and classrooms have it. Asking for them on a team organization returns a 400, not a misleading zero.

memberCount with status invited or joined has no all-members form, so those two require userType.

"Monthly only" means a metric's role-sliced form exists only as a monthly series. If you send userType for one of these metrics, you must also send dimensions: ["month"], or the request is a 400. Sending neither is fine: {"name": "postCount"} alone is a valid request and returns a single number. "Required" means the metric has no all-members form at all.

Responses carry the resolved properties, not a copy of the request. An omitted property comes back as all. A scalar userType comes back as a one-element array. Duplicates are collapsed. Match a meta.pending or meta.errors entry to your request using the name plus those resolved properties.

Membership counts describe the organization's current roster, not a historical snapshot. A member who has since left is excluded, even from a range that predates their departure. status: all and joined count current members who joined on or before to. status: active counts current members who contributed anything between from and to. status: inactive is the current roster size minus that active count. status: invited ignores from and to altogether. It counts the invitations outstanding right now. Everything else (posts, padlets, comments, reactions, visitors, engagement minutes) is scoped to the range.

What data holds

The shape follows the request:

Requestdata
A plain counta number — 42
dimensions: ["month"]an array of monthly rows. The figure's key follows the metric: value for memberCount, padletCount, postCount, commentCount and peerInteractionCount, count for newMemberCount and reactionCount, visitors for visitorCount
A property given as an arrayan object keyed by property value — { "active": 12, "inactive": 3 }. Each value keeps the shape that slice would have alone, so a monthly request gives an object of monthly arrays, and topPadlets with two sortBy values an object of ranked lists
topPadlets, topUsers, topMediaan array of at most ten objects, each with a hashid, a name or title, and its ranking figures. The cap is fixed — no limit parameter, no pagination
attachmentCountByCategoryan array of { category, count } — a breakdown the metric always returns, with no property to ask for it
scannedItemCountByType, flaggedItemCountByCategorya numeric map keyed by scanned-item type or flagged category, likewise intrinsic
subscriptionan object with the billing dates, status and seat count

subscription is the one metric whose fields are not a figure for the range. The dates and status describe the organization's current billing state. The seat count is as of the last day of the range, or today when the range ends in the future, which the default range usually does. It comes back null when the range spans more than one billing term, or when billing history doesn't reach that far back.

Errors

CodeMeaning
400Unknown metric, an invalid property value, a malformed or too-wide range, a duplicate entry, or more than 50 resolved queries
401Missing or revoked API key
404The organization does not exist, is not enrolled, or your key has no analytics access to it — the same response for all three
406The Accept header named something else. Send application/vnd.api+json, or omit it
415The Content-Type header named something else. Some clients default to form encoding; send application/json. A malformed body under an accepted type is a 400 with BAD_JSON instead
429More than 60 requests in an hour for this API key and organization — a poll after a 202 spends one. Retry-After gives the wait in seconds

Example

curl -X POST https://api.padlet.dev/v1/organizations/$ORG_ID/analytics \
  -H "X-API-KEY: $PADLET_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "metrics": [
      { "name": "padletCount" },
      { "name": "memberCount", "status": "active", "userType": ["teacher", "admin", "owner"] }
    ],
    "dimensions": ["month"]
  }'
{
  "data": {
    "id": "wksp_1oRLGeKoGK9DPvOw",
    "type": "organizationAnalytics",
    "attributes": {
      "metrics": [
        { "name": "padletCount", "properties": { "userType": "all" }, "data": [{ "month": "2026-08", "value": 1 }] },
        {
          "name": "memberCount",
          "properties": { "status": "active", "userType": ["teacher", "admin", "owner"] },
          "data": [{ "month": "2026-08", "value": 1 }]
        }
      ]
    }
  },
  "meta": { "status": "ready", "dateRange": { "from": "2026-08-03", "to": "2027-08-03" } }
}

Not every metric has a monthly form for every slice: memberCount has one for active with a role slice, so asking for ["active", "inactive"] alongside dimensions: ["month"] is a 400 naming the combination.

Path Params
string
required
length between 16 and 22

Hashid of the organization where you are an admin, or a teacher for a classroom. Read it
from GET /me?include=organizations, which lists every wksp_ organization your key
belongs to. org_ organizations on padlet.org are not supported yet.

Body Params
metrics
array of objects
required
length between 1 and 25

The metrics to read. One metric may appear more than once under different
properties — that is how two role slices are compared — but two entries that
resolve to the same name and properties are rejected. A request may resolve to
at most 50 queries in total, so a wide breakdown across many metrics has to be
split across calls.

metrics*
string
enum
required

A metric from the catalog on the Analytics page.

enum

memberCount only. invited and joined have no all-members form, so they
require userType. An array breaks the metric down by status.

enum

Organization roles to count. Omit for every member. Which roles an organization has depends
on its type: teacher and student in a school or classroom, maker and
contributor in a team. wallEngagementMinutes also accepts anonymous for
visitors who were not signed in — and without userType, that metric is
workspace-wide and already includes them; send an explicit role list to exclude
them. Unlike other properties, an array here is one slice rather than a
breakdown — repeat the metric to compare slices. all cannot be combined with
role names in the same array — send it alone, or omit userType.

enum

topPadlets: posts or visitors. topUsers: posts or padlets. Required — there is no default ranking.

enum

topMedia only: video or audio. Required.

dimensions
array of strings

Pass ["month"] for a monthly series instead of one figure for the whole range.
Every metric in the call must have a monthly form or the whole call fails.

dimensions
Allowed:
string
^\d{4}-(0[1-9]|1[0-2])$

First month of the range, inclusive. Send with to or omit both. Omitting both
reads the organization's current billing term, or all time if it has none.

string
^\d{4}-(0[1-9]|1[0-2])$

Last month of the range, inclusive. The range counts both endpoints and may span
at most 24 of them, so to can be no more than 23 months after from.

Headers
string
enum
Defaults to application/vnd.api+json

Generated from available request content types

Allowed:
Responses

Language
Credentials
Header
LoadingLoading…
Response
Click Try It! to start a request and see the response here! Or choose an example:
application/vnd.api+json