Skip to content

questions.get()

Retrieves a single question by its unique identifier, including complete content, metadata, and current user interaction status.

async get(questionId: number): Promise<QuestionResponseModel>
ParameterTypeRequiredDescription
questionIdnumberYesThe unique identifier of the question to retrieve

Returns a Promise<QuestionResponseModel> containing complete question information:

PropertyTypeDescription
idnumberThe question’s unique identifier
titlestringThe actual question, stated briefly in one sentence
bodystringQuestion body content in HTML format
bodyMarkdownstringQuestion body content in Markdown format
tagsTagSummaryResponseModel[]Tags associated with the question
ownerUserSummaryResponseModelQuestion author information
lastEditorUserSummaryResponseModelLast user to edit the question
creationDateDateWhen the question was created
lastActivityDateDateLast significant activity (edit, answer, vote, etc.)
scorenumberNet score (upvotes minus downvotes)
isAnsweredbooleanTrue if question has at least one upvoted answer
answerCountnumberTotal number of answers
viewCountnumberNumber of times the question has been viewed
webUrlstringDirect URL to the question
shareUrlstringShareable URL for the question
isDeletedbooleanTrue if the question was deleted
isObsoletebooleanTrue if marked obsolete by moderator
isClosedbooleanTrue if closed by moderator
bountyBountyResponseModelBounty information if active
communitiesCommunitySummaryResponseModel[]Associated communities
mentionedUsersMentionedUserResponseModel[]Users mentioned in the question
mentionedUserGroupsMentionedUserGroupResponseModel[]User groups mentioned
userIsFollowingbooleanTrue if current user follows this question
userHasUpvotedbooleanTrue if current user upvoted
userHasDownvotedbooleanTrue if current user downvoted
userHasBookmarkedbooleanTrue if current user bookmarked
import { StackOverflowSDK } from 'so-teams-sdk';
const sdk = new StackOverflowSDK({
accessToken: 'your-access-token',
baseUrl: 'https://[your-site].stackenterprise.co/api/v3'
});
// Get a specific question
const question = await sdk.questions.get(12345);
console.log('Question Details:');
console.log(`Title: ${question.title}`);
console.log(`Author: ${question.owner?.displayName}`);
console.log(`Score: ${question.score}`);
console.log(`Views: ${question.viewCount}`);
console.log(`Answers: ${question.answerCount}`);
console.log(`Created: ${question.creationDate}`);
console.log(`Tags: ${question.tags?.map(t => t.name).join(', ')}`);
console.log(`URL: ${question.webUrl}`);
// Display content (HTML format)
console.log('\nContent (HTML):');
console.log(question.body);
// Display content (Markdown format)
console.log('\nContent (Markdown):');
console.log(question.bodyMarkdown);
async function analyzeQuestion(questionId: number) {
try {
const question = await sdk.questions.get(questionId);
// Analyze question metrics
const metrics = {
engagement: {
score: question.score || 0,
views: question.viewCount || 0,
answers: question.answerCount || 0,
engagementRatio: (question.score || 0) / Math.max(question.viewCount || 1, 1) * 100
},
status: {
isAnswered: question.isAnswered || false,
isClosed: question.isClosed || false,
isDeleted: question.isDeleted || false,
isObsolete: question.isObsolete || false,
hasBounty: !!question.bounty
},
userInteraction: {
isFollowing: question.userIsFollowing || false,
hasUpvoted: question.userHasUpvoted || false,
hasDownvoted: question.userHasDownvoted || false,
hasBookmarked: question.userHasBookmarked || false
},
timeline: {
age: Math.floor((Date.now() - new Date(question.creationDate).getTime()) / (1000 * 60 * 60 * 24)),
lastActivity: question.lastActivityDate ?
Math.floor((Date.now() - new Date(question.lastActivityDate).getTime()) / (1000 * 60 * 60 * 24)) :
null,
wasEdited: question.lastEditor && question.lastEditor.userId !== question.owner?.userId
}
};
console.log(`Question Analysis: ${question.title}`);
console.log(`- Score: ${metrics.engagement.score} (${metrics.engagement.engagementRatio.toFixed(2)}% engagement ratio)`);
console.log(`- Views: ${metrics.engagement.views}`);
console.log(`- Answers: ${metrics.engagement.answers}`);
console.log(`- Age: ${metrics.timeline.age} days`);
console.log(`- Last activity: ${metrics.timeline.lastActivity} days ago`);
console.log(`- Status: ${metrics.status.isAnswered ? 'Answered' : 'Unanswered'}`);
if (metrics.status.isClosed) console.log('- CLOSED by moderator');
if (metrics.status.hasBounty) console.log('- Has active bounty');
if (metrics.timeline.wasEdited) console.log('- Has been edited');
console.log('\nUser Interactions:');
if (metrics.userInteraction.hasUpvoted) console.log('- You upvoted this question');
if (metrics.userInteraction.hasDownvoted) console.log('- You downvoted this question');
if (metrics.userInteraction.hasBookmarked) console.log('- You bookmarked this question');
if (metrics.userInteraction.isFollowing) console.log('- You are following this question');
return { question, metrics };
} catch (error) {
console.error('Failed to analyze question:', error.message);
throw error;
}
}
const analysis = await analyzeQuestion(12345);
async function processQuestionContent(questionId: number) {
const question = await sdk.questions.get(questionId);
// Extract and analyze content
const contentAnalysis = {
basic: {
titleLength: question.title?.length || 0,
bodyLength: question.body?.length || 0,
markdownLength: question.bodyMarkdown?.length || 0,
tagCount: question.tags?.length || 0
},
structure: {
hasCodeBlocks: (question.bodyMarkdown || '').includes('```'),
hasLinks: (question.bodyMarkdown || '').includes('['),
hasLists: (question.bodyMarkdown || '').includes('- ') || (question.bodyMarkdown || '').includes('1. '),
hasImages: (question.bodyMarkdown || '').includes('!['),
paragraphCount: (question.bodyMarkdown || '').split('\n\n').length
},
mentions: {
userCount: question.mentionedUsers?.length || 0,
groupCount: question.mentionedUserGroups?.length || 0,
users: question.mentionedUsers?.map(u => u.displayName) || [],
groups: question.mentionedUserGroups?.map(g => g.name) || []
},
tags: question.tags?.map(tag => ({
name: tag.name,
id: tag.id,
questionCount: tag.count
})) || []
};
console.log(`Content Analysis for: ${question.title}`);
console.log(`\nBasic Metrics:`);
console.log(`- Title: ${contentAnalysis.basic.titleLength} characters`);
console.log(`- Body: ${contentAnalysis.basic.bodyLength} characters (HTML)`);
console.log(`- Markdown: ${contentAnalysis.basic.markdownLength} characters`);
console.log(`- Tags: ${contentAnalysis.basic.tagCount}`);
console.log(`\nContent Structure:`);
console.log(`- Code blocks: ${contentAnalysis.structure.hasCodeBlocks ? 'Yes' : 'No'}`);
console.log(`- Links: ${contentAnalysis.structure.hasLinks ? 'Yes' : 'No'}`);
console.log(`- Lists: ${contentAnalysis.structure.hasLists ? 'Yes' : 'No'}`);
console.log(`- Images: ${contentAnalysis.structure.hasImages ? 'Yes' : 'No'}`);
console.log(`- Paragraphs: ${contentAnalysis.structure.paragraphCount}`);
if (contentAnalysis.mentions.userCount > 0) {
console.log(`\nMentioned Users: ${contentAnalysis.mentions.users.join(', ')}`);
}
if (contentAnalysis.mentions.groupCount > 0) {
console.log(`Mentioned Groups: ${contentAnalysis.mentions.groups.join(', ')}`);
}
console.log(`\nTags:`);
contentAnalysis.tags.forEach(tag => {
console.log(`- ${tag.name} (used in ${tag.questionCount} questions)`);
});
return contentAnalysis;
}
const contentAnalysis = await processQuestionContent(12345);
async function getQuestionForWorkflow(questionId: number, workflow: 'review' | 'answer' | 'moderate') {
try {
const question = await sdk.questions.get(questionId);
// Prepare workflow-specific data
const workflowData = {
question,
workflow,
metadata: {
retrieved: new Date().toISOString(),
questionAge: Math.floor((Date.now() - new Date(question.creationDate).getTime()) / (1000 * 60 * 60 * 24)),
lastActivityAge: question.lastActivityDate ?
Math.floor((Date.now() - new Date(question.lastActivityDate).getTime()) / (1000 * 60 * 60)) : null
}
};
switch (workflow) {
case 'review':
return {
...workflowData,
reviewChecklist: {
hasTitle: !!question.title && question.title.length > 10,
hasContent: !!question.body && question.body.length > 50,
hasTags: question.tags && question.tags.length > 0,
isNotDeleted: !question.isDeleted,
isNotObsolete: !question.isObsolete,
needsAttention: !question.isAnswered && question.score < 0
}
};
case 'answer':
return {
...workflowData,
answerContext: {
canAnswer: !question.isClosed && !question.isDeleted,
hasExistingAnswers: question.answerCount > 0,
isAnswered: question.isAnswered,
difficulty: question.score > 5 ? 'advanced' : question.score > 0 ? 'intermediate' : 'beginner',
tags: question.tags?.map(t => t.name) || []
}
};
case 'moderate':
return {
...workflowData,
moderationContext: {
requiresModeration: question.score < -2 || question.viewCount > 1000,
isClosed: question.isClosed,
isDeleted: question.isDeleted,
isObsolete: question.isObsolete,
hasBounty: !!question.bounty,
flagCount: 0, // Would need separate API call
reportedIssues: []
}
};
default:
return workflowData;
}
} catch (error) {
console.error(`Failed to get question for ${workflow} workflow:`, error.message);
throw error;
}
}
// Usage examples
const reviewData = await getQuestionForWorkflow(12345, 'review');
const answerData = await getQuestionForWorkflow(12345, 'answer');
const moderationData = await getQuestionForWorkflow(12345, 'moderate');
console.log('Review checklist:', reviewData.reviewChecklist);
console.log('Answer context:', answerData.answerContext);
console.log('Moderation context:', moderationData.moderationContext);
async function getBulkQuestions(questionIds: number[]) {
console.log(`Retrieving ${questionIds.length} questions...`);
const results = {
successful: [],
failed: [],
summary: {
retrieved: 0,
errors: 0,
totalRequested: questionIds.length
}
};
for (let i = 0; i < questionIds.length; i++) {
const questionId = questionIds[i];
try {
console.log(`Loading question ${i + 1}/${questionIds.length}: ${questionId}`);
const question = await sdk.questions.get(questionId);
results.successful.push({
questionId,
question,
index: i
});
results.summary.retrieved++;
// Add delay to avoid rate limiting
if (i < questionIds.length - 1) {
await new Promise(resolve => setTimeout(resolve, 500));
}
} catch (error) {
console.error(`Failed to load question ${questionId}:`, error.message);
results.failed.push({
questionId,
error: error.message,
index: i
});
results.summary.errors++;
}
}
console.log(`\nBulk retrieval completed:`);
console.log(`- Successfully retrieved: ${results.summary.retrieved}/${results.summary.totalRequested}`);
console.log(`- Errors: ${results.summary.errors}`);
return results;
}
const bulkResults = await getBulkQuestions([12345, 12346, 12347, 12348]);
// Process successful retrievals
bulkResults.successful.forEach(result => {
console.log(`${result.question.title} - Score: ${result.question.score}`);
});
// Handle failed retrievals
if (bulkResults.failed.length > 0) {
console.log('Failed to retrieve:');
bulkResults.failed.forEach(failure => {
console.log(`- Question ${failure.questionId}: ${failure.error}`);
});
}
async function monitorQuestionState(questionId: number, checkInterval: number = 5000) {
console.log(`Starting monitoring for question ${questionId}`);
let previousState = await sdk.questions.get(questionId);
console.log(`Initial state: Score ${previousState.score}, Answers ${previousState.answerCount}`);
const monitor = setInterval(async () => {
try {
const currentState = await sdk.questions.get(questionId);
// Check for changes
const changes = [];
if (currentState.score !== previousState.score) {
changes.push(`Score: ${previousState.score}${currentState.score}`);
}
if (currentState.answerCount !== previousState.answerCount) {
changes.push(`Answers: ${previousState.answerCount}${currentState.answerCount}`);
}
if (currentState.viewCount !== previousState.viewCount) {
changes.push(`Views: ${previousState.viewCount}${currentState.viewCount}`);
}
if (currentState.isAnswered !== previousState.isAnswered) {
changes.push(`Answered: ${previousState.isAnswered}${currentState.isAnswered}`);
}
if (currentState.isClosed !== previousState.isClosed) {
changes.push(`Closed: ${previousState.isClosed}${currentState.isClosed}`);
}
if (changes.length > 0) {
console.log(`\n[${new Date().toLocaleTimeString()}] Question ${questionId} changes:`);
changes.forEach(change => console.log(`- ${change}`));
}
previousState = currentState;
} catch (error) {
console.error('Monitoring error:', error.message);
}
}, checkInterval);
// Return a function to stop monitoring
return () => {
clearInterval(monitor);
console.log(`Stopped monitoring question ${questionId}`);
};
}
// Start monitoring (returns stop function)
const stopMonitoring = await monitorQuestionState(12345, 10000); // Check every 10 seconds
// Stop after 1 minute
setTimeout(() => {
stopMonitoring();
}, 60000);
async function exportQuestionData(questionId: number, format: 'json' | 'markdown' | 'summary' = 'json') {
try {
const question = await sdk.questions.get(questionId);
switch (format) {
case 'json':
return {
exportFormat: 'json',
exportedAt: new Date().toISOString(),
data: question
};
case 'markdown':
const markdownContent = `# ${question.title}
**Author:** ${question.owner?.displayName || 'Unknown'}
**Created:** ${question.creationDate}
**Score:** ${question.score}
**Views:** ${question.viewCount}
**Answers:** ${question.answerCount}
**Tags:** ${question.tags?.map(t => `\`${t.name}\``).join(', ') || 'None'}
## Question
${question.bodyMarkdown || question.body || 'No content available'}
---
**URL:** ${question.webUrl}
**Share:** ${question.shareUrl}`;
return {
exportFormat: 'markdown',
exportedAt: new Date().toISOString(),
content: markdownContent
};
case 'summary':
return {
exportFormat: 'summary',
exportedAt: new Date().toISOString(),
summary: {
id: question.id,
title: question.title,
author: question.owner?.displayName,
metrics: {
score: question.score,
views: question.viewCount,
answers: question.answerCount
},
dates: {
created: question.creationDate,
lastActivity: question.lastActivityDate
},
status: {
answered: question.isAnswered,
closed: question.isClosed,
deleted: question.isDeleted
},
tags: question.tags?.map(t => t.name) || [],
urls: {
web: question.webUrl,
share: question.shareUrl
}
}
};
default:
throw new Error(`Unsupported export format: ${format}`);
}
} catch (error) {
console.error('Question export failed:', error.message);
throw error;
}
}
// Export in different formats
const jsonExport = await exportQuestionData(12345, 'json');
const markdownExport = await exportQuestionData(12345, 'markdown');
const summaryExport = await exportQuestionData(12345, 'summary');
console.log('Markdown Export:');
console.log(markdownExport.content);

This method can throw the following errors:

Error TypeStatus CodeDescription
AuthenticationError401Invalid or missing authentication token
TokenExpiredError401Authentication token has expired
ForbiddenError403Insufficient permissions to access the question
NotFoundError404Question with the specified ID does not exist
SDKErrorVariousOther API or network errors
import StackOverflowSDK, { NotFoundError, ForbiddenError, AuthenticationError } from 'so-teams-sdk';
const sdk = new StackOverflowSDK({
accessToken: 'your-access-token',
baseUrl: 'https://[your-site].stackenterprise.co/api/v3'
});
try {
const question = await sdk.questions.get(12345);
console.log('Question retrieved successfully:', question.title);
} catch (error) {
if (error instanceof NotFoundError) {
console.error('Question not found - it may have been deleted or the ID is incorrect');
} else if (error instanceof ForbiddenError) {
console.error('Cannot access question - insufficient permissions or private content');
} else if (error instanceof AuthenticationError) {
console.error('Authentication required to access this question');
} else {
console.error('Failed to retrieve question:', error.message);
}
}
async function safeGetQuestion(questionId: number) {
try {
const question = await sdk.questions.get(questionId);
return {
success: true,
question,
message: 'Question retrieved successfully'
};
} catch (error) {
if (error instanceof NotFoundError) {
return {
success: false,
reason: 'not_found',
message: 'Question does not exist or has been deleted'
};
} else if (error instanceof ForbiddenError) {
return {
success: false,
reason: 'forbidden',
message: 'Insufficient permissions to access this question'
};
}
return {
success: false,
reason: 'error',
message: error.message
};
}
}
const result = await safeGetQuestion(12345);
if (result.success) {
console.log(`Retrieved: ${result.question.title}`);
} else {
console.log(`Failed to retrieve question: ${result.message}`);
}
  • User Interaction Status: Properties like userHasUpvoted, userHasBookmarked reflect the current authenticated user’s interactions
  • Content Formats: Both HTML (body) and Markdown (bodyMarkdown) versions are provided
  • Status Flags: Multiple boolean flags indicate question state (closed, deleted, obsolete, answered)
  • Activity Tracking: lastActivityDate includes any significant activity (edits, answers, votes, bounties)
  • View Counting: viewCount tracks unique question views across all users
  • URL Properties: webUrl for direct access, shareUrl for sharing functionality
  • Mention Detection: Automatically identifies mentioned users and groups in question content
  • Bounty Information: Active bounties are included in the bounty property
  • Tag Metadata: Tags include usage statistics and identification information
  • Performance: Single question retrieval is faster than filtered queries but consider caching for repeated access