cancel
Showing results for 
Search instead for 
Did you mean: 
Highlighted
Community Member

Listing all question groups in a quiz

Jump to solution

Hi,

Many of our quizzes are set up using question groups that use questions from assessment question banks. Based on retrieving the submission data, I now want to retrieve the actual quiz question info (text, primarily).

For these quizzes, api/v1/courses/{{CourseID}}/quizzes/{{QuizID}}/questions/ returns nothing. A call to api/v1/courses/{{CourseID}}/quizzes/{{QuizID}}/groups/ results in a 404.

Is there an API call to retrieve all the quiz question groups in a quiz?

Labels (1)
Tags (1)
1 Solution

Accepted Solutions
Highlighted
Navigator

saelterman@troy.edu,

Is there an API call to retrieve all the quiz question groups in a quiz?

You can get all of the quiz question group IDs, which isn't the same as the questions themselves.

I did some more looking into Question Banks tonight in an effort to figure out how to get the information out. A lot of this isn't necessary to answer the question about where to get the quiz question groups, but I wanted to include it since we had been talking about it in another place.

For this example, I'm using these values.

  • course_id = 896851
  • quiz_id = 5556241
  • assignment_id = 22540501
  • quiz_submission_id = 34201810
  • user_id = 3346297 (this is fake user for testing)

You can obtain all of the values in a question bank, but not through the API. There is an internal AJAX call that will return the information. Go to Quizzes and choose Manage Question Banks and pick the bank that you want to use.

Look at the pathname in the browser. Let's say it's /courses/896851/question_banks/4145586. What you want to do is append a /questions to the end so it looks like /courses/896851/question_banks/4145586/questions.

By default, it returns 50 items at a time, although it supports the per_page query parameter, which supports up to 100 at a time.

If you're making the call from within the browser, you'll be presented with a while(1); at the beginning. If you strip that off, you'll have the JSON of the questions in the bank. There are two properties. 

  1. "pages" tells you the number of pages you'll need to get all the questions. You can iterate through the questions with the query parameter page.
  2. "questions" contains the actual questions.

Here's the non-API call I made (I did the per_page=1 just to get 1 question).

GET /courses/896851/question_banks/4145586/questions?per_page=1

Here's what it looks like.

{
"pages":4,
"questions":[
{
"assessment_question":{
"id":147490845,
"name":"Q1a",
"question_data":{
"answers":[
{
"weight":100,
"id":52247,
"migration_id":"QUE_1005_A1",
"text":"No answer text provided.",
"html":"\u003cimg class=\"equation_image\" title=\"x=2\" src=\"/equation_images/x%3D2\" alt=\"x=2\"\u003e"
},
{
"weight":0,
"id":41690,
"migration_id":"QUE_1006_A2",
"text":"No answer text provided.",
"html":"\u003cimg class=\"equation_image\" title=\"x=3\" src=\"/equation_images/x%3D3\" alt=\"x=3\"\u003e"
},
{
"weight":0,
"id":17612,
"migration_id":"QUE_1007_A3",
"text":"No answer text provided.",
"html":"\u003cimg class=\"equation_image\" title=\"x=-2\" src=\"/equation_images/x%3D-2\" alt=\"x=-2\"\u003e"
},
{
"weight":0,
"id":42206,
"migration_id":"QUE_1008_A4",
"text":"No answer text provided.",
"html":"\u003cimg class=\"equation_image\" title=\"x=-1\" src=\"/equation_images/x%3D-1\" alt=\"x=-1\"\u003e"
}
],
"correct_comments":"",
"incorrect_comments":"",
"question_text":"Where does the graph of the rational function \u003cimg class=\"equation_image\" title=\"f(x)=\\frac{(x-2)(x-3)^2}{(x+2)(x+1)^2}\" src=\"/equation_images/f%28x%29%3D%5Cfrac%7B%28x-2%29%28x-3%29%5E2%7D%7B%28x%2B2%29%28x%2B1%29%5E2%7D\" alt=\"f(x)=\\frac{(x-2)(x-3)^2}{(x+2)(x+1)^2}\"\u003e cross the x-axis?",
"question_name":"Q1a",
"migration_id":"910600_QUE_1003",
"points_possible":1.0,
"question_type":"multiple_choice_question",
"question_bank_name":"equationtest",
"is_quiz_question_bank":true,
"prepped_for_import":true
},
"context_id":896851,
"context_type":"Course",
"workflow_state":"active",
"created_at":"2017-01-31T21:07:14Z",
"updated_at":"2017-01-31T21:07:14Z",
"assessment_question_bank_id":4145586,
"deleted_at":null,
"migration_id":"910600_QUE_1003",
"position":null
}
}
]
}‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

For each question, the assessment_question_bank_id (4145586) is the question bank ID. We will need the assessment_question.id (147490845) later to identify the question.

What is not there is the quiz_question.id that was used in the quiz. This is a bank and may be used in multiple quizzes.

So, let's go to an actual quiz that was completed. I needed to find the submission information for a quiz, so I went to the Get all quiz submissions endpoint of the Quiz Submissions API.

This call was

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

and it returned this information (the user_id=3346297 is a fake user account used for testing)

{
"quiz_submissions":[
{
"id":34201810,
"quiz_id":5556241,
"quiz_version":3,
"user_id":3346297,
"submission_id":266093043,
"score":0.0,
"kept_score":0.0,
"started_at":"2019-06-07T01:22:47Z",
"end_at":null,
"finished_at":"2019-06-07T01:23:10Z",
"attempt":1,
"workflow_state":"complete",
"fudge_points":null,
"quiz_points_possible":4,
"extra_attempts":null,
"extra_time":null,
"manually_unlocked":null,
"validation_token":"... redacted ...",
"score_before_regrade":null,
"has_seen_results":false,
"time_spent":22,
"attempts_left":0,
"overdue_and_needs_submission":false,
"excused?":false,
"html_url":"https://richland.instructure.com/courses/896851/quizzes/5556241/submissions/34201810",
"result_url":"https://richland.instructure.com/courses/896851/quizzes/5556241/history?quiz_submission_id=34201810\u0026version=1"
}
]
}‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

