Is there an API call that get a list of all submissions for one specific quiz and one specific user?

Jump to solution
Community Explorer

I have a Python program that gets all quizzes from a course and for each quiz, it gets all submissions for that quiz. After that, gets all attempts for each submission. It is a triply nested for loop.

I would like to have an API call that knowing the course_id, quiz_id, user_id, could get all submissions for that quiz for that user.

Something similar to what we have for the assignments.

GET /api/v1/courses/:course_id/assignments/:assignment_id/submissions/:user_id

Thanks in advance.

1 Solution
Community Champion

 @marcia_moraes ,

The List submissions for multiple assignments endpoint of the submissions API will do everything you say you want (and a little more). It's what Canvas uses to fetch the gradebook information, but it can be used for other things.

The basic call is

GET /api/v1/course/:course_id/students/submissions

but that won't do anything by itself.

  • You need to add at least one assignment_ids[]= and at least one student_ids[] to the query.
  • To get the grades for all of the attempts, add include[]=submission_history.
  • The quiz_id is available inside the body property, but you will need to parse it from the quiz item. The body looks kind of like JSON, but it's not properly formed. Alternatively, you can include[]=assignment and that gives you the quiz_id. I would not include the assignment if you are fetching multiple students as it returns the assignment object for each student.
  • You can add per_page=100 to fetch more up to 100 submissions at a time. This minimizes the number of API calls that need made, although this is not necessarily the fastest way to get the information.

What I would do for the quiz_id is to get a list of all of the quizzes first or assignments first.

  • If you use the List quizzes in a course endpoint of the Quizzes API, then id is the quiz_id and you will need to hang onto the assignment_id to make the submissions call. The submission call needs the assignment_id, not the quiz_id.
  • If you use the List assignments endpoint of the Assignments API, then assignments that are quizzes will have is_quiz_assignment=true and also a quiz_id. Hang onto the quiz_id because you want it, but the id for the assignment list is the assignment_id, which is what you need for the submissions call.
  • If you use the List assignment groups endpoint of the Assignment Groups API and include[]=assignments then you can get all of the assignments with a single call (typically) without having to worry about pagination. What I wrote about the list assignments applies here as well. You can cut down the amount of information transferred, which speeds things up with exclude_response_fields[]=rubric and exclude_response_fields[]=description. You can also exclude_assignment_submission_types[]=discussion_topicexclude_assignment_submission_types[]=wiki_page, and exclude_assignment_submission_types[]=external_tool to further cut down the information transferred. Unfortunately, there is not an include_assignment_submission_types[]=online_quiz, (I used strikethrough to emphasize it's not there) you would get that from the list quizzes endpoint.

Then I would have a single loop that iterated through the assignments, perhaps in batches of assignments, depending on how many quizzes you have. I would use student_ids[]=all to get the submissions for all students with an active enrollment. You have to explicitly list student ids for those students who are concluded or inactive as they are not included.

The assignment_ids[] will take multiple values, which is why I mentioned batching. For example, if you had 20 quizzes that you wanted to obtain the information for, you could probably put all 20 at the same time by repeating assignment_ids[], but I would either go 1 at a time (number of API calls = number of quizzes) or do array slices and make perhaps 4 or 5 at a time.

The more information you request, the more likely you are to run into pagination issues. You can get up to 100 submissions at a time with per_page=100, so that could be up to 100 students for a single assignment, 50 students for 2 assignments, or 25 students for 4 assignments. Pagination for the multiple submissions endpoint uses the opaque bookmark, so you cannot predict what the next page will be, you'll have to wait for the first request to finish before starting the next. That blocks the beauty of concurrent calls. As long as your students × assignments doesn't exceed 100, you can make multiple concurrent calls, subject to the rate limiting that Canvas has in place. Browsers allow 5-7 concurrent requests, but if you try 14 at the same instant, you'll hit the threshold and get blocked and they won't succeed.

As for the actual request, what you're going to get is a structure that looks like this (I've removed a lot of the information)

"body":"user: 9335957, quiz: 6068641, score: 5.0, time: 2020-02-13 01:43:09 +0000",

The array here only contains one object, but you would have one for each student.

  • The id in each case is the submission id.
  • The user_id and assignment_id are the what they say.
  • The quiz_id is called quiz inside the body. What I would do, though, is use the quiz id that I found earlier or tie it to the assignment_id.
  • The grader_id is the opposite of the quiz_id when the quiz is automatically graded. In this case, the quiz_id=6068641 and the grader_id=-6068641.
  • The score is the overall score. In this case, 5.67 is the average of 6, 6, and 5, which is how I grade my quizzes.
  • The submission_history gives all of the attempts. In this case, my student took it 3 times.
    • id is the submission id. I know you didn't ask for this, but it's different from the overall submission id and for some API calls you need it. 
    • score is the score for that attempt. Note that there is also a grade property, but that is the grade for all of the attempts, not the grade for the listed attempt. Be sure to use grade
    • submitted_at is the time when the student submitted the quiz.
    • attempt is the attempt number

View solution in original post