Pilot: enrollment requiredThe analytics endpoints work only for organizations Padlet has enrolled. To enroll, email [email protected]. Until then, every call returns
404.
Before you start
- API key. Get one from your developer settings.
- Organization hashid. Call
GET /me?include=organizations. It lists every organization your key can read.
This Power BI dataflow loads Padlet analytics into two tables:
Metrics: the organization's figures.Users: its members.
It also waits for results that are not ready yet, so you don't have to. The file only works in Power BI. Excel cannot import it.
Set up the dataflow
Download padlet-analytics.model.json. Then, in Power BI:
- In a Pro workspace, choose + New → Dataflow. Upload the file in the Import box.
Don't use Get data or Add tables. They read the file as data, not as a dataflow. Import only appears on the new-dataflow screen, so if you are inside a dataflow, close it and start a new one. - When asked for credentials, choose Anonymous. The API key is sent in a header instead.
- Open the dataflow and select Edit tables. On the Home ribbon, select Manage parameters. Set
ApiKeyto your API key andOrgIdto your organization hashid. Select Save & close. - Select Refresh now. Check Refresh history to see if it worked.
The two tables
| Table | One row per | To change it |
|---|---|---|
Metrics | Metric. Starts with padletCount, postCount and active memberCount | Edit the metric list in the Metrics query. See Read organization analytics |
Users | Current member, with their padlets, posts, comments and reactions | Edit the options in the Users query. See Read organization analytics users |
The Users query options:
| Field | Value |
|---|---|
from, to | Dates like "2026-01-01" and "2026-03-31". Send both or neither |
status | "active" or "inactive" |
userType | One role, like "teacher", or several, like {"teacher", "admin"} |
q | Text to search for in names and emails |
sort | A column, like "posts", "name" or "lastActivityDate" |
order | "asc" or "desc" |
perPage | Rows per request, up to 100. Only lower it when debugging |
The Users query fetches every page of members. With 100 rows a page and a limit of 60 requests an hour, one refresh can load about 6,000 members. If your organization is larger, add filters. Otherwise the refresh stops with a 429 error.
Scheduled refresh
To make scheduled refresh work in the Power BI Service:
- Keep the URL static. The dataflow uses a fixed base URL and puts the hashid in
RelativePath. Don't build the URL by joining strings. That works in Desktop but fails in the Service. - Use Anonymous credentials. Set them once in the dataflow's settings, under data source credentials. The API key is sent in a header.
Limits to plan around:
| Limit | Value |
|---|---|
| Refresh time | 2 hours on Pro, 5 hours on Premium/PPU |
| Scheduled refreshes | 8 a day on Pro |
| Padlet API requests | 60 an hour per API key and organization. Each retry counts |
Use fixed from and to dates, not rolling ones. Fixed dates let Padlet serve cached results, so the refresh spends less time waiting.
Troubleshooting
| Problem | Cause and fix |
|---|---|
401 | API key missing, revoked, or from a different environment |
404 | Wrong hashid, organization not enrolled, or key has no analytics access. All three look the same |
400 | Metrics: unknown metric, invalid property value, range over 24 months, duplicate metric, or more than 50 queries. Users: unknown status, userType, sort or order |
415 | The request body was form-encoded. The dataflow's Content-Type header prevents this |
429 | More than 60 requests in an hour for this key and organization. For Users, the error names the limit. Add filters or raise perPage |
| Still pending after every retry | The dataflow stops rather than load partial data. Pass a larger maxAttempts as the fourth argument in the Metrics query, or request less |
| A column will not load | Power BI cannot load records or lists in a cell. Select plain fields and set their column types |
| "This dataset includes a dynamic data source" | The URL is built by joining strings somewhere. Move the changing part into RelativePath |
