Canvas API: Complete Guide with Examples (REST, Authentication, Endpoints)
Formswrite Team
•
February 15, 2026

Canvas API: Complete Guide with Examples
The Canvas REST API is one of the most comprehensive LMS APIs available. It gives you full programmatic access to courses, users, enrollments, assignments, quizzes, grades, and nearly every feature in Canvas LMS.
This guide covers authentication, key endpoints, and working code examples.
API Overview
| Property | Value |
|---|---|
| Base URL | https://yourschool.instructure.com/api/v1 |
| Protocol | REST over HTTPS |
| Authentication | Bearer token (Access Token) |
| Response Format | JSON |
| Pagination | Link header-based |
| Rate Limit | 700 requests per 10 minutes |
Canvas API Authentication
Generate an Access Token
- Log in to Canvas
- Go to Account → Settings
- Scroll to Approved Integrations
- Click "+ New Access Token"
- Enter a purpose (e.g., "API Integration") and optional expiry
- Click "Generate Token"
- Copy the token immediately — it won't be shown again
Use the Token
bash# Authorization header (recommended)
curl -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
https://yourschool.instructure.com/api/v1/courses
Key Canvas API Endpoints
Courses
| Method | Endpoint | Description |
|---|---|---|
GET | /api/v1/courses | List your courses |
GET | /api/v1/courses/:id | Get a specific course |
POST | /api/v1/accounts/:account_id/courses | Create a course |
PUT | /api/v1/courses/:id | Update a course |
DELETE | /api/v1/courses/:id | Delete/conclude a course |
Users
| Method | Endpoint | Description |
|---|---|---|
GET | /api/v1/users/self | Get current user |
GET | /api/v1/accounts/:id/users | List users in account |
POST | /api/v1/accounts/:id/users | Create a user |
PUT | /api/v1/users/:id | Update a user |
Enrollments
| Method | Endpoint | Description |
|---|---|---|
GET | /api/v1/courses/:id/enrollments | List enrollments |
POST | /api/v1/courses/:id/enrollments | Enroll a user |
DELETE | /api/v1/courses/:id/enrollments/:id | Remove enrollment |
Assignments
| Method | Endpoint | Description |
|---|---|---|
GET | /api/v1/courses/:id/assignments | List assignments |
GET | /api/v1/courses/:id/assignments/:id | Get assignment details |
POST | /api/v1/courses/:id/assignments | Create an assignment |
PUT | /api/v1/courses/:id/assignments/:id | Update an assignment |
Quizzes
| Method | Endpoint | Description |
|---|---|---|
GET | /api/v1/courses/:id/quizzes | List quizzes |
GET | /api/v1/courses/:id/quizzes/:id | Get quiz details |
POST | /api/v1/courses/:id/quizzes | Create a quiz |
GET | /api/v1/courses/:id/quizzes/:id/questions | List quiz questions |
POST | /api/v1/courses/:id/quizzes/:id/questions | Add a question |
Submissions & Grades
| Method | Endpoint | Description |
|---|---|---|
GET | /api/v1/courses/:id/assignments/:id/submissions | List submissions |
PUT | /api/v1/courses/:id/assignments/:id/submissions/:user_id | Grade a submission |
Modules
| Method | Endpoint | Description |
|---|---|---|
GET | /api/v1/courses/:id/modules | List modules |
POST | /api/v1/courses/:id/modules | Create a module |
POST | /api/v1/courses/:id/modules/:id/items | Add item to module |
Code Examples
Python: List All Courses
pythonimport requests
CANVAS_URL = "https://yourschool.instructure.com"
TOKEN = "your_access_token"
headers = {"Authorization": f"Bearer {TOKEN}"}
response = requests.get(
f"{CANVAS_URL}/api/v1/courses",
headers=headers,
params={"per_page": 100}
)
courses = response.json()
for course in courses:
print(f"{course['id']}: {course['name']}")
Python: Create a User
pythonresponse = requests.post(
f"{CANVAS_URL}/api/v1/accounts/1/users",
headers=headers,
json={
"user": {
"name": "Jane Smith",
"short_name": "Jane",
"sortable_name": "Smith, Jane"
},
"pseudonym": {
"unique_id": "[email protected]",
"password": "SecurePass123!"
}
}
)
user = response.json()
print(f"Created user: {user['id']} - {user['name']}")
Python: Enroll a User in a Course
pythonresponse = requests.post(
f"{CANVAS_URL}/api/v1/courses/5/enrollments",
headers=headers,
json={
"enrollment": {
"user_id": user["id"],
"type": "StudentEnrollment",
"enrollment_state": "active"
}
}
)
print(f"Enrolled: {response.json()['id']}")
Node.js: List Assignments
javascriptconst CANVAS_URL = 'https://yourschool.instructure.com';
const TOKEN = process.env.CANVAS_TOKEN;
async function getAssignments(courseId) {
const response = await fetch(
`${CANVAS_URL}/api/v1/courses/${courseId}/assignments?per_page=100`,
{ headers: { 'Authorization': `Bearer ${TOKEN}` } }
);
return response.json();
}
const assignments = await getAssignments(5);
assignments.forEach(a => console.log(`${a.id}: ${a.name} (${a.points_possible} pts)`));
cURL: Create a Quiz
bashcurl -X POST "https://yourschool.instructure.com/api/v1/courses/5/quizzes" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"quiz": {
"title": "Chapter 5 Quiz",
"description": "Test your knowledge of Chapter 5",
"quiz_type": "assignment",
"time_limit": 30,
"allowed_attempts": 2,
"scoring_policy": "keep_highest",
"published": false
}
}'
Handling Pagination
Canvas uses Link header pagination. When a response has more pages, the
Link header contains the next page URL.pythondef get_all_pages(url, headers):
results = []
while url:
response = requests.get(url, headers=headers)
results.extend(response.json())
# Parse Link header for next page
links = response.headers.get('Link', '')
url = None
for link in links.split(','):
if 'rel="next"' in link:
url = link.split(';')[0].strip('<> ')
return results
all_users = get_all_pages(
f"{CANVAS_URL}/api/v1/courses/5/users?per_page=100",
headers
)
Rate Limits
Canvas enforces a rate limit of 700 requests per 10 minutes per user. The response headers include:
| Header | Description |
|---|---|
X-Rate-Limit-Remaining | Requests remaining in current window |
X-Request-Cost | Cost of the current request |
If you hit the limit, Canvas returns a
403 Forbidden with a Retry-After header.Creating Quizzes: Canvas API vs Formswrite
Creating quizzes through the Canvas API requires multiple API calls — one to create the quiz, then one per question, with each question requiring answer options. For a 50-question quiz, that's 50+ API calls with complex JSON payloads.
Formswrite offers a much simpler alternative:
| Approach | Steps for 50-Question Quiz |
|---|---|
| Canvas API | 50+ API calls with complex JSON for each question |
| Formswrite | 1 API call → download QTI → import into Canvas |
bash# Formswrite: Convert Google Doc to Canvas QTI
curl -X POST https://api.formswrite.com/api/v1/convert \
-H "Authorization: Bearer FORMSWRITE_TOKEN" \
-H "Content-Type: application/json" \
-d '{"documentId": "DOC_ID", "format": "canvas"}'
The AI reads your Google Doc, extracts all questions, and generates a valid QTI package that Canvas imports directly.
Canvas API Documentation
- Official API docs: canvas.instructure.com/doc/api
- Live API explorer: Your Canvas instance →
/doc/api/live - GitHub: github.com/instructure/canvas-lms
FAQ
How do I get a Canvas API token?
Go to Account → Settings → Approved Integrations → "+ New Access Token". Copy the token immediately.
What is the Canvas API rate limit?
700 requests per 10 minutes per user. Monitor the
X-Rate-Limit-Remaining response header.Can I create quiz questions via the Canvas API?
Yes, using
POST /api/v1/courses/:id/quizzes/:id/questions, but it requires complex JSON for each question. For bulk quiz creation, Formswrite is significantly faster.Is the Canvas API free?
Yes. The API is included with any Canvas LMS instance, including the free teacher version.
Does Canvas support GraphQL?
Yes. Canvas has a GraphQL API alongside the REST API, primarily used for analytics and reporting.
Summary
The Canvas REST API is comprehensive and well-documented. For user and course management, it's excellent. For quiz creation specifically, pair it with Formswrite to convert Google Docs into Canvas QTI packages — saving dozens of API calls per quiz.