There I found the quiz_submission array and the first item has an id=34201810. That becomes my quiz_submission_id.

Now let's use the Get all quiz submission questions endpoint of the Quiz Submission Questions API. We need the quiz_submission_id, which is 34201810.

GET /api/v1/quiz_submissions/34201810/questions

This returns an array with 4 items in it. The first two items were from a question bank, but were included in a question group by using the "Find questions" option. The last two questions were from the question bank I'm showing here, but were linked to the question bank.

I've removed the question text just to shorten up the size of this JSON.

{
"quiz_submission_questions":[
{
"id":99130504,
"quiz_id":5556241,
"quiz_group_id":2834622,
"assessment_question_id":56267656,
"position":1,
"question_name":"Question 1",
"question_type":"numerical_question",
"question_text":"... redacted for brevity ...",
"variables":null,
"formulas":null,
"answer_tolerance":null,
"formula_decimal_places":null,
"matches":null,
"flagged":false,
"correct":false
},
{
"id":99130508,
"quiz_id":5556241,
"quiz_group_id":2834622,
"assessment_question_id":56267660,
"position":2,
"question_name":"Question 2",
"question_type":"numerical_question",
"question_text":"... redacted for brevity ...",
"variables":null,
"formulas":null,
"answer_tolerance":null,
"formula_decimal_places":null,
"matches":null,
"flagged":false,
"correct":false
},
{
"id":99130524,
"quiz_id":5556241,
"quiz_group_id":2834623,
"assessment_question_id":147490845,
"position":4,
"question_name":"Question 4",
"question_type":"multiple_choice_question",
"question_text":"... redacted for brevity ...",
"answers":[
{
"id":52247,
"text":"No answer text provided.",
"html":"\u003cimg class=\"equation_image\" title=\"x=2\" src=\"https://richland.instructure.com/equation_images/x%3D2\" alt=\"x=2\" x-canvaslms-safe-mathml='\u0026lt;math xmlns=\"http://www.w3.org/1998/Math/MathML\"\u0026gt;\n \u0026lt;mi\u0026gt;x\u0026lt;/mi\u0026gt;\n \u0026lt;mo\u0026gt;=\u0026lt;/mo\u0026gt;\n \u0026lt;mn\u0026gt;2\u0026lt;/mn\u0026gt;\n\u0026lt;/math\u0026gt;'\u003e"
},
{
"id":41690,
"text":"No answer text provided.",
"html":"\u003cimg class=\"equation_image\" title=\"x=3\" src=\"https://richland.instructure.com/equation_images/x%3D3\" alt=\"x=3\" x-canvaslms-safe-mathml='\u0026lt;math xmlns=\"http://www.w3.org/1998/Math/MathML\"\u0026gt;\n \u0026lt;mi\u0026gt;x\u0026lt;/mi\u0026gt;\n \u0026lt;mo\u0026gt;=\u0026lt;/mo\u0026gt;\n \u0026lt;mn\u0026gt;3\u0026lt;/mn\u0026gt;\n\u0026lt;/math\u0026gt;'\u003e"
},
{
"id":17612,
"text":"No answer text provided.",
"html":"\u003cimg class=\"equation_image\" title=\"x=-2\" src=\"https://richland.instructure.com/equation_images/x%3D-2\" alt=\"x=-2\" x-canvaslms-safe-mathml='\u0026lt;math xmlns=\"http://www.w3.org/1998/Math/MathML\"\u0026gt;\n \u0026lt;mi\u0026gt;x\u0026lt;/mi\u0026gt;\n \u0026lt;mo\u0026gt;=\u0026lt;/mo\u0026gt;\n \u0026lt;mo\u0026gt;\u0026amp;#x2212;\u003c!-- − --\u003e\u0026lt;/mo\u0026gt;\n \u0026lt;mn\u0026gt;2\u0026lt;/mn\u0026gt;\n\u0026lt;/math\u0026gt;'\u003e"
},
{
"id":42206,
"text":"No answer text provided.",
"html":"\u003cimg class=\"equation_image\" title=\"x=-1\" src=\"https://richland.instructure.com/equation_images/x%3D-1\" alt=\"x=-1\" x-canvaslms-safe-mathml='\u0026lt;math xmlns=\"http://www.w3.org/1998/Math/MathML\"\u0026gt;\n \u0026lt;mi\u0026gt;x\u0026lt;/mi\u0026gt;\n \u0026lt;mo\u0026gt;=\u0026lt;/mo\u0026gt;\n \u0026lt;mo\u0026gt;\u0026amp;#x2212;\u003c!-- − --\u003e\u0026lt;/mo\u0026gt;\n \u0026lt;mn\u0026gt;1\u0026lt;/mn\u0026gt;\n\u0026lt;/math\u0026gt;'\u003e"
}
],
"variables":null,
"formulas":null,
"answer_tolerance":null,
"formula_decimal_places":null,
"matches":null,
"flagged":false,
"correct":false
},
{
"id":99130525,
"quiz_id":5556241,
"quiz_group_id":2834623,
"assessment_question_id":147490846,
"position":3,
"question_name":"Question 3",
"question_type":"multiple_choice_question",
"question_text":"... redacted for brevity ...",
"answers":[
{
"id":81939,
"text":"No answer text provided.",
"html":"\u003cimg class=\"equation_image\" title=\"x=2\" src=\"https://richland.instructure.com/equation_images/x%3D2\" alt=\"x=2\" x-canvaslms-safe-mathml='\u0026lt;math xmlns=\"http://www.w3.org/1998/Math/MathML\"\u0026gt;\n \u0026lt;mi\u0026gt;x\u0026lt;/mi\u0026gt;\n \u0026lt;mo\u0026gt;=\u0026lt;/mo\u0026gt;\n \u0026lt;mn\u0026gt;2\u0026lt;/mn\u0026gt;\n\u0026lt;/math\u0026gt;'\u003e"
},
{
"id":166,
"text":"No answer text provided.",
"html":"\u003cimg class=\"equation_image\" title=\"x=3\" src=\"https://richland.instructure.com/equation_images/x%3D3\" alt=\"x=3\" x-canvaslms-safe-mathml='\u0026lt;math xmlns=\"http://www.w3.org/1998/Math/MathML\"\u0026gt;\n \u0026lt;mi\u0026gt;x\u0026lt;/mi\u0026gt;\n \u0026lt;mo\u0026gt;=\u0026lt;/mo\u0026gt;\n \u0026lt;mn\u0026gt;3\u0026lt;/mn\u0026gt;\n\u0026lt;/math\u0026gt;'\u003e"
},
{
"id":33183,
"text":"No answer text provided.",
"html":"\u003cimg class=\"equation_image\" title=\"x=-2\" src=\"https://richland.instructure.com/equation_images/x%3D-2\" alt=\"x=-2\" x-canvaslms-safe-mathml='\u0026lt;math xmlns=\"http://www.w3.org/1998/Math/MathML\"\u0026gt;\n \u0026lt;mi\u0026gt;x\u0026lt;/mi\u0026gt;\n \u0026lt;mo\u0026gt;=\u0026lt;/mo\u0026gt;\n \u0026lt;mo\u0026gt;\u0026amp;#x2212;\u003c!-- − --\u003e\u0026lt;/mo\u0026gt;\n \u0026lt;mn\u0026gt;2\u0026lt;/mn\u0026gt;\n\u0026lt;/math\u0026gt;'\u003e"
},
{
"id":47507,
"text":"No answer text provided.",
"html":"\u003cimg class=\"equation_image\" title=\"x=-1\" src=\"https://richland.instructure.com/equation_images/x%3D-1\" alt=\"x=-1\" x-canvaslms-safe-mathml='\u0026lt;math xmlns=\"http://www.w3.org/1998/Math/MathML\"\u0026gt;\n \u0026lt;mi\u0026gt;x\u0026lt;/mi\u0026gt;\n \u0026lt;mo\u0026gt;=\u0026lt;/mo\u0026gt;\n \u0026lt;mo\u0026gt;\u0026amp;#x2212;\u003c!-- − --\u003e\u0026lt;/mo\u0026gt;\n \u0026lt;mn\u0026gt;1\u0026lt;/mn\u0026gt;\n\u0026lt;/math\u0026gt;'\u003e"
}
],
"variables":null,
"formulas":null,
"answer_tolerance":null,
"formula_decimal_places":null,
"matches":null,
"flagged":false,
"correct":false
}
]
}

