How can I retrieve the top rated answer given a question id?

558 Views Asked by At

I want to retrieve top rated answer given the questions id. API documentiation states that one can get all answers matching "a set of ids" from a site using the answers/{ids} method.

I have tried to the following URL: https://api.stackexchange.com/questions?site=stackoverflow.com&ids=32290879 which returns a set of items, but when I search through the page I don't find the matching question_id (32209879)

search for question id returns zero matching results

How do I retrieve the top rated answer given a question id?

1

There are 1 best solutions below

0
On

You need to use the answers on questions (/questions/{ids}/answers) method, which returns answers for a set of question ids. You can then sort the answers by votes. However, the thing becomes more complicated if you pass more than 1 id. You'll then need to loop through every item and implement pagination.

There are various SE API libraries for a variety of languages on StackApps. You can use one depending on your language.


Here's a JavaScript example:

const key = 'U4DMV*8nvpm3EOpvf69Rxw((';
const sitename = 'stackoverflow';
const filter = '!9_bDE-nvI';
const buildApiUrl = ids => `https://api.stackexchange.com/2.2/questions/${ids}/answers?site=${sitename}&filter=${filter}&key=${key}&pagesize=100&sort=votes`;
const table = document.querySelector('.s-table');
var answers = {}, results = [];

async function getTopVotedAnswers() {
  answers = {};
  [...table.querySelectorAll('tr:not(#theaders)')].forEach(el => el.remove());
  document.querySelector('textarea').value = ''; // clear logs
  document.querySelector('#submit-ids-button').disabled = true; // disable button
  const arrIds = document.querySelector('#ids-input').value.split(',');

  // loop in case the ids are >100
  for (let i = 0; i < Math.ceil(arrIds.length / 100); i++) {
    const questionAnswers = await getFromApi(buildApiUrl(arrIds.slice(i * 100, (i + 1) * 100).join(';')), 1);
    if (!questionAnswers) return; // API returned an error
    results = {}; // reset results
    questionAnswers.items.forEach(item => {
      if (!answers[item.question_id]) { // we haven't found the top answer for that question
        answers[item.question_id] = [item.question_id, item.answer_id, item.score];
      }
    });
  }

  // input data in the table
  Object.values(answers).forEach(answer => {
    const trElement = document.createElement('tr');
    for (let i = 0; i < answer.length; i++) {
      const tdElement = document.createElement('td');
      tdElement.innerHTML = answer[i];
      trElement.appendChild(tdElement);
    }
    table.appendChild(trElement);
  });
  document.querySelector('#output').style.display = ''; // show the output table

  document.querySelector('#submit-ids-button').disabled = false; // re-enable button
}

async function getFromApi(apiUrl, page) {
  const call = await fetch(`${apiUrl}&page=${page}`), resp = await call.json();
  if (resp.error_id) { // oops! error!
    appendToLogs('error', `API returned error ${resp.error_id} (${resp.error_name}): ${resp.error_message}. Stopping.`);
    return;
  }
  appendToLogs('info', `Fetched page ${resp.page}. Quota remaining is ${resp.quota_remaining}`);

  // handle backoff
  const backoff = resp.backoff;
  if (backoff) {
    appendToLogs('warning', `BACKOFF received. Waiting for ${backoff} seconds.`);
    await new Promise(x => setTimeout(x, backoff * 1000));
  }

  // handle has_more
  if (resp.has_more) {
    resp.items.forEach(item => results.push(item));
    await getFromApi(apiUrl, page + 1);
  }
  return !results.length ? resp : results;
}

function appendToLogs(severity, textToAppend) {
  document.querySelector('textarea').value += `${severity.toUpperCase()}: ${textToAppend}\n`;
}

document.querySelector('#submit-ids-button').addEventListener('click', getTopVotedAnswers);
<link rel="stylesheet" href="https://unpkg.com/@stackoverflow/stacks/dist/css/stacks.min.css">

<!-- from https://stackoverflow.design/product/components/inputs/#appended-inputs -->
<div class="grid gs4 gsy fd-column">
  <label class="grid--cell s-label">Please enter semicolon-separated question ids</label>
  <div class="grid">
    <div class="grid ai-center order-last s-input-fill">
      <div class="grid gs4 gsx ai-center">
        <button class="s-btn s-btn__primary s-btn__sm grid--cell" id="submit-ids-button" type="button">Submit</button>
      </div>
    </div>
    <div class="grid fl1 ps-relative">
      <input class="grid--cell s-input brr0" id="ids-input" type="text" placeholder="Enter ids here" />
    </div>
  </div>
</div>
<br/>
<div class="grid ff-column-nowrap gs4 gsy" id="output" style="display: none">
  <h3 class="grid--cell">Output</h3>
  <table class="s-table">
    <tr id="theaders">
      <th>Question id</th>
      <th>Top voted answer id</th>
      <th>Answer score</th>
    </tr>
  </table>
</div>
<br/>
<div class="grid ff-column-nowrap gs4 gsy">
  <h3 class="grid--cell">Logs</h3>
  <textarea class="grid--cell s-textarea" readonly style="resize: vertical;" rows="5"></textarea>
</div>

And here's a Python one (using StackAPI):

from stackapi import StackAPI

answers_hash = {}
# Example ids
ids = [4, 26833209, 30246303, 35022905, 37782149, 38176075, 48553152, 59339862, 60527079]
api_filter = '!9f*CwGV65'

# Call the API
SITE = StackAPI('stackoverflow')
answers = SITE.fetch('questions/{ids}/answers',
                     ids = ids,
                     sort = 'votes',
                     filter = api_filter)

# Find top voted answers
for item in answers['items']:
  if not item['question_id'] in answers_hash:
    answers_hash[item['question_id']] = [item['answer_id'], item['score']]

# Print information
for question_id, info in answers_hash.items():
  print(f"The top voted answer for {question_id} is {info[0]} with a score of {info[1]}")