Power BI and Excel
Load your MuchSkills skills data into Excel or Power BI with Power Query. You create a Read client in MuchSkills, paste one query into a blank Power Query, and get a table with one row per person per skill that refreshes when you want.
- Who
- A MuchSkills Owner or Admin creates the client; anyone with Excel or Power BI loads the data
- You need
- An API client with the Read scope
- Tools
- Power Query in Excel, Power BI Desktop and the Power BI service
- Refresh
- On demand, or on a schedule in the Power BI service
How to
How it works
The query on this page does three things on every refresh. It swaps your Client ID and Client Secret for an access token. It reads the profile search, 300 people at a time, with POST requests. Then it turns the result into a table with one row per person per skill. It uses the Web.Contents function with its Content, Headers, RelativePath and Query options. Microsoft's reference for Web.Contents
Before you start
- An API client with the Read scope. An Owner or Admin creates it under Team/Org › Settings › API Access. The steps are in MuchSkills API and API Access.
- Excel with Power Query, or Power BI Desktop. To refresh on a schedule, you also need access to a Power BI workspace.
- A private file. The query holds your Client Secret. Keep the workbook or report file to yourself and share the results.
Steps
In MuchSkills
- Create a Read client. Go to Team/Org › Settings › API Access, click New Client, name it, for example Power BI export, tick Read under Scopes and click Create Client. Copy the Client ID and Client Secret before you click Done.
In Excel or Power BI Desktop
- Open a blank query. In Excel, choose Data › Get Data › From Other Sources › Blank Query. In Power BI Desktop, choose Home › Get data › Blank query.
- Paste the query. In the Power Query editor, choose Home › Advanced Editor, delete what is there and paste this query. Replace
YOUR_CLIENT_IDandYOUR_CLIENT_SECRETwith your values and keep the quotation marks. Click Done.
let
ClientId = "YOUR_CLIENT_ID",
ClientSecret = "YOUR_CLIENT_SECRET",
BaseUrl = "https://app.muchskills.com",
PageSize = 300,
// 1. Get an access token. A new one is fetched on every refresh.
TokenResponse = Json.Document(Web.Contents(BaseUrl, [
RelativePath = "oauth2/token",
Headers = [#"Content-Type" = "application/x-www-form-urlencoded"],
Content = Text.ToBinary(Uri.BuildQueryString([
grant_type = "client_credentials",
client_id = ClientId,
client_secret = ClientSecret
]))
])),
Token = TokenResponse[access_token],
// 2. Read one page of profiles. The search is a POST:
// skip and limit go in the address, the body is {}.
GetPage = (Skip as number) as record =>
Json.Document(Web.Contents(BaseUrl, [
RelativePath = "api/v2/profile/search",
Query = [skip = Text.From(Skip), limit = Text.From(PageSize)],
Headers = [
Authorization = "Bearer " & Token,
#"Content-Type" = "application/json"
],
Content = Text.ToBinary("{}")
])),
// 3. Read every page.
FirstPage = GetPage(0),
Total = FirstPage[pagination][total],
MoreSkips = List.Numbers(PageSize, List.Max({0, Number.RoundUp(Total / PageSize) - 1}), PageSize),
Pages = {FirstPage} & List.Transform(MoreSkips, GetPage),
Profiles = List.Combine(List.Transform(Pages, each _[data])),
// 4. One row per person per skill.
OrEmpty = (x) => if x = null then {} else x,
Rows = List.Combine(List.Transform(Profiles, (person) =>
List.Combine(List.Transform(OrEmpty(Record.FieldOrDefault(person, "skillTypes", null)), (category) =>
List.Transform(OrEmpty(Record.FieldOrDefault(category, "skills", null)), (skill) => [
Name = person[profile][name],
Email = person[profile][email],
Department = Record.FieldOrDefault(person[profile], "department", null),
#"Skill category" = category[type],
Skill = skill[name],
#"Own level (1-9)" = Record.FieldOrDefault(skill, "level", null),
#"Validated level (1-9)" = Record.FieldOrDefault(skill, "validatedLevel", null)
])
))
)),
Result = Table.FromRecords(Rows)
in
Result
- Allow the connection. If Power Query asks how to connect to
https://app.muchskills.com, choose Anonymous and click Connect. If it asks about privacy levels, choose Organizational. - Load the table. In Excel, choose Home › Close & Load. In Power BI Desktop, choose Home › Close & Apply. To get fresh data later, choose Data › Refresh All in Excel or Refresh in Power BI Desktop.
In the Power BI service (optional)
- Publish and set credentials. Publish the report to a workspace. Open the semantic model's settings, and under Data source credentials edit the credentials for
https://app.muchskills.com: choose Anonymous and the privacy level Organizational, then sign in. - Schedule the refresh. In the same settings, turn on scheduled refresh and choose how often. No gateway is needed. Microsoft's guide to data refresh in Power BI
Check it worked
- You get a table with one row per person per skill, with each person's own level and any validated level.
- The number of different emails in the table matches the number of people in your MuchSkills organisation.
- In the Power BI service, the refresh history shows a successful refresh.
Benefits
What you get
- Skills in your own reports. Combine MuchSkills data with HR, finance or project data in Power BI or Excel.
- Fresh numbers without exports. A refresh pulls the current data, so nobody downloads and pastes files.
- Self and manager views side by side. Both levels are in the table, so you can see where they differ.
Important to know
Important to know
Levels are on a 1 to 9 scale. 1 to 3 is Beginner, 4 to 6 Intermediate and 7 to 9 Expert. The validated level is the one a manager confirmed. It is empty when no manager has validated the skill.
POST requests must be anonymous. Power Query only sends POST requests when the connection is set to Anonymous. That is why the query fetches its own token rather than using a stored credential. Microsoft's notes on the Web connector
Keep the base address fixed. The Power BI service can refresh this query because the address is always https://app.muchskills.com, and the path and paging are added with RelativePath and Query. If you edit the query, keep that pattern. Joining the address together as text can stop scheduled refresh from working.
Paging. Each page holds up to 300 people. The query reads the total from the first page and fetches the rest.
Request limits. If you refresh many queries at once, MuchSkills may answer 429 Too Many Requests. Wait a minute and refresh again.
Revoke if exposed. If the file reaches someone it should not, click Revoke on the client in API Access and create a new one.
Common errors
Troubleshooting
| What you see | Why | What to do |
|---|---|---|
An error with (404): Not Found | The data was loaded with From Web, or the query was changed to send a GET | Paste the query from this page again and change only the two values |
An error with (400): Bad Request from oauth2/token | The Client ID or Client Secret is wrong, or the quotation marks were removed | Check both values. If the secret is lost, revoke the client and create a new one |
An error with (401): Unauthorized | The client was revoked, or the token was rejected | Refresh once more. If it stays, create a new client and use its values |
An error with (429) | Too many requests in a short time | Wait a minute and refresh |
| "Web.Contents with the Content option is only supported when connecting anonymously" | The credentials for https://app.muchskills.com are not Anonymous | In Data source settings, edit the permissions for the address and choose Anonymous |
| A privacy levels prompt or a Formula.Firewall error | Power Query is checking how data sources may be combined | Set the privacy level for https://app.muchskills.com to Organizational |
| The Power BI service says the semantic model has a dynamic data source and cannot refresh | The query builds its address from joined text | Keep the base address fixed and add paths with RelativePath and Query, as in the query on this page |
Frequently asked questions
Why can we not use From Web in Excel?
From Web sends a GET request. The MuchSkills profile search only accepts POST, and it answers a GET with 404 Not Found. The query on this page sends the right request and fetches its own token.
Why do we choose Anonymous if the API needs a token?
The query asks MuchSkills for a token itself, using your Client ID and Client Secret, and sends it with each call. Power Query only allows POST requests when the connection is set to Anonymous.
Does scheduled refresh work in the Power BI service?
Yes. The query keeps the base address fixed and adds the path and paging with the RelativePath and Query options, which the Power BI service can refresh. Set the data source credentials to Anonymous after you publish.
Do we need a data gateway?
No. MuchSkills is a cloud service the Power BI service can reach over the internet, so the semantic model refreshes without a gateway.
Is it safe to keep the Client Secret in the query?
Anyone who can open the query can read the secret. Use a client with the Read scope only, share the report or the loaded table rather than the file, and revoke the client in API Access if the file goes somewhere it should not.
Do we have to renew a token?
No. Every refresh asks MuchSkills for a new token, so there is nothing to renew.
Can we load skill lists, certificates or availability too?
Yes. Copy the query, keep the token part, and change the path to another endpoint, such as api/v1/analysis for analysis lists. GET endpoints take no Content option.