Notice the third question (starting in line 38) has an assessment_question_id of 147490845, which matches the one that we had when we obtained the question bank through the non-API call.

Also notice the presence of quiz_group_id in lines 6, 22, 40, and 79. The first two questions (included from the bank) were from quiz question group 2834622 and the last two (linked to the bank) were from quiz question group 2834623.

Those are the two question groups that were used in the quiz.

Now you're thinking ... OK, I want to use the Get a single quiz group endpoint of the Quiz Questions Group API.

That is mostly not-helpful. Here is the call to get the first group

GET /api/v1/courses/896851/quizzes/5556241/groups/2834622

And here is the first group. I learn from the name that it was obtained using the "Find Questions" approach and that it is position=1 or the first question group.

{
"id":2834622,
"quiz_id":5556241,
"name":"Find Questions",
"pick_count":2,
"question_points":1.0,
"position":1,
"assessment_question_bank_id":null
}

The second call was made to

GET /api/v1/courses/896851/quizzes/5556241/groups/2834623

and here is what it returned.

{
"id":2834623,
"quiz_id":5556241,
"name":"Linked Questions",
"pick_count":2,
"question_points":1.0,
"position":2,
"assessment_question_bank_id":4145586
}

From this, I know that it was "Linked Questions", it's in position=2 (second question group) and the assessment_question_bank_id=4145586, which I already knew because I created the quiz, but if you were trying to get the information after the fact, this would tell you which question bank the questions came from. Remember that it takes a non-API question to get the full contents of the bank.

Note that this quiz only had the two question groups. If there are questions not inside a question group, then things may get a little trickier and you'll have to look at the position within the questions as well as the position of the groups. I don't have an example here, it would probably only muddy an already long message, but I remember looking at this in the past.

At some point, you're going to say "I don't want the quiz questions that one student took, I want all of them that were available." You probably think that the List questions in a quiz or a submission endpoint from the Quiz Questions API would answer that. Well, it does for the "Find Questions" questions. It returns all of the questions that are in the question group.

GET /api/v1/courses/896851/quizzes/5556241/questions?per_page=100

Gives me the 24 questions that I copied into the quiz, but none of the questions that were linked to a question bank.

Ahh, but I have the question IDs now, at least for this one student. That sounds promising. Let's try the Get a single quiz question endpoint of the Quiz Questions API. The quiz question IDs are 99130504, 99130508, 99130524, and 99130525.

For the first two questions, which were obtained using "Find Questions," I get results.

GET /api/v1/courses/896851/quizzes/5556241/questions/99130504
GET /api/v1/courses/896851/quizzes/5556241/questions/99130508

These calls give me something like this:

{
"id":99130504,
"quiz_id":5556241,
"quiz_group_id":2834622,
"assessment_question_id":56267656,
"position":6,
"question_name":"Left Area 06",
"question_type":"numerical_question",
"question_text":"Temperatures in Decatur in June follow a normal model with a mean of 73.7 degrees and a standard deviation of 5.9 degrees. An innovative school has decided that the coolest 3.9% of days will constitute a weather emergency where students will be unable to study and classes will be cancelled. How cold must the temperature be to cancel school?",
"points_possible":1,
"correct_comments":"",
"incorrect_comments":"",
"neutral_comments":null,
"correct_comments_html":null,
"incorrect_comments_html":null,
"neutral_comments_html":null,
"answers":[
{
"weight":100,
"id":7112,
"text":"answer_text",
"numerical_answer_type":"exact_answer",
"exact":63.30177924
}
],
"variables":null,
"formulas":null,
"answer_tolerance":null,
"formula_decimal_places":null,
"matches":null,
"matching_answer_incorrect_matches":null
}

But now l try those that were linked to a question bank.

GET /api/v1/courses/896851/quizzes/5556241/questions/99130524
GET /api/v1/courses/896851/quizzes/5556241/questions/99130525


Instead of getting the questions, I get a 404 Not Found error.

