The following is a similar query to the one in [Glorfindel][1]'s [answer][2], but implemented as a runnable snippet and using the [Stack Exchange API][3] (as the load on the API can be quite heavy, please procure the API key on [StackApps][4] or use an existing one). The generated report includes the following stats: - link to the answering user profile - number of answers given by the user - total reputation earned via bounties - total score of answers Example report for a user id 953887 on Stack Overflow: <img src="https://i.sstatic.net/3alv9.png" width="500" alt="sample report for a user id 953887 on Stack Overflow" /> --- <!-- begin snippet: js hide: false console: true babel: false --> <!-- language: lang-js --> /// <reference types="@userscripters/stackexchange-api-types" /> const API_BASE = "https://api.stackexchange.com"; const API_VER = 2.3; /** * @summary delays script execution * @param {number} [ms] milliseconds to delay for * @returns {Promise<void>} */ const delay = (ms = 100) => new Promise((resolve) => setTimeout(resolve, ms)); /** * @summary makes a table cell * @param {string| Node} content * @returns {HTMLTableCellElement} */ const makeCell = (content) => { const td = document.createElement("td"); td.append(content); return td; }; /** * @summary makes a table row * @param {Array<string | Node>} columns * @returns {HTMLTableRowElement} */ const makeRow = (...columns) => { const tr = document.createElement("tr"); const cells = columns.map(makeCell); tr.append(...cells); return tr; }; const makeLink = (url, label) => { const a = document.createElement("a"); a.target = "_blank"; a.href = url; a.innerText = label; return a; }; /** * @see https://api.stackexchange.com/docs/questions-on-users * * @summary gets question info by user id * @param {number} id user id * @param {{ site?: string, page?: number, ...rest }} [options] request configuration * @returns {Promise<StackExchangeAPI.Question[]>} */ const getQuestions = async (id, { site = "stackoverflow", page = 1, ...rest }) => { const url = new URL(`${API_BASE}/${API_VER}/users/${id}/questions`); url.search = new URLSearchParams({ site, page: page.toString(), ...rest, }).toString(); const res = await fetch(url.toString()); if (!res.ok) return []; const { items = [], has_more = false, backoff } = await res.json(); if (backoff) { await delay(backoff * 1e3); return getQuestions(id, { site, page, ...rest }); } if (has_more) { items.push( ...(await getQuestions(id, { site, page: page + 1, ...rest, })) ); } return items; }; /** * @see https://api.stackexchange.com/docs/answers-on-questions * * @summary gets answers by question ids * @param {number[]} ids user id * @param {{ site?: string, page?: number, ...rest }} [options] request configuration * @returns {Promise<StackExchangeAPI.Answer[]>} */ const getAnswers = async (ids, { site = "stackoverflow", page = 1, ...rest }) => { const url = new URL(`${API_BASE}/${API_VER}/questions/${ids.join(";")}/answers`); url.search = new URLSearchParams({ site, page: page.toString(), ...rest, }).toString(); const res = await fetch(url.toString()); if (!res.ok) return []; const { items = [], has_more = false, backoff } = await res.json(); if (backoff) { await delay(backoff * 1e3); return getAnswers(ids, { site, page, ...rest }); } if (has_more) { items.push( ...(await getAnswers(ids, { site, page: page + 1, ...rest, })) ); } return items; }; /** * @template {unknown} T * * @summary splits an array into chunks * @param {T[]} arr array to split * @param {number} size chunk size * @returns {T[][]} */ const chunkify = (arr, size) => { /** @type {T[][]} */ const output = []; arr.forEach((e, i) => { let chunk = output[output.length - 1]; i % size ? chunk.push(e) : output.push([e]); }); return output; }; /** * @summary sums score of all posts * @param {Array<StackExchangeAPI.Post>} posts * @returns {number} */ const getTotalScore = (posts) => posts.reduce((a, c) => a + c.score, 0); window.addEventListener("load", () => { const apiKeyInput = document.getElementById("api_key"); const accountInput = document.getElementById("account_id"); const submit = document.getElementById("search"); const siteInput = document.getElementById("site"); const progress = document.getElementById("status"); progress.value = 0; submit.addEventListener("click", async () => { const { value: key } = apiKeyInput; const { value: site } = siteInput; const { value: userId } = accountInput; const report = document.querySelector("#preview > tbody"); const questions = await getQuestions(userId, { key, site }); progress.value = 25; const questionIds = questions.map(({ question_id }) => question_id); const chunks = chunkify(questionIds, 100); // max number of ids is 100 const { length: numChunks } = chunks; const progressValuePerChunk = 75 / numChunks; /** @type {Record<string, { answers: StackExchangeAPI.Answer[], link: string, name: string }>} */ const answers = {}; for (const chunk of chunks) { const items = await getAnswers(chunk, { key, site, filter: "!nKzQUREjAc" }); items.forEach((answer) => { const { owner: { user_id, display_name, link } } = answer; if (!answers[user_id]) answers[user_id] = { answers: [] }; const userAnswers = answers[user_id]; userAnswers.answers.push(answer); userAnswers.link = link; userAnswers.name = display_name; }); progress.value += progressValuePerChunk; } report.querySelectorAll("tr").forEach((r) => r.remove()); const answersByUser = Object.values(answers); answersByUser.sort((a, b) => getTotalScore(b.answers) - getTotalScore(a.answers)); const rows = answersByUser.flatMap(({ link, name, answers }) => { const userLink = makeLink(link, name); const totalAnswers = answers.length; const totalBounties = answers.reduce((c, a) => c + (a.awarded_bounty_amount || 0), 0); const totalScore = getTotalScore(answers); return makeRow( userLink, totalAnswers, totalBounties, totalScore ); }); report.append(...rows); report.closest("table").classList.remove("hidden"); progress.value = 100; }); }); <!-- language: lang-css --> label { display: block; font-weight: 600; } caption, label { font-weight: 600; margin-bottom: 0.5vh; } table { margin-top: 4vh; border-collapse: collapse; } th, td { padding: 2vh 2vw; border: 1px solid lightgray; } .hidden { display: none; } progress { margin-top: 2vh; height: 20px; border-radius: 0; } <!-- language: lang-html --> <form> <label for="api_key">SE API key</label> <input id="api_key" type="text" title="SE API key" placeholder="abc12345" /> <label for="site">Site API Slug</label> <input id="site" type="text" title="Site API slug" placeholder="stackoverflow" value="stackoverflow" /> <label for="account_id">User Account Id</label> <input id="account_id" type="text" title="Account Id" placeholder="12345" /> <button id="search" type="button">Get Info</button> </form> <progress id="status" max="100" value="0"></progress> <table id="preview" class="hidden" cellspacing="0"> <caption>Stack Exchange Post Activity</caption> <thead> <tr> <th>Account</th> <th>Answers</th> <th>Bounty rep</th> <th>Score</th> </tr> </thead> <tbody> </tbody> </table> <!-- end snippet --> [1]: https://meta.stackexchange.com/users/295232/glorfindel [2]: https://meta.stackexchange.com/a/377012/786798 [3]: https://api.stackexchange.com/docs [4]: https://stackapps.com/apps/oauth/register