The Instructure Community will enter a read-only state on November 22, 2025 as we prepare to migrate to our new Community platform in early December. Read our blog post for more info about this change.
Found this content helpful? Log in or sign up to leave a like!
EDIT: Updated the post to reflect some of the things I learned in comments and feedback to make this more clear.
Good day! First time posting here, so here's to hoping I can make this easy to understand. I'm an instructor with a passion to automate manual tasks.
Foregoing the fact that I'm using the Python canvasapi, I'm struggling with the Live API (which will later inform how I use my Python API).
The Canvas LMS API provides the following example of a hash:
{ "quiz_submissions": [{ "attempt": 1, "fudge_points": -2.4, "questions": { "1": { "score": 2.5, "comment": "This can't be right, but I'll let it pass this one time." }, "2": { "score": 0, "comment": "Good thinking. Almost!" } } }] }
The link to that source is here:
Here's the hash my Python code produces:
{
'quiz_submissions': [{
'attempt': 1,
'fudge_points': 0,
'questions': {
'229417081': {'score': 10, 'comment': ''},
'229417071': {'score': 10, 'comment': ''},
'229417074': {'score': 10, 'comment': ''},
'229417023': {'score': 10, 'comment': ''},
'229415416': {'score': 30, 'comment': ''},
'229417041': {'score': 30, 'comment': ''}
}
}]
}We've already established that the question IDs are the correct value to use rather than 1/2/3/etc, and the single/double quotes are not the issue. The course_id, quiz_id, and submission ID I give to Live API are all correct, as they work for the GETs. It's this one PUT that is rejecting my hash with the following error:
{ "status": "bad_request", "message": "missing required key :quiz_submissions" }
What am I doing wrong with my hash in Live API?
Solved! Go to Solution.
The canvasapi Python library is just a wrapper for raw API calls -- internally, it still makes the API calls as stated in the official Instructure Canvas API documentation.
The canvasapi Python library just handles the pagination and also wraps the JSON responses into a Python object that can be manipulated easier. Think of the library as a way to quickly interface with the official Canvas API without having to be bogged down with handling HTTP calls. 🙂
I also recommend reading the following as well: Keyword Arguments -- canvasapi as it will give you the syntax for making the calls within the canvasapi library. Note that the update score and submissions API code matches the 'list of nested parameters' style.
I did make a successful update_score_and_comments call in my beta instance right now and here's the format of how the call should work within the canvasapi library:
(the update object to update the submission by)
update_obj = [
{
'attempt': #the attempt number that you want to edit, required,
'fudge_points': #optional, how much to fudge by,
'questions': {
<question id, in quotes>: {
'score': <score>,
'comment': <comment, in quotes>
},
#continue listing questions in the format above
}
}
]
(the call itself)
#you have to fetch the submission that you want to edit. This I will leave up as an exercise.
quiz_sub = #the quiz submission that you fetched up
update = quiz_sub.update_score_and_comments(quiz_submissions=update_obj) #update_obj from the above code
print(update) #should print up the quiz no, the user id, and the submission noHope this helps!
I know next to nothing about using API, but I can pattern match pretty good. two things I noticed that were different were the numbers in the new_dict list and what the Api example showed ("1","2" etc) for the question numbers - I can go with your numbers based on a data base value possibly, but I don't know. The other thing I noticed which once again may or may not matter is that in new_dict all your entries are in single quotes, but what is shown is the example is double qoutes - but python (to my limited knowledge with it) treats those the same, but is that case for how it is interpreted. Just my observations. You would know more about the correctness of what I stated.
Ron
@Ron_Bowman , both good points! I have an OK answer to the first one about the numbers 1/2/3 vs my seemingly-arbitrary numbers. I can't find it again, but I read a thread from a couple years ago where someone was bemoaning that the documentation at the link in the OP was wrong and that the "question ID" shouldn't be 1, 2, 3, etc but instead should be the actual question ID within Canvas. I've tried both in case that gentlemen was wrong, and I'm sticking with the actual question IDs since that makes the most sense to me for an API.
As for the single/double quotes, also an interesting thought. I use double quotes in my actual code. I copied the new_dict value from the VSCode debugger, which always uses single quotes. I can't imagine VSCode subbing my double quotes for single quotes being the problem, but maybe? Not sure what I could do about that other than use a different IDE.
Hi @MichaelCrowl,
Can you post your full python code somewhere where we might be able to review the whole thing? I know enough python to write some scripts and interpret some others, but the snippets you've posted here just aren't quite making sense to me without the rest of the code to go along with it. You also mentioned both python API calls and the Live API, which would be two different things. Are you able to clarify your methodology at all? Any additional code or clarifications you can give will likely help me or others here try to figure out where something might be going wrong.
-Chris
@chriscas , thanks for the prompt reply! I want to focus on the format of the hash that the API expects before I throw all the code your way. To maybe help clarify and avoid having you compare the link in the OP to my code, here's the documented format of a hash:
{ "quiz_submissions": [{ "attempt": 1, "fudge_points": -2.4, "questions": { "1": { "score": 2.5, "comment": "This can't be right, but I'll let it pass this one time." }, "2": { "score": 0, "comment": "Good thinking. Almost!" } } }] }
And here is my overall hash from the OP, but combined together to remove the variable obfuscation.
{
'quiz_submissions': [{
'attempt': 1,
'fudge_points': 0,
'questions': {
'229417081': {'score': 10, 'comment': ''},
'229417071': {'score': 10, 'comment': ''},
'229417074': {'score': 10, 'comment': ''},
'229417023': {'score': 10, 'comment': ''},
'229415416': {'score': 30, 'comment': ''},
'229417041': {'score': 30, 'comment': ''}
}
}]
}As Mr Bowman asked in his question above, the single quotes are a product of the VSCode debugger, so I can't imagine that being a problem. As far as I can tell, this should work per the API documentation. I've tried using both the question IDs as shown above and numbering them 1-6, but neither work.
As for your question on Canvas API vs Live API, I was using Live API as a method to debug. I gave it my course_id, quiz_id, and submission ID to verify I had all the right values and am indeed able to get the correct data back. When I try that with the PUT for "update student scores and comments" with the above hash, it gives me the message "missing required key :quiz_submissions"
Does that help? If not, I can share more of the relevant code.
Without the full code, it'd be hard to diagnose the issue, but looking at your Python code, I think you're using the canvasapi Python library built by UCF.
As @chriscas mentioned, the Live API located at <instance>.instructure.com/docs/api/live and the canvasapi Python library are two different things, so we need to know in which environment your API calls are failing along with as much detail as possible so we can help.
@melodyc_lam, I think I see what you're all saying now. I assumed that the API call the Python library makes would be the same that Live API would use? For reference, I'm using this Python library:
https://canvasapi.readthedocs.io/en/stable/getting-started.html
I think you correctly identified it, and I assumed it was an official Instructure library. In the end, I can't get either API to work (Live or Python), but I can get quiz submissions from both, which is why I'm still stuck on the idea of submitting the correctly-formatted hash - it's the only difference between the GETs and PUT.
The canvasapi Python library is just a wrapper for raw API calls -- internally, it still makes the API calls as stated in the official Instructure Canvas API documentation.
The canvasapi Python library just handles the pagination and also wraps the JSON responses into a Python object that can be manipulated easier. Think of the library as a way to quickly interface with the official Canvas API without having to be bogged down with handling HTTP calls. 🙂
I also recommend reading the following as well: Keyword Arguments -- canvasapi as it will give you the syntax for making the calls within the canvasapi library. Note that the update score and submissions API code matches the 'list of nested parameters' style.
I did make a successful update_score_and_comments call in my beta instance right now and here's the format of how the call should work within the canvasapi library:
(the update object to update the submission by)
update_obj = [
{
'attempt': #the attempt number that you want to edit, required,
'fudge_points': #optional, how much to fudge by,
'questions': {
<question id, in quotes>: {
'score': <score>,
'comment': <comment, in quotes>
},
#continue listing questions in the format above
}
}
]
(the call itself)
#you have to fetch the submission that you want to edit. This I will leave up as an exercise.
quiz_sub = #the quiz submission that you fetched up
update = quiz_sub.update_score_and_comments(quiz_submissions=update_obj) #update_obj from the above code
print(update) #should print up the quiz no, the user id, and the submission noHope this helps!
@melodyc_lam , thank you so much for this response, and I apologize for the delayed response. Now that the semester is over, I can finally focus on these back burner projects. My update_obj was a list inside a dictionary, which might've been part of the problem. Here's the entirety of my function, slightly revised from what I posted before to match what you have here:
def post_comment(course_id,assnmt_id,user_id,comment):
course = canvas.get_course(course_id)
for quiz in course.get_quizzes():
if(quiz.assignment_id == int(assnmt_id)):
# The quiz variable is now my target assignemnt to grade
break
# For each submission, find the user's submission I care about
for sub in quiz.get_submissions():
if(sub.user_id == int(user_id)):
break
# Make my dictionary for the API
q_ids = [str(x.assessment_question_id) for x in sub.get_submission_questions()]
new_dict = {}
# Now put that dict in another dict, because API
# The scores of 10 and 30 are a test for now to see if I can update this user's score with a 100%
for spec_quids,spec_scores in zip(q_ids,[10,10,10,10,30,30]):
new_dict[spec_quids]={"score":spec_scores, "comment":""}
# And the final dict
update_obj = [
{
"attempt": 1,
"fudge_points": 0,
"questions": new_dict
}]
print(update_obj)
update = sub.update_score_and_comments(quiz_submissions=update_obj)
print(update)
The code runs without giving errors now, but I'm not seeing the user's submission updated with the full points on Canvas within SpeedGrader. I verified the user ID and assignment ID are both correct. The returned `update` variable contains all the correct information, as far as I can tell. Is there something I've overlooked?
@chriscas , is this the code you were looking for? Now that I've reviewed this thread with a fresh mind, I realize I totally missed what you were asking for, and I reckon this is it. The other 300 lines of my program are irrelevant to the current discussion, which is what I thought you were asking for.
I got it! I'm not sure what the difference between "assessment_question_id" and "id" are for each question, but I was using the former when I should have been using the latter. I found the discrepancy when comparing the data I was seeing in the debugger to the data on the website in the developer tools of my browser.
I also now realize that the "update_score_and_comments()" is the Python API reference, but "PUT /api/v1/courses/:course_id/quizzes/:quiz_id/submissions/:id" is the Live API reference. Regardless, I can't get either to work. XD
I don't use Python or the Canvas library, but I have done a lot of the Canvas API and quizzes (although not this particular API). Here are some comments based on my experience.
You definitely want to use the real Canvas IDs. The 1, 2, 3, are just examples in the documentation, not a literal 1st question, 2nd question, 3rd question, etc.
You are correct that many of the POST and PUT API endpoints have an parent object in them that does not appear in the GET. You need the quiz_submissions wrapper when using the PUT or POST versions. That explains why the LIVE API rejected it. The Python library you're using may add that for you (I don't use it, so I'm not sure). When I write my code for this stuff, I do a check. If the object I'm sending is missing the parent object, then I add it for the user (which is just me). I cannot speak for the library you're using. It would seem that since I'm using a named parameter, that I would take care of that for the user, but it may just be easier to used named parameters in Python.
I've found that I need to put a [] in the parameter name if your JSON.stringify or library doesn't automatically do it. That is, "quiz_submissions" doesn't work for me, but "quiz_submissions[]" does. That's an open and close bracket at the end to indicate it's an array. You would think that since quiz_submissions is an array that the conversion would add that, but it really depends on the library you're using. Since I'm not familiar with the Python or the library, the calling of the function using quiz_submissions=[mydict] would be a place I would explore. I would make it so that quiz_submissions was the value I was passing, which would mean that it wouldn't need an extra quiz_submissions on the inside. What I would pass would be quiz_submissions[], but I don't know if that can be done in Python. Again, don't take any Python suggestions from me too literally, but I would make sure that there is a quiz_submissions[] parameter in what is sent to Canvas.
The IDE you're using shouldn't matter. JSON uses double quotes, but that code that converts it to JSON would take care of that. I'm assuming that your Python library tells Canvas that content type is application/json for you.
What I do when I reach a point like this (possibly before this) is to print the JSON code that I'm sending to Canvas. I would compare it visually and make sure those brackets are in there. Then I would go to a REST client (Postman seems popular, I still use a different one) and send it and see if it works.
There is nothing special about this request. In other words, it appears to be an object that you are sending to a routine that makes a PUT request. Does your library support a generic PUT statement rather than the sub.update_score_and_comments() function? If the library has any debugging options, you might turn that on and see what is being sent to Canvas.
@James -
Glad you responded. I had thought about tagging you in my original response because of the work you do. I figured that you would have some good insight as what needed to be done.
Ron
The legend himself! I've seen a bunch of your comments as I've tried to learn more about Live API, so thank you for all you do here.
If I could get Live API to work first, then that'd help inform my Python API use. Adding that "quiz_submissions" key to the dictionary that the hash expects still makes Live API return with "missing required key :quiz_submissions". I will update the OP to show the full hash I produce as compared to the example in the Canvas LMS REST Documentation, that way it's not hidden in a comment
So where exactly am I missing the "quiz_submissions" key?
Community helpTo interact with Panda Bot, our automated chatbot, you need to sign up or log in:
Sign inTo interact with Panda Bot, our automated chatbot, you need to sign up or log in:
Sign in