{
"errors":[
{
"message":"The specified resource does not exist."
}
]
}

What that suggests is that the only way through the API to get the question bank questions is to fetch the Quiz Submission Questions for every user. Now, some will most likely be duplicated, so you may not need to obtain all of them. However, this won't give you every question in the bank, it will only give you the questions that were administered to students and it can only be done after the quiz has been taken. If you want all of them, you need that non-API call mentioned earlier.

None of this contains the student answers, just the questions that the students received.

If you want the student responses, you need to look at the List assignment submissions or List submissions for multiple assignments endpoints of the Submissions API and be sure to add the query parameter include[]=submission_history.

But I don't have the assignment_id needed to make this call. I'm going to use the Get a single quiz endpoint of the Quizzes API to find this out.

GET /api/v1/courses/896851/quizzes/5556241

There is too much information to show, so I've trimmed a bunch of it out.

{
"id":5556241,
"title":"Normal Probabilities Bank",
"quiz_type":"assignment",
"assignment_id":22540501,
"question_types":[
"numerical_question"
]
}

Here we get the assignment_id=22540501 and also learn that it only has numerical_question question types in the quiz.


Except that last statement is incorrect. There were numerical_question and multiple_choice question types in the quiz. But the multiple_choice question types were linked to a question bank and so they're not actually inside the quiz. There's no easy way for Canvas to know what type of questions will be administered ahead of time since the questions are pulled from the question bank when the quiz is delivered.

Now I'm going to use the submissions API to get the submission data. I would normally use the multiple version, but I only have a single assignment here. It gives me all submissions, even those not attempted. With the multiple submission version, I can specify the student.

I'll show both API calls.

GET /api/v1/courses/896851/assignments/22540501/submissions?include[]=submission_history
GET /api/v1/courses/896851/students/submissions?student_ids[]=3346297&assignment_ids[]=22540501&include[]=submission_history
‍‍

Here is the information returned.

[
{
"id":266093043,
"body":"user: 3346297, quiz: 5556241, score: 0.0, time: 2019-06-07 01:23:10 +0000",
"url":null,
"grade":"0",
"score":0.0,
"submitted_at":"2019-06-07T01:23:10Z",
"assignment_id":22540501,
"user_id":3346297,
"submission_type":"online_quiz",
"workflow_state":"graded",
"grade_matches_current_submission":true,
"graded_at":"2019-06-07T01:23:10Z",
"grader_id":-5556241,
"attempt":1,
"cached_due_date":null,
"excused":null,
"late_policy_status":null,
"points_deducted":null,
"grading_period_id":null,
"extra_attempts":null,
"late":false,
"missing":false,
"seconds_late":0,
"entered_grade":"0",
"entered_score":0.0,
"preview_url":"https://richland.instructure.com/courses/896851/assignments/22540501/submissions/3346297?preview=1\u0026version=1",
"submission_history":[
{
"id":34201810,
"body":null,
"url":null,
"grade":"0",
"score":0.0,
"submitted_at":"2019-06-07T01:23:10Z",
"assignment_id":22540501,
"user_id":3346297,
"submission_type":"online_quiz",
"workflow_state":"complete",
"grade_matches_current_submission":true,
"graded_at":null,
"grader_id":-5556241,
"attempt":1,
"cached_due_date":null,
"excused":null,
"late_policy_status":null,
"points_deducted":null,
"grading_period_id":null,
"extra_attempts":null,
"late":false,
"missing":false,
"seconds_late":0,
"entered_grade":"0",
"entered_score":0.0,
"preview_url":"https://richland.instructure.com/courses/896851/assignments/22540501/submissions/3346297?preview=1\u0026version=1",
"submission_data":[
{
"correct":false,
"points":0.0,
"question_id":99130504,
"text":"10"
},
{
"correct":false,
"points":0.0,
"question_id":99130508,
"text":"20"
},
{
"correct":false,
"points":0.0,
"question_id":99130525,
"answer_id":81939,
"text":"81939"
},
{
"correct":false,
"points":0.0,
"question_id":99130524,
"answer_id":17612,
"text":"17612"
}
]
}
],
"anonymous_id":"2XHRv"
}
]

This is an array of objects, each object is a submission. There's only one item here since I limited it to one student and one assignment. I believe that if a student took it multiple times, you would get an entry for each attempt.

Notice the submission_history.submission_data array.


The question_id  values are 99130504, 99130508, 99130525, and 99130524. This matches the questions in the order they were given on the quiz. You'll probably also notice they are not (in this case) the same order as the array giving the quiz submission question data. But if you look there, you'll see the third item was in position 4 while the 4th item was in position 3. This one is in the order given, but the other was not.

So, let's take question 4, which matches the third item that we were looking at earlier. Here is just that block.

{
"correct":false,
"points":0.0,
"question_id":99130524,
"answer_id":17612,
"text":"17612"
}

The question_id=99130524 but is this useless if you obtain the full question bank through the non-API call. It would also be useless if you created a quiz of all the questions in the question bank and exported it as a QTI file. It may (unconfirmed) have the assessment_question_id in there, but the question id is tied to a specific quiz and it isn't the quiz that you used to create the export.

The only way the 99130524 makes sense is if you get the quiz submission questions for the user. From the quiz submission question, we see that quiz_submission_questions.id=99130524 matches up with assessment_question_id=147490845 and we could tie that back to the question bank information obtained through the non-API call.

I do not know whether that number is unique per quiz or if each student getting that quiz gets a different question_id and you have to rely on the assessment_question_id instead.

We see that the answer they gave has an answer_id of 17612 and if we go back to the question bank that we obtained through the non-API call or the quiz submission questions that we obtained through the API, we see that the equation image used for the student response is the multiple choice response for x=-2. That is the incorrect answer, which is why correct=false and the points=0.0.

For instructions on how to interpret the responses, you need to look at the bottom of the Quiz Submission Questions API documentation.

I know this question was just about the quiz question groups, but I wanted to document what I knew before I forgot it. Also, having the quiz question groups is probably not enough by itself.

View solution in original post

4 Replies
Highlighted
Navigator

saelterman@troy.edu,

Is there an API call to retrieve all the quiz question groups in a quiz?

