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!
Have a question about the Canvas APIs? Have a cool API integration you'd be willing to share? If so, please post here.
Solved! Go to Solution.
Hi @EdithMurillo,
I think you just need a couple tweaks to your url...
First, I'm pretty sure you need to use the account call for this. Second, the parameter name is enrollment_term_id, not just term_id. Third, you need to swap your = and : around.
So in the end, your call should look like:
https://xxx.test.instructure.com/api/v1/accounts/1/courses?enrollment_term_id=sis_term_id:xxx
I hope this works for you!
-Chris
Glen,
I personally don't know a way to do this using the API. You might want to look at Canvas Hosted Data for this problem. You can query for this using Canvas hosted data.
Josh
I did some digging and was unable to find a way to create this type of filter. @jblumberg reference to hosted data is by far the easiest way to do this. If you don't have access to hosted data you can at least narrow the courses you are searching by retrieving all of the courses in an account filtered to the current term (Accounts - Canvas LMS REST API Documentation).
Tip: If you are working with the root account and/or the current term, you can use:
See Object IDs, SIS IDs, and special IDs - Canvas LMS REST API Documentation for some other great substitution options.
Anyone have any luck using the Create External Tools API? I am passing the following data to our test enviroment and receive the below error.
{"externaltool":[{"name":"testTool","privacy_level":"public","consumer_key":"consumer_key","shared_secret":"123456","description":"description","url":"http%3a%2f%2fwww.psu.edu"}]}
ERROR:
{"errors":{"name":[{"attribute":"name","type":"blank","message":"blank"}],"consumer_key":[{"attribute":"consumer_key","type":"blank","message":"blank"}],"shared_secret":[{"attribute":"shared_secret","type":"blank","message":"blank"}],"url":[{"attribute":"url","type":"Either the url or domain should be set.","message":"Either the url or domain should be set."}],"domain":[{"attribute":"domain","type":"Either the url or domain should be set.","message":"Either the url or domain should be set."}]}}
I was able to create a course navigation link without any issues, so the
endpoint seems to be working. I was using my /docs/api/live page. For a
one-off creation process, that seems fairly foolproof.
I'm a little confused about the data you are sending. I'm pasting in the
return I got from my successful create. The only fields I posted were url,
consumer_key, consumer_secret, name, privacy_level, and the
course_navigation stuff. The rest is all null or default values. I included
the course nav fields because that seemed like the easiest type to test for
once created. I did visit the course, and sure enough the nav item is
there.
Anyway, I POSTed all that to
https://udel.test.instructure.com:443/api/v1/courses/[courseid]/external_tools.
So, I'm thinking that your data has an extra level to it, which I don't
understand. You've got your name, etc nested inside an externaltool array,
whereas I'm just posting to the externaltools endpoint.
HTH
{
"id": 80521,
"domain": null,
"url": "https://apps.ats.udel.edu/canvas/index.html",
"consumer_key": "none",
"name": "Test",
"description": null,
"created_at": "2015-12-09T19:31:56Z",
"updated_at": "2015-12-09T19:31:56Z",
"privacy_level": "anonymous",
"custom_fields": null,
"workflow_state": "anonymous",
"vendor_help_link": null,
"user_navigation": null,
"course_navigation": {
"default": "true",
"enabled": true,
"text": "Our Apps",
"url": "https://apps.ats.udel.edu/canvas/index.html",
"visibility": "members",
"label": "Our Apps",
"selection_width": 800,
"selection_height": 400
},
"account_navigation": null,
"resource_selection": null,
"editor_button": null,
"homework_submission": null,
"migration_selection": null,
"course_home_sub_navigation": null,
"course_settings_sub_navigation": null,
"global_navigation": null,
"assignment_menu": null,
"file_menu": null,
"discussion_topic_menu": null,
"module_menu": null,
"quiz_menu": null,
"wiki_page_menu": null,
"tool_configuration": null,
"link_selection": null,
"assignment_selection": null,
"post_grades": null,
"not_selectable": true
}
Response Code
Becky Kinney
Academic Technology Services
Project Blog <http://sites.udel.edu/bkinney/>
On Wed, Dec 9, 2015 at 11:36 AM, wbk2@psu.edu <instructure@jiveon.com>
Thanks, I figured it all out. You were right it was the way I was passing the parameters into the query.
Let me set the stage. I am working with the Canvas API
I am making an xhr to a server that sends back 100 results at a time. It returns a 'Link' in the HEADER that looks like this: Link: <https://<canvas>/api/v1/courses/:id/discussion_topics.json?opaqueA>; rel="current", <https://<canvas>/api/v1/courses/:id/discussion_topics.json?opaqueB>; rel="next", <https://<canvas>/api/v1/courses/:id/discussion_topics.json?opaqueC>; rel="first", <https://<canvas>/api/v1/courses/:id/discussion_topics.json?opaqueD>; rel="last"
Question 1: What is the best way to get the url with the rel="next"? Currently I am doing this (It works but I feel like there is a better way):
function parseHeaderForRel(xhr, rel) {
return xhr.getResponseHeader('Link')
.split(',')
.reduce(function(pv, cv, i) {
if (cv.indexOf('rel="next"') > -1) {
pv.push(cv.substring(cv.lastIndexOf('<') + 1, cv.lastIndexOf('>')));
}
return pv;
}, []);
}
Question 2: How do I continue to make calls until the xhr response header no longer contains rel='next'? Also note that the rel="last" is not ALWAYS available, so I don't want to rely on it. I am currently working with something that looks like this.
function getCoursesInAccount(accountId, data, url) {
data = data || [];
url = url || '/api/v1/accounts/' + accountId + '/courses?per_page=100';
return $.get(url).done(function(res, textStatus, jqXHR){
var url = parseHeaderForRel(jqXHR, 'next')[0];
if(url){
data.push(res);
getCoursesInAccount(accountId, data, url)
} else {
data.push(res);
console.log(data);
return data;
}
})
}
The console.log will always log the correct data, but I can't get it out of the function and I want this to be a service I can call upon in other functions. I have played with just returning the entire $.get() and using 'then' but I don't know how many 'then's I will need.
Any direction is greatly appreciated!
@kenneth_larsen - I heard you know things.
Kenneth knows...
Hmmmm... that is a good one. All of the API requests that I make that have dealt with paged results have used PHP to make all of the API calls and process the "next" header links. I will then use ajax requests to ping the PHP page so that I am not passing an OAuth token in the JavaScript. I can foresee using this type of functionality in some of the tools I am working on so I will have to do some digging and see what I can figure out.
Thanks Kenneth. I am honing in a solution that is going to work for us. I will definitely share once we finalize.
-- And my bad on the delayed response! EOY launches, holidays and then Codemash have kept me very busy.
Your xhr request is still returning json right? If so you can get the 'next' url form response.links['next']['url'].
Here is a pagination script I've used that originated from the github user kajiagga. It is in python, but it shows the logic of stepping through the process. It grabs the 'next' url until the json return is empty.
groupOutcomeList = []
all_done = False
url = 'https://%s/api/v1/%s/outcome_groups?per_page=100' % (domain,apiType)
while not all_done:
response = requests.get(url,headers=get_headers())
if not response.json():
return
else:
for s in response.json():
groupOutcomeList.append(s)
if 'next' in response.links:
url = response.links['next']['url']
else:
all_done = True
return groupOutcomeList
I wish that were the case!
xhr.getResponseHeader('Link') returns a string that looks like this:
<https://canvas.u.edu/api/v1/accounts/1/courses?page=1&per_page=10>; rel="current",
<https://canvas.u.edu/api/v1/accounts/1/courses?page=2&per_page=10>; rel="next",
<https://canvas.u.edu/api/v1/accounts/1/courses?page=1&per_page=10>; rel="first",
<https://canvas.u.edu/api/v1/accounts/1/courses?page=27&per_page=10>; rel="last"
Is is possible to add the below line into your Ajax?
xhr.responseType = 'json'
I wish it were that easy, and I hope I am missing something, but from everything I have seen and tried, I can't get the header to come back in JSON. It would be awesome for someone to prove me wrong though!
I'm probably not the one to prove you wrong, but I found a blog post that better describes the xhr.responseType='json' in case it is useful.
Loading JSON-formatted data with Ajax and xhr.responseType='json' · Mathias Bynens
Just posted about a new LTI tool that we developed at Eastern Washington University that displays visualizations of Canvas discussions.
This looks awesome, Matt. Thanks for sharing. Have you considered putting this out at the eduappscenter as a tool you would host? I realize that it requires API access, and that that presents a bit of a dilemma. Is that why you are offering this as a self-hosted solution, or are there other reasons? I ask because I have a couple of API-dependent apps of my own that I'd love to make public, but I've never quite had the nerve to do it. The apps I want to share would not require us to store any institutional data beyond the tokens themselves, but given that tokens can't be scoped, storing one is kind of a big deal.
While the Threadz app doesn't store any api tokens, it could. Because of that, we didn't want this tool to be overlooked due to any FERPA security issues from others, so by making it a self-hosted app, many security issues get taken off the table.
Plus, we weren't sure how wide spread the interest might be so we didn't want to go through the process of committing university resources to an outside project. We'd be open to reconsidering this however.
Alright everyone, I'm trying to decide on a topic for 2016 InstructureCon. Chime in and vote now at What should Kenneth Larsen present on at InstructureCon 2016?
I am attempting to add an assignment that uses an External Tool
The external tool has been created and tested to ensure it is functional. I can create assignment and module items in the GUI, but am having difficulty creating an assignment with the API that uses an External Tool.
Below is the CURL command that I am using
curl 'https://ecpi.instructure.com/api/v1/courses/896/assignments'
-d 'assignment[name]=ECPI Post Test'
-d 'assignment[submission_types]=external_tool'
-d 'assignment[external_tool_tag_attributes]=url:https://test.ecpi.net/quiz/connect_post.php,new_tab:true'
-d 'assignment[points_possible]=100'
-d 'assignment[grading_type]=points'
-d 'assignment[assignment_group_id]=4083'
-H 'Authorization: Bearer 4534~ETXXXXXXXX'
(yes, the token has been changed in the code above)
The output I get is an error message that give no help
{"errors":[{"message":"An error occurred.","error_code":"internal_server_error"}],"error_report_id":46141}
Does anyone have any idea as to why the command fails with an error?
Just a stab in the dark since I use PHP to handle my curl events, but try changing
-d 'assignment[external_tool_tag_attributes]=url:https://test.ecpi.net/quiz/connect_post.php',new_tab:true
To
-d 'assignment[external_tool_tag_attributes][url]=https://test.ecpi.net/quiz/connect_post.php'
-d 'assignment[external_tool_tag_attributes][new_tab]=true'
I also use PHP. I changed command as you recommended. SUCCESS
-d 'assignment[external_tool_tag_attributes][url]=https://test.ecpi.net/quiz/connect_post.php'
-d 'assignment[external_tool_tag_attributes][new_tab]=true'
I did not gather the above form the API documentation below
Thanks for you help
Assignments - Canvas LMS REST API Documentation
assignment[external_tool_tag_attributes]
Hash of attributes if submission_types is [“external_tool”] Example:
external_tool_tag_attributes: { %r/ url to the external tool
url: "http://instructure.com",
%r/ create a new tab for the module, defaults to false. n
ew_tab: false }
I think I discovered that through trial and error on another API some time back. Glad it worked.
I generated a ticket for Canvas Support on this issue. Canvas Support indicated the engineers would change the API documentation
The API documentation says that a course can be in different states. Does anyone know what the definition is for these different states? What is the difference between "claimed" and "available"? Or to be more precise, what does the "claimed" state mean, is it simply "unpublished"?
Canvas LMS REST API Documentation
Thanks for any clarity you can provide.
Matt
Looking thru the source... makes me think that the 'claimed' status is used to indicate a course that has just been undeleted.
Hey,
I am new to use Canvas API. I am trying now to upload file into the user's file section via API. By reading the uploading file document described @ https://canvas.instructure.com/doc/api/file.file_uploads.html. I tested to upload the file via POST using php cURL below , but looks like the name of the file wasn't pass in. Any one have idea why the file name got failed to post, or I misunderstood something here? Thanks in advance!
//CURL POST File
<?php
//path to test user's file section
$apiUrl = 'users/' . $testuserid .'/files' ;
$uploadfolder ='Upload testfolder'
//test data information
$content_type = "text/plain";
$name = 'test.txt';
$size=filesize("./test.txt");
$parent_folder_path =$uploadfolder; //new folder
$postdata = 'content_type=' . $content_type .
'&name=' . urlencode($name) .
'&size=' . $size .
'&parent_folder_path=' . $parent_folder_path;
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, TESTINSTANCE . '/api/v1/' . $url); //api target post url
curl_setopt ($ch, CURLOPT_HTTPHEADER, AUTHINFO);//Authorization
curl_setopt($ch, CURLOPT_POST,1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $postdata); //file information
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
$response = curl_exec($ch);
curl_close($ch);
echo $response;
?>
//POSTDATA and RESPONSE DUMP
string 'name=words.txt&size=475954&content_type=text/plain&parent_folder_path=Upload test folder' (length=91)
array (size=3)
'upload_url' => string 'https://instructure-uploads.s3.amazonaws.com/' (length=45)
'upload_params' =>
array (size=8)
'AWSAccessKeyId' => string 'XXXXXXXXX' (length=20)
'Filename' => string '' (length=0)
'key' => string 'XXXXXXXXXXXXX' (length=54)
'acl' => string 'private' (length=7)
'Policy' => string 'XXXXXXXXXXXXX' (length=564)
'Signature' => string 'XXXXXXXXXXXXX' (length=28)
'success_action_redirect' => string 'XXXXXXXXXXXXXXX' (length=112)
'content-type' => string 'text/plain' (length=10)
'file_param' => string 'file' (length=4)
@rlzhang , I am not seeing anything that is really sticking out at me, but I upload files my server and upload via URL. On a note not related to the name, did you replace the upload folder name in your pasted code? If not, I would recommend using something like '/test_uploads' or some other variation without using spaces.
add . "/" to $parent_folder_path
'parent_folder_path' => "/".$parent_folder_path
We want to add rubrics to each of out discussions. I looked through the API documentation and cannot find an endpoint for Rubrics, but can find the RubricRating object and RubricCriteria objects references in the Assignment endpoint documentation Assignments - Canvas LMS REST API Documentation
Does anyone know how to link preexisting rubric to a graded discussion?
Hi Kenneth,
Thanks for your reply. Yes. The original filedata I was testing on POST was string 'name=words.txt&size=475954&content_type=text/plain&parent_folder_path=Eval Reports/Fall2015'.
I simplified the note on the code paste above. I did just tried the suggestion you gave, changed the folder name without space, but still didn't got the file name passed in.
Thanks,
Thanks Martin. Actually the folder looks like works correctly for me, Since from UI, I could see the folder was created after the POST request was completed (no matter there's a space in the folder name or not). The issue is the name of the file wasn't passed in via my POST.
Thanks,
Hi all!
Is anyone using the CalendarEvents API to synchronize a Canvas course calendar with an external source such as a Curriculum DB?
We have a curriculum database with information on all sessions (type, title, description) and offerings (location, time, section, and instructors). I'm considering the best way to handle this as we migrate from Sakai... Right now, here is my thought:
INITIAL LOAD - Course events are created using the API and the internal ID for the Curriculum Database gets appended to the event description for all events.
MODIFICATIONS - When someone changes the details of one of these events in the CurriculumDB, it uses the API to update that event in Canvas. If the event was getting deleted, delete it in Canvas
NIGHTLY - Get the list of all events for the course and then iterate, looking for the internalID in the description. Compare the event coming from Canvas with what is currently in the CurriculumDB. If different, update. If the same, do nothing. If it doesn't exist in the CurriculmDB, delete it. If it doesn't exist in Canvas, create it.
Ok -- now the 2nd question -- is anyone doing anything to PREVENT the teachers from editing these type of 'provided' calendar events? We don't want them to think that they can just change the date/time/location of an event... But -- at the same time, we DO want them to be able to create/edit/delete ad-hoc calendar events.
Thanks All!
Chris
Hi,
I am new to all of this. Any help you can provide would be greatly appreciated. I am currently working with Postman and I'm trying to get a list of all courses using the following call function:
GET /api/v1/courses
It is only pulling courses that are assigned to my account. I would like to get a list of all of the courses within the LMS. Can you tell me how I can call for a list of all courses?
Thank you Martin!
Is there was a way as an admin to pull all courses? Not just the courses in my account. I'm looking to pull a list of all courses within the LMS.
I would like to pull a list of all courses within a specific term.
The following GET function only pulls courses that are linked to a specific account.
With the list courses in an account API, you can include the enrollment_term_id to view only courses for a given term. Every Canvas course is linked to a specific account. If you put in the root account and don't limit it by sub accounts, it will list all courses.
Thank you Kenneth! We used the root account ID and was able to pull more courses. We are using the get function in Postman, but it's not listing every course. Is there a limit to the number of records that display after we run the function? It looks like only 10 courses are showing up at a time.
Canvas API requests return paged results (see Pagination in the API). You can increase the number of results per page by adding a per_page=## to your request, but for a list of all courses, you will need to loop through the pages and I am not sure how/if you can do that with Postman. You can manually move to the next page by adding page=# to your results, but that can still be tedious.
Thank you so much Kenneth!
I have one more question. Is there way for us to export these results into a readable report through postman?
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