Quiz Questions Groups and the API

Community Champion

 @erik_happaerts   asked the following question in the Community: Export results question group 

Our students need to take a test in which both the final result as the score for each question group is important. Up to now we were unable to export the received scores for each question group. How can this be accomplished in Canvas?

That is a non-trivial thing to do, even without looking at the responses the students gave. I like a challenge every now and then and so I looked into what it would take. The results grew so long that I thought other people might benefit from having it available as blog rather than getting lost as a response to a question, so I'm posting the findings here.


I've got a quiz I gave a stats class.

  • Fourteen students took it, multiple attempts were allowed,
  • The quiz had five questions pulled from question groups. Each question was worth 2 points.
  • The course ID is 2151246
  • The quiz ID is 4000796
  • The particular student I'm going to focus on got questions 1, 2, 3, and 5 correct, while missing question 4.
  • In all of the API calls, you'll need to worry about pagination. I've often added a per_page=100 to the end of the examples (without documenting) so that I can show you the counts from the fetches.

Quiz Submissions

Start by getting a list of the quiz submissions from the Quiz Submissions API.

GET /api/v1/courses/2151246/quizzes/4000796/submissions

You can add query parameters of include[]=user to get information about the user if you desire. 

You'll get something like this. I actually got 14 of these, but I wanted to show the whole data structure, so I set the query parameter per_page=1 to get just the first one.


The ID at the top is actually the quiz_submission_id, you'll need that later. The attempt lets me know that this is the second attempt the student made.

The results shown here are the results for the final attempt, not necessarily the attempt that was kept. Here's another student who did worse on their last attempt than they had on a previous one.


Quiz Submission Questions

The Quiz Submissions tells you the quiz_submission_id. This is not the same thing as the submission_id that is used other places, it's specific to quizzes.

Now it's time to get the questions that were asked on that particular version of the quiz. This comes from the Get all quiz submission questions endpoint of the Quiz Submissions API.

GET /api/v1/quiz_submissions/22612684/questions

Making this call returns something like this


The two most obvious properties are the quiz_group_id, which is going to tell you which group the object belongs to, and the correct, which will let you know if the student got it right.

The position tells you the position of the question on the quiz and could also be used to determine which group, so if you wanted to compare the answers for questions 3, 4, and 5 without knowing the quiz_question_group directly, that might be an approach. You would need to be careful with question groups that pull more than one question if you use this approach.

Quiz Question Groups

What's missing is from that last part how many points the question was worth. You may not need this if you just want to know the number of questions that are correct and not how many points were awarded for them.

Questions from a question group don't have value directly, the question group has points assigned and those are distributed based on how many questions there are that are selected.

We're going to need to get the question group information. We use the Get a single quiz group endpoint of the Quiz Question Groups API for this. It takes the name of the quiz_group_id from the previous object.

GET /api/v1/courses/2151246/quizzes/4000796/groups/2089889

This returns an object like this


I called my question group the very original name of "Question Group", I picked 1 question from the lot and made them worth 2 points each.

On my quiz question submissions, each of those "correct": true questions is worth 2 points.


You may or may not have a need to know what the actual question asked was. You can see in the Quiz Question Submissions section that a part of the question was provided. There is more information available, but you would need to fetch it.

You can either list questions in a quiz or submission or you can get a single question using the Quiz Questions API. They are keyed off the quiz_submission_question_id field.

GET /api/v1/courses/2151246/quizzes/4000796/questions

For my quiz, that returns an array of 60 items (each of my five question groups had 12 possibilities). Here's the object for the question we've been working with so far.


You can also get at that same information by specifying the quiz_submission_question_id in the query. This will give you just one response.

GET /api/v1/courses/2151246/quizzes/4000796/questions/73329645

What I get from here that I didn't have before is the correct answer and any comments I've entered. There may be more than one correct answer, as indicated by the "answers" being an array rather than just a single value. If there is more than one, I don't know which one "correct": true means from the Quiz Submission Question, just that it was one of the correct ones.

Multiple Attempts

So far, all of the information provided has been for the last attempt. Remember our student who made 2 attempts? How do we get at the first one?

Questions used in multiple attempts

The questions that were given on the first attempt can be obtained by adding quiz_submission_id and quiz_submission_attempt to the query parameters.

GET /api/v1/courses/2151246/quizzes/4000796/questions?quiz_submission_id=22612684&quiz_submission_attempt=1

On the first attempt, the first question was represented by this object


Also notice that by specifying the quiz_submission_id, I only got the 5 questions that were for that submission attempt. Note that specifying quiz_submission_id will not do anything unless you also specify quiz_submission_attempt. They both have to be included to have any effect.

Submission questions used in multiple attempts

Great! But that didn't tell me whether or not the student got it right. For this, you need to get the Quiz Submission Questions, but specify the attempt to use.

The attempt is passed as a query parameter of quiz_submission_attempt. You don't need to specify the quiz_submission_id here in the query string because it's already part of the route.

GET /api/v1/quiz_submissions/22612684/questions?quiz_submission_attempt=1

This gives me this object.


That doesn't give me the correct answer, but it does let me know that the student missed it because of the "correct": false.

Individual Questions

Let's say that you have some questions that weren't part of a question group.  I did in quiz_id = 4000809. 

Fetching the list of submissions only returned 13 items because one student didn't take that quiz. The first quiz_submission_id there was 22612342.

The last question on the quiz was given to everyone. Here's what that object looks like.


Notice that the quiz_group_id is null when it's an individual question.

Linked Question Banks

What about questions pulled from a question bank? I don't generally use these -- finding them more hassle than their worth -- but some people may.

To help answer that question, I went in and created a quiz with one question group that pulled 2 questions from a question bank. This is in a sandbox, which has a different course ID.

First I get the list of submissions

GET /api/v1/courses/896851/quizzes/4075752/submissions


Then I grab the submission questions

GET /api/v1/quiz_submissions/22645502/questions




There's more -- including the possible choices and whether or not the student got the question correct, but no idea of how many points they got on it.

The presence of assessment_question_id lets me know that this came from a linked question bank. That may not matter to you since there's also a quiz_group_id and that may be all that you care about.

However, if you go to fetch the questions in the normal way, you're not going to like the results.

GET /api/v1/courses/896851/quizzes/4075752/questions

Here is what it returned.


That's right, it returned nothing. When you link the questions, they don't actually get brought into the quiz and you can't get the question from the list quiz questions endpoint.

And if you try to get a single question ...

GET /api/v1/courses/896851/quizzes/4075752/questions/74214072

then you get this response


At least getting the question group information is useful

GET /api/v1/courses/896851/quizzes/4075752/groups/2143822


This lets us know that the questions were pulled from question bank with the ID of 1412342. That, coupled with the assessment_question_id from the previous results, lets us identify the question bank and question in it.

Unfortunately, as best I can tell, the Question Banks aren't exposed through the API: Where is the Question Bank API?