You can get all of the quiz question group IDs, which isn't the same as the questions themselves.

I did some more looking into Question Banks tonight in an effort to figure out how to get the information out. A lot of this isn't necessary to answer the question about where to get the quiz question groups, but I wanted to include it since we had been talking about it in another place.

For this example, I'm using these values.

  • course_id = 896851
  • quiz_id = 5556241
  • assignment_id = 22540501
  • quiz_submission_id = 34201810
  • user_id = 3346297 (this is fake user for testing)

You can obtain all of the values in a question bank, but not through the API. There is an internal AJAX call that will return the information. Go to Quizzes and choose Manage Question Banks and pick the bank that you want to use.

Look at the pathname in the browser. Let's say it's /courses/896851/question_banks/4145586. What you want to do is append a /questions to the end so it looks like /courses/896851/question_banks/4145586/questions.

By default, it returns 50 items at a time, although it supports the per_page query parameter, which supports up to 100 at a time.

If you're making the call from within the browser, you'll be presented with a while(1); at the beginning. If you strip that off, you'll have the JSON of the questions in the bank. There are two properties. 

  1. "pages" tells you the number of pages you'll need to get all the questions. You can iterate through the questions with the query parameter page.
  2. "questions" contains the actual questions.

Here's the non-API call I made (I did the per_page=1 just to get 1 question).

GET /courses/896851/question_banks/4145586/questions?per_page=1

Here's what it looks like.

{
"pages":4,
"questions":[
{
"assessment_question":{
"id":147490845,
"name":"Q1a",
"question_data":{
"answers":[
{
"weight":100,
"id":52247,
"migration_id":"QUE_1005_A1",
"text":"No answer text provided.",
"html":"\u003cimg class=\"equation_image\" title=\"x=2\" src=\"/equation_images/x%3D2\" alt=\"x=2\"\u003e"
},
{
"weight":0,
"id":41690,
"migration_id":"QUE_1006_A2",
"text":"No answer text provided.",
"html":"\u003cimg class=\"equation_image\" title=\"x=3\" src=\"/equation_images/x%3D3\" alt=\"x=3\"\u003e"
},
{
"weight":0,
"id":17612,
"migration_id":"QUE_1007_A3",
"text":"No answer text provided.",
"html":"\u003cimg class=\"equation_image\" title=\"x=-2\" src=\"/equation_images/x%3D-2\" alt=\"x=-2\"\u003e"
},
{
"weight":0,
"id":42206,
"migration_id":"QUE_1008_A4",
"text":"No answer text provided.",
"html":"\u003cimg class=\"equation_image\" title=\"x=-1\" src=\"/equation_images/x%3D-1\" alt=\"x=-1\"\u003e"
}
],
"correct_comments":"",
"incorrect_comments":"",
"question_text":"Where does the graph of the rational function \u003cimg class=\"equation_image\" title=\"f(x)=\\frac{(x-2)(x-3)^2}{(x+2)(x+1)^2}\" src=\"/equation_images/f%28x%29%3D%5Cfrac%7B%28x-2%29%28x-3%29%5E2%7D%7B%28x%2B2%29%28x%2B1%29%5E2%7D\" alt=\"f(x)=\\frac{(x-2)(x-3)^2}{(x+2)(x+1)^2}\"\u003e cross the x-axis?",
"question_name":"Q1a",
"migration_id":"910600_QUE_1003",
"points_possible":1.0,
"question_type":"multiple_choice_question",
"question_bank_name":"equationtest",
"is_quiz_question_bank":true,
"prepped_for_import":true
},
"context_id":896851,
"context_type":"Course",
"workflow_state":"active",
"created_at":"2017-01-31T21:07:14Z",
"updated_at":"2017-01-31T21:07:14Z",
"assessment_question_bank_id":4145586,
"deleted_at":null,
"migration_id":"910600_QUE_1003",
"position":null
}
}
]
}‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

For each question, the assessment_question_bank_id (4145586) is the question bank ID. We will need the assessment_question.id (147490845) later to identify the question.

What is not there is the quiz_question.id that was used in the quiz. This is a bank and may be used in multiple quizzes.

So, let's go to an actual quiz that was completed. I needed to find the submission information for a quiz, so I went to the Get all quiz submissions endpoint of the Quiz Submissions API.

This call was

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

and it returned this information (the user_id=3346297 is a fake user account used for testing)

{
"quiz_submissions":[
{
"id":34201810,
"quiz_id":5556241,
"quiz_version":3,
"user_id":3346297,
"submission_id":266093043,
"score":0.0,
"kept_score":0.0,
"started_at":"2019-06-07T01:22:47Z",
"end_at":null,
"finished_at":"2019-06-07T01:23:10Z",
"attempt":1,
"workflow_state":"complete",
"fudge_points":null,
"quiz_points_possible":4,
"extra_attempts":null,
"extra_time":null,
"manually_unlocked":null,
"validation_token":"... redacted ...",
"score_before_regrade":null,
"has_seen_results":false,
"time_spent":22,
"attempts_left":0,
"overdue_and_needs_submission":false,
"excused?":false,
"html_url":"https://richland.instructure.com/courses/896851/quizzes/5556241/submissions/34201810",
"result_url":"https://richland.instructure.com/courses/896851/quizzes/5556241/history?quiz_submission_id=34201810\u0026version=1"
}
]
}‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍

There I found the quiz_submission array and the first item has an id=34201810. That becomes my quiz_submission_id.

Now let's use the Get all quiz submission questions endpoint of the Quiz Submission Questions API. We need the quiz_submission_id, which is 34201810.

GET /api/v1/quiz_submissions/34201810/questions

This returns an array with 4 items in it. The first two items were from a question bank, but were included in a question group by using the "Find questions" option. The last two questions were from the question bank I'm showing here, but were linked to the question bank.

I've removed the question text just to shorten up the size of this JSON.

{
"quiz_submission_questions":[
{
"id":99130504,
"quiz_id":5556241,
"quiz_group_id":2834622,
"assessment_question_id":56267656,
"position":1,
"question_name":"Question 1",
"question_type":"numerical_question",
"question_text":"... redacted for brevity ...",
"variables":null,
"formulas":null,
"answer_tolerance":null,
"formula_decimal_places":null,
"matches":null,
"flagged":false,
"correct":false
},
{
"id":99130508,
"quiz_id":5556241,
"quiz_group_id":2834622,
"assessment_question_id":56267660,
"position":2,
"question_name":"Question 2",
"question_type":"numerical_question",
"question_text":"... redacted for brevity ...",
"variables":null,
"formulas":null,
"answer_tolerance":null,
"formula_decimal_places":null,
"matches":null,
"flagged":false,
"correct":false
},
{
"id":99130524,
"quiz_id":5556241,
"quiz_group_id":2834623,
"assessment_question_id":147490845,
"position":4,
"question_name":"Question 4",
"question_type":"multiple_choice_question",
"question_text":"... redacted for brevity ...",
"answers":[
{
"id":52247,
"text":"No answer text provided.",
"html":"\u003cimg class=\"equation_image\" title=\"x=2\" src=\"https://richland.instructure.com/equation_images/x%3D2\" alt=\"x=2\" x-canvaslms-safe-mathml='\u0026lt;math xmlns=\"http://www.w3.org/1998/Math/MathML\"\u0026gt;\n \u0026lt;mi\u0026gt;x\u0026lt;/mi\u0026gt;\n \u0026lt;mo\u0026gt;=\u0026lt;/mo\u0026gt;\n \u0026lt;mn\u0026gt;2\u0026lt;/mn\u0026gt;\n\u0026lt;/math\u0026gt;'\u003e"
},
{
"id":41690,
"text":"No answer text provided.",
"html":"\u003cimg class=\"equation_image\" title=\"x=3\" src=\"https://richland.instructure.com/equation_images/x%3D3\" alt=\"x=3\" x-canvaslms-safe-mathml='\u0026lt;math xmlns=\"http://www.w3.org/1998/Math/MathML\"\u0026gt;\n \u0026lt;mi\u0026gt;x\u0026lt;/mi\u0026gt;\n \u0026lt;mo\u0026gt;=\u0026lt;/mo\u0026gt;\n \u0026lt;mn\u0026gt;3\u0026lt;/mn\u0026gt;\n\u0026lt;/math\u0026gt;'\u003e"
},
{
"id":17612,
"text":"No answer text provided.",
"html":"\u003cimg class=\"equation_image\" title=\"x=-2\" src=\"https://richland.instructure.com/equation_images/x%3D-2\" alt=\"x=-2\" x-canvaslms-safe-mathml='\u0026lt;math xmlns=\"http://www.w3.org/1998/Math/MathML\"\u0026gt;\n \u0026lt;mi\u0026gt;x\u0026lt;/mi\u0026gt;\n \u0026lt;mo\u0026gt;=\u0026lt;/mo\u0026gt;\n \u0026lt;mo\u0026gt;\u0026amp;#x2212;\u003c!-- − --\u003e\u0026lt;/mo\u0026gt;\n \u0026lt;mn\u0026gt;2\u0026lt;/mn\u0026gt;\n\u0026lt;/math\u0026gt;'\u003e"
},
{
"id":42206,
"text":"No answer text provided.",
"html":"\u003cimg class=\"equation_image\" title=\"x=-1\" src=\"https://richland.instructure.com/equation_images/x%3D-1\" alt=\"x=-1\" x-canvaslms-safe-mathml='\u0026lt;math xmlns=\"http://www.w3.org/1998/Math/MathML\"\u0026gt;\n \u0026lt;mi\u0026gt;x\u0026lt;/mi\u0026gt;\n \u0026lt;mo\u0026gt;=\u0026lt;/mo\u0026gt;\n \u0026lt;mo\u0026gt;\u0026amp;#x2212;\u003c!-- − --\u003e\u0026lt;/mo\u0026gt;\n \u0026lt;mn\u0026gt;1\u0026lt;/mn\u0026gt;\n\u0026lt;/math\u0026gt;'\u003e"
}
],
"variables":null,
"formulas":null,
"answer_tolerance":null,
"formula_decimal_places":null,
"matches":null,
"flagged":false,
"correct":false
},
{
"id":99130525,
"quiz_id":5556241,
"quiz_group_id":2834623,
"assessment_question_id":147490846,
"position":3,
"question_name":"Question 3",
"question_type":"multiple_choice_question",
"question_text":"... redacted for brevity ...",
"answers":[
{
"id":81939,
"text":"No answer text provided.",
"html":"\u003cimg class=\"equation_image\" title=\"x=2\" src=\"https://richland.instructure.com/equation_images/x%3D2\" alt=\"x=2\" x-canvaslms-safe-mathml='\u0026lt;math xmlns=\"http://www.w3.org/1998/Math/MathML\"\u0026gt;\n \u0026lt;mi\u0026gt;x\u0026lt;/mi\u0026gt;\n \u0026lt;mo\u0026gt;=\u0026lt;/mo\u0026gt;\n \u0026lt;mn\u0026gt;2\u0026lt;/mn\u0026gt;\n\u0026lt;/math\u0026gt;'\u003e"
},
{
"id":166,
"text":"No answer text provided.",
"html":"\u003cimg class=\"equation_image\" title=\"x=3\" src=\"https://richland.instructure.com/equation_images/x%3D3\" alt=\"x=3\" x-canvaslms-safe-mathml='\u0026lt;math xmlns=\"http://www.w3.org/1998/Math/MathML\"\u0026gt;\n \u0026lt;mi\u0026gt;x\u0026lt;/mi\u0026gt;\n \u0026lt;mo\u0026gt;=\u0026lt;/mo\u0026gt;\n \u0026lt;mn\u0026gt;3\u0026lt;/mn\u0026gt;\n\u0026lt;/math\u0026gt;'\u003e"
},
{
"id":33183,
"text":"No answer text provided.",
"html":"\u003cimg class=\"equation_image\" title=\"x=-2\" src=\"https://richland.instructure.com/equation_images/x%3D-2\" alt=\"x=-2\" x-canvaslms-safe-mathml='\u0026lt;math xmlns=\"http://www.w3.org/1998/Math/MathML\"\u0026gt;\n \u0026lt;mi\u0026gt;x\u0026lt;/mi\u0026gt;\n \u0026lt;mo\u0026gt;=\u0026lt;/mo\u0026gt;\n \u0026lt;mo\u0026gt;\u0026amp;#x2212;\u003c!-- − --\u003e\u0026lt;/mo\u0026gt;\n \u0026lt;mn\u0026gt;2\u0026lt;/mn\u0026gt;\n\u0026lt;/math\u0026gt;'\u003e"
},
{
"id":47507,
"text":"No answer text provided.",
"html":"\u003cimg class=\"equation_image\" title=\"x=-1\" src=\"https://richland.instructure.com/equation_images/x%3D-1\" alt=\"x=-1\" x-canvaslms-safe-mathml='\u0026lt;math xmlns=\"http://www.w3.org/1998/Math/MathML\"\u0026gt;\n \u0026lt;mi\u0026gt;x\u0026lt;/mi\u0026gt;\n \u0026lt;mo\u0026gt;=\u0026lt;/mo\u0026gt;\n \u0026lt;mo\u0026gt;\u0026amp;#x2212;\u003c!-- − --\u003e\u0026lt;/mo\u0026gt;\n \u0026lt;mn\u0026gt;1\u0026lt;/mn\u0026gt;\n\u0026lt;/math\u0026gt;'\u003e"
}
],
"variables":null,
"formulas":null,
"answer_tolerance":null,
"formula_decimal_places":null,
"matches":null,
"flagged":false,
"correct":false
}
]
}

Notice the third question (starting in line 38) has an assessment_question_id of 147490845, which matches the one that we had when we obtained the question bank through the non-API call.

Also notice the presence of quiz_group_id in lines 6, 22, 40, and 79. The first two questions (included from the bank) were from quiz question group 2834622 and the last two (linked to the bank) were from quiz question group 2834623.

Those are the two question groups that were used in the quiz.

Now you're thinking ... OK, I want to use the Get a single quiz group endpoint of the Quiz Questions Group API.

That is mostly not-helpful. Here is the call to get the first group

GET /api/v1/courses/896851/quizzes/5556241/groups/2834622

And here is the first group. I learn from the name that it was obtained using the "Find Questions" approach and that it is position=1 or the first question group.

{
"id":2834622,
"quiz_id":5556241,
"name":"Find Questions",
"pick_count":2,
"question_points":1.0,
"position":1,
"assessment_question_bank_id":null
}

The second call was made to

GET /api/v1/courses/896851/quizzes/5556241/groups/2834623

and here is what it returned.

{
"id":2834623,
"quiz_id":5556241,
"name":"Linked Questions",
"pick_count":2,
"question_points":1.0,
"position":2,
"assessment_question_bank_id":4145586
}

From this, I know that it was "Linked Questions", it's in position=2 (second question group) and the assessment_question_bank_id=4145586, which I already knew because I created the quiz, but if you were trying to get the information after the fact, this would tell you which question bank the questions came from. Remember that it takes a non-API question to get the full contents of the bank.

Note that this quiz only had the two question groups. If there are questions not inside a question group, then things may get a little trickier and you'll have to look at the position within the questions as well as the position of the groups. I don't have an example here, it would probably only muddy an already long message, but I remember looking at this in the past.

At some point, you're going to say "I don't want the quiz questions that one student took, I want all of them that were available." You probably think that the List questions in a quiz or a submission endpoint from the Quiz Questions API would answer that. Well, it does for the "Find Questions" questions. It returns all of the questions that are in the question group.

GET /api/v1/courses/896851/quizzes/5556241/questions?per_page=100

Gives me the 24 questions that I copied into the quiz, but none of the questions that were linked to a question bank.

Ahh, but I have the question IDs now, at least for this one student. That sounds promising. Let's try the Get a single quiz question endpoint of the Quiz Questions API. The quiz question IDs are 99130504, 99130508, 99130524, and 99130525.

For the first two questions, which were obtained using "Find Questions," I get results.

GET /api/v1/courses/896851/quizzes/5556241/questions/99130504
GET /api/v1/courses/896851/quizzes/5556241/questions/99130508

These calls give me something like this:

{
"id":99130504,
"quiz_id":5556241,
"quiz_group_id":2834622,
"assessment_question_id":56267656,
"position":6,
"question_name":"Left Area 06",
"question_type":"numerical_question",
"question_text":"Temperatures in Decatur in June follow a normal model with a mean of 73.7 degrees and a standard deviation of 5.9 degrees. An innovative school has decided that the coolest 3.9% of days will constitute a weather emergency where students will be unable to study and classes will be cancelled. How cold must the temperature be to cancel school?",
"points_possible":1,
"correct_comments":"",
"incorrect_comments":"",
"neutral_comments":null,
"correct_comments_html":null,
"incorrect_comments_html":null,
"neutral_comments_html":null,
"answers":[
{
"weight":100,
"id":7112,
"text":"answer_text",
"numerical_answer_type":"exact_answer",
"exact":63.30177924
}
],
"variables":null,
"formulas":null,
"answer_tolerance":null,
"formula_decimal_places":null,
"matches":null,
"matching_answer_incorrect_matches":null
}

But now l try those that were linked to a question bank.

GET /api/v1/courses/896851/quizzes/5556241/questions/99130524
GET /api/v1/courses/896851/quizzes/5556241/questions/99130525


Instead of getting the questions, I get a 404 Not Found error.

{
"errors":[
{
"message":"The specified resource does not exist."
}
]
}

What that suggests is that the only way through the API to get the question bank questions is to fetch the Quiz Submission Questions for every user. Now, some will most likely be duplicated, so you may not need to obtain all of them. However, this won't give you every question in the bank, it will only give you the questions that were administered to students and it can only be done after the quiz has been taken. If you want all of them, you need that non-API call mentioned earlier.

None of this contains the student answers, just the questions that the students received.

If you want the student responses, you need to look at the List assignment submissions or List submissions for multiple assignments endpoints of the Submissions API and be sure to add the query parameter include[]=submission_history.

But I don't have the assignment_id needed to make this call. I'm going to use the Get a single quiz endpoint of the Quizzes API to find this out.

GET /api/v1/courses/896851/quizzes/5556241

There is too much information to show, so I've trimmed a bunch of it out.

{
"id":5556241,
"title":"Normal Probabilities Bank",
"quiz_type":"assignment",
"assignment_id":22540501,
"question_types":[
"numerical_question"
]
}

Here we get the assignment_id=22540501 and also learn that it only has numerical_question question types in the quiz.


Except that last statement is incorrect. There were numerical_question and multiple_choice question types in the quiz. But the multiple_choice question types were linked to a question bank and so they're not actually inside the quiz. There's no easy way for Canvas to know what type of questions will be administered ahead of time since the questions are pulled from the question bank when the quiz is delivered.

Now I'm going to use the submissions API to get the submission data. I would normally use the multiple version, but I only have a single assignment here. It gives me all submissions, even those not attempted. With the multiple submission version, I can specify the student.

I'll show both API calls.

GET /api/v1/courses/896851/assignments/22540501/submissions?include[]=submission_history
GET /api/v1/courses/896851/students/submissions?student_ids[]=3346297&assignment_ids[]=22540501&include[]=submission_history
‍‍

Here is the information returned.

[
{
"id":266093043,
"body":"user: 3346297, quiz: 5556241, score: 0.0, time: 2019-06-07 01:23:10 +0000",
"url":null,
"grade":"0",
"score":0.0,
"submitted_at":"2019-06-07T01:23:10Z",
"assignment_id":22540501,
"user_id":3346297,
"submission_type":"online_quiz",
"workflow_state":"graded",
"grade_matches_current_submission":true,
"graded_at":"2019-06-07T01:23:10Z",
"grader_id":-5556241,
"attempt":1,
"cached_due_date":null,
"excused":null,
"late_policy_status":null,
"points_deducted":null,
"grading_period_id":null,
"extra_attempts":null,
"late":false,
"missing":false,
"seconds_late":0,
"entered_grade":"0",
"entered_score":0.0,
"preview_url":"https://richland.instructure.com/courses/896851/assignments/22540501/submissions/3346297?preview=1\u0026version=1",
"submission_history":[
{
"id":34201810,
"body":null,
"url":null,
"grade":"0",
"score":0.0,
"submitted_at":"2019-06-07T01:23:10Z",
"assignment_id":22540501,
"user_id":3346297,
"submission_type":"online_quiz",
"workflow_state":"complete",
"grade_matches_current_submission":true,
"graded_at":null,
"grader_id":-5556241,
"attempt":1,
"cached_due_date":null,
"excused":null,
"late_policy_status":null,
"points_deducted":null,
"grading_period_id":null,
"extra_attempts":null,
"late":false,
"missing":false,
"seconds_late":0,
"entered_grade":"0",
"entered_score":0.0,
"preview_url":"https://richland.instructure.com/courses/896851/assignments/22540501/submissions/3346297?preview=1\u0026version=1",
"submission_data":[
{
"correct":false,
"points":0.0,
"question_id":99130504,
"text":"10"
},
{
"correct":false,
"points":0.0,
"question_id":99130508,
"text":"20"
},
{
"correct":false,
"points":0.0,
"question_id":99130525,
"answer_id":81939,
"text":"81939"
},
{
"correct":false,
"points":0.0,
"question_id":99130524,
"answer_id":17612,
"text":"17612"
}
]
}
],
"anonymous_id":"2XHRv"
}
]

This is an array of objects, each object is a submission. There's only one item here since I limited it to one student and one assignment. I believe that if a student took it multiple times, you would get an entry for each attempt.

Notice the submission_history.submission_data array.


The question_id  values are 99130504, 99130508, 99130525, and 99130524. This matches the questions in the order they were given on the quiz. You'll probably also notice they are not (in this case) the same order as the array giving the quiz submission question data. But if you look there, you'll see the third item was in position 4 while the 4th item was in position 3. This one is in the order given, but the other was not.

So, let's take question 4, which matches the third item that we were looking at earlier. Here is just that block.

{
"correct":false,
"points":0.0,
"question_id":99130524,
"answer_id":17612,
"text":"17612"
}

The question_id=99130524 but is this useless if you obtain the full question bank through the non-API call. It would also be useless if you created a quiz of all the questions in the question bank and exported it as a QTI file. It may (unconfirmed) have the assessment_question_id in there, but the question id is tied to a specific quiz and it isn't the quiz that you used to create the export.

The only way the 99130524 makes sense is if you get the quiz submission questions for the user. From the quiz submission question, we see that quiz_submission_questions.id=99130524 matches up with assessment_question_id=147490845 and we could tie that back to the question bank information obtained through the non-API call.

I do not know whether that number is unique per quiz or if each student getting that quiz gets a different question_id and you have to rely on the assessment_question_id instead.

We see that the answer they gave has an answer_id of 17612 and if we go back to the question bank that we obtained through the non-API call or the quiz submission questions that we obtained through the API, we see that the equation image used for the student response is the multiple choice response for x=-2. That is the incorrect answer, which is why correct=false and the points=0.0.

For instructions on how to interpret the responses, you need to look at the bottom of the Quiz Submission Questions API documentation.

I know this question was just about the quiz question groups, but I wanted to document what I knew before I forgot it. Also, having the quiz question groups is probably not enough by itself.

View solution in original post

Highlighted

James,

This answer is A+. It took me a while to review the entire sequence and recreate it. 

In the end, what I needed was just a subset of calls. Thanks to your answer on my other question about getting quiz submissions for inactive students, I am able to get the data I need.

I have it working in Postman at the moment; it will take a little longer to get it fully automated in a script. When I do, I might post something to the community in case it's relevant for others.

Thanks again!

Highlighted

what about submission comments in https://springford.instructure.com?  It is still canvas.  Along with that, I am having this problem, I cannot mark something as unread

0 Kudos
Highlighted
Your question appears so far removed from this thread that I don't know what you're asking.

For the second part: If you're running any custom JavaScript (either through Tampermonkey or the Canvas theme) then you might try disabling it first. You might also try a different browser or running in in private or incognito mode to see if it works there. If none of that makes a difference, then report it by clicking on Help and reporting a problem.
0 Kudos