google-classroom
// Google Classroom API integration with managed OAuth. Manage courses, assignments, students, teachers, and announcements. Use this skill when users want to create courses, manage coursework, track student submissions, or post announcements. For other third party apps, use the api-gateway skill (https
Google Classroom
Access the Google Classroom API with managed OAuth authentication. Manage courses, coursework, students, teachers, announcements, and submissions.
Quick Start
# List all courses
python3 <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://gateway.maton.ai/google-classroom/v1/courses')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
Base URL
https://gateway.maton.ai/google-classroom/{api-path}
The Google Classroom API uses the path pattern:
https://gateway.maton.ai/google-classroom/v1/{resource}
Authentication
All requests require the Maton API key in the Authorization header:
Authorization: Bearer $MATON_API_KEY
Environment Variable: Set your API key as MATON_API_KEY:
export MATON_API_KEY="YOUR_API_KEY"
Getting Your API Key
- Sign in or create an account at maton.ai
- Go to maton.ai/settings
- Copy your API key
Connection Management
Manage your Google Classroom OAuth connections at https://ctrl.maton.ai.
List Connections
python3 <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://ctrl.maton.ai/connections?app=google-classroom&status=ACTIVE')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
Create Connection
python3 <<'EOF'
import urllib.request, os, json
data = json.dumps({'app': 'google-classroom'}).encode()
req = urllib.request.Request('https://ctrl.maton.ai/connections', data=data, method='POST')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
req.add_header('Content-Type', 'application/json')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
Get Connection
python3 <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://ctrl.maton.ai/connections/{connection_id}')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
Response:
{
"connection": {
"connection_id": "8efa1361-0e86-40b1-a63b-53a5051f8ac6",
"status": "ACTIVE",
"creation_time": "2026-02-14T00:00:00.000000Z",
"last_updated_time": "2026-02-14T00:00:00.000000Z",
"url": "https://connect.maton.ai/?session_token=...",
"app": "google-classroom",
"metadata": {}
}
}
Open the returned url in a browser to complete OAuth authorization.
Delete Connection
python3 <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://ctrl.maton.ai/connections/{connection_id}', method='DELETE')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
Specifying Connection
If you have multiple Google Classroom connections, specify which one to use with the Maton-Connection header:
python3 <<'EOF'
import urllib.request, os, json
req = urllib.request.Request('https://gateway.maton.ai/google-classroom/v1/courses')
req.add_header('Authorization', f'Bearer {os.environ["MATON_API_KEY"]}')
req.add_header('Maton-Connection', '8efa1361-0e86-40b1-a63b-53a5051f8ac6')
print(json.dumps(json.load(urllib.request.urlopen(req)), indent=2))
EOF
If omitted, the gateway uses the default (oldest) active connection.
API Reference
Courses
List Courses
GET /v1/courses
GET /v1/courses?courseStates=ACTIVE
GET /v1/courses?teacherId=me
GET /v1/courses?studentId=me
GET /v1/courses?pageSize=10
Query Parameters:
courseStates- Filter by state:ACTIVE,ARCHIVED,PROVISIONED,DECLINED,SUSPENDEDteacherId- Filter by teacher ID (usemefor current user)studentId- Filter by student ID (usemefor current user)pageSize- Number of results per page (max 100)pageToken- Token for next page
Response:
{
"courses": [
{
"id": "825635865485",
"name": "Introduction to Programming",
"section": "Section A",
"descriptionHeading": "CS 101",
"description": "Learn the basics of programming",
"ownerId": "102753038276005039640",
"creationTime": "2026-02-14T01:53:58.991Z",
"updateTime": "2026-02-14T01:53:58.991Z",
"enrollmentCode": "3qsua37m",
"courseState": "ACTIVE",
"alternateLink": "https://classroom.google.com/c/ODI1NjM1ODY1NDg1",
"guardiansEnabled": false
}
],
"nextPageToken": "..."
}
Get Course
GET /v1/courses/{courseId}
Create Course
POST /v1/courses
Content-Type: application/json
{
"name": "Course Name",
"section": "Section A",
"descriptionHeading": "Course Title",
"description": "Course description",
"ownerId": "me"
}
Response:
{
"id": "825637533405",
"name": "Course Name",
"section": "Section A",
"ownerId": "102753038276005039640",
"courseState": "PROVISIONED",
"enrollmentCode": "abc123"
}
Update Course
PATCH /v1/courses/{courseId}?updateMask=name,description
Content-Type: application/json
{
"name": "Updated Course Name",
"description": "Updated description"
}
Note: Use updateMask query parameter to specify which fields to update.
Delete Course
DELETE /v1/courses/{courseId}
Note: Courses must be archived before deletion. To archive, update the course with courseState: "ARCHIVED".
Course Work (Assignments)
List Course Work
GET /v1/courses/{courseId}/courseWork
GET /v1/courses/{courseId}/courseWork?courseWorkStates=PUBLISHED
GET /v1/courses/{courseId}/courseWork?orderBy=dueDate
Query Parameters:
courseWorkStates- Filter by state:PUBLISHED,DRAFT,DELETEDorderBy- Sort by:dueDate,updateTimepageSize- Number of results per pagepageToken- Token for next page
Get Course Work
GET /v1/courses/{courseId}/courseWork/{courseWorkId}
Create Course Work
POST /v1/courses/{courseId}/courseWork
Content-Type: application/json
{
"title": "Assignment Title",
"description": "Assignment description",
"workType": "ASSIGNMENT",
"state": "PUBLISHED",
"maxPoints": 100,
"dueDate": {
"year": 2026,
"month": 3,
"day": 15
},
"dueTime": {
"hours": 23,
"minutes": 59
}
}
Work Types:
ASSIGNMENT- Regular assignmentSHORT_ANSWER_QUESTION- Short answer questionMULTIPLE_CHOICE_QUESTION- Multiple choice question
States:
DRAFT- Not visible to studentsPUBLISHED- Visible to students
Update Course Work
PATCH /v1/courses/{courseId}/courseWork/{courseWorkId}?updateMask=title,description
Content-Type: application/json
{
"title": "Updated Title",
"description": "Updated description"
}
Delete Course Work
DELETE /v1/courses/{courseId}/courseWork/{courseWorkId}
Student Submissions
List Student Submissions
GET /v1/courses/{courseId}/courseWork/{courseWorkId}/studentSubmissions
GET /v1/courses/{courseId}/courseWork/{courseWorkId}/studentSubmissions?states=TURNED_IN
Query Parameters:
states- Filter by state:NEW,CREATED,TURNED_IN,RETURNED,RECLAIMED_BY_STUDENTuserId- Filter by student IDpageSize- Number of results per pagepageToken- Token for next page
Note: Course work must be in PUBLISHED state to list submissions.
Response:
{
"studentSubmissions": [
{
"courseId": "825635865485",
"courseWorkId": "825637404958",
"id": "Cg4I8ufNwwYQ7tSZgYIB",
"userId": "102753038276005039640",
"creationTime": "2026-02-14T02:30:00.000Z",
"state": "NEW",
"alternateLink": "https://classroom.google.com/..."
}
]
}
Get Student Submission
GET /v1/courses/{courseId}/courseWork/{courseWorkId}/studentSubmissions/{submissionId}
Grade Submission
PATCH /v1/courses/{courseId}/courseWork/{courseWorkId}/studentSubmissions/{submissionId}?updateMask=assignedGrade,draftGrade
Content-Type: application/json
{
"assignedGrade": 95,
"draftGrade": 95
}
Return Submission
POST /v1/courses/{courseId}/courseWork/{courseWorkId}/studentSubmissions/{submissionId}:return
Content-Type: application/json
{}
Teachers
List Teachers
GET /v1/courses/{courseId}/teachers
Response:
{
"teachers": [
{
"courseId": "825635865485",
"userId": "102753038276005039640",
"profile": {
"id": "102753038276005039640",
"name": {
"givenName": "John",
"familyName": "Doe",
"fullName": "John Doe"
},
"emailAddress": "john.doe@example.com"
}
}
]
}
Get Teacher
GET /v1/courses/{courseId}/teachers/{userId}
Add Teacher
POST /v1/courses/{courseId}/teachers
Content-Type: application/json
{
"userId": "teacher@example.com"
}
Remove Teacher
DELETE /v1/courses/{courseId}/teachers/{userId}
Students
List Students
GET /v1/courses/{courseId}/students
Get Student
GET /v1/courses/{courseId}/students/{userId}
Add Student
POST /v1/courses/{courseId}/students
Content-Type: application/json
{
"userId": "student@example.com"
}
Remove Student
DELETE /v1/courses/{courseId}/students/{userId}
Announcements
List Announcements
GET /v1/courses/{courseId}/announcements
GET /v1/courses/{courseId}/announcements?announcementStates=PUBLISHED
Get Announcement
GET /v1/courses/{courseId}/announcements/{announcementId}
Create Announcement
POST /v1/courses/{courseId}/announcements
Content-Type: application/json
{
"text": "Announcement text content",
"state": "PUBLISHED"
}
States:
DRAFT- Not visible to studentsPUBLISHED- Visible to students
Update Announcement
PATCH /v1/courses/{courseId}/announcements/{announcementId}?updateMask=text
Content-Type: application/json
{
"text": "Updated announcement text"
}
Delete Announcement
DELETE /v1/courses/{courseId}/announcements/{announcementId}
Topics
List Topics
GET /v1/courses/{courseId}/topics
Get Topic
GET /v1/courses/{courseId}/topics/{topicId}
Create Topic
POST /v1/courses/{courseId}/topics
Content-Type: application/json
{
"name": "Topic Name"
}
Update Topic
PATCH /v1/courses/{courseId}/topics/{topicId}?updateMask=name
Content-Type: application/json
{
"name": "Updated Topic Name"
}
Delete Topic
DELETE /v1/courses/{courseId}/topics/{topicId}
Course Work Materials
List Course Work Materials
GET /v1/courses/{courseId}/courseWorkMaterials
Get Course Work Material
GET /v1/courses/{courseId}/courseWorkMaterials/{courseWorkMaterialId}
Invitations
List Invitations
GET /v1/invitations?courseId={courseId}
GET /v1/invitations?userId=me
Note: Either courseId or userId is required.
Create Invitation
POST /v1/invitations
Content-Type: application/json
{
"courseId": "825635865485",
"userId": "user@example.com",
"role": "STUDENT"
}
Roles:
STUDENTTEACHEROWNER
Accept Invitation
POST /v1/invitations/{invitationId}:accept
Delete Invitation
DELETE /v1/invitations/{invitationId}
User Profiles
Get Current User
GET /v1/userProfiles/me
Response:
{
"id": "102753038276005039640",
"name": {
"givenName": "John",
"familyName": "Doe",
"fullName": "John Doe"
},
"emailAddress": "john.doe@example.com",
"permissions": [
{
"permission": "CREATE_COURSE"
}
],
"verifiedTeacher": false
}
Get User Profile
GET /v1/userProfiles/{userId}
Course Aliases
List Course Aliases
GET /v1/courses/{courseId}/aliases
Pagination
The Google Classroom API uses token-based pagination. Responses include a nextPageToken when more results are available.
GET /v1/courses?pageSize=10
Response:
{
"courses": [...],
"nextPageToken": "Ci8KLRIrEikKDmIMCLK8v8wGEIDQrsYBCgsI..."
}
To get the next page:
GET /v1/courses?pageSize=10&pageToken=Ci8KLRIrEikKDmIMCLK8v8wGEIDQrsYBCgsI...
Code Examples
JavaScript
// List all courses
const response = await fetch(
'https://gateway.maton.ai/google-classroom/v1/courses',
{
headers: {
'Authorization': `Bearer ${process.env.MATON_API_KEY}`
}
}
);
const data = await response.json();
console.log(data.courses);
Python
import os
import requests
# List all courses
response = requests.get(
'https://gateway.maton.ai/google-classroom/v1/courses',
headers={'Authorization': f'Bearer {os.environ["MATON_API_KEY"]}'}
)
data = response.json()
print(data['courses'])
Create Assignment Example
import os
import requests
course_id = "825635865485"
# Create an assignment
assignment = {
"title": "Week 1 Homework",
"description": "Complete exercises 1-10",
"workType": "ASSIGNMENT",
"state": "PUBLISHED",
"maxPoints": 100,
"dueDate": {"year": 2026, "month": 3, "day": 15},
"dueTime": {"hours": 23, "minutes": 59}
}
response = requests.post(
f'https://gateway.maton.ai/google-classroom/v1/courses/{course_id}/courseWork',
headers={
'Authorization': f'Bearer {os.environ["MATON_API_KEY"]}',
'Content-Type': 'application/json'
},
json=assignment
)
print(response.json())
Notes
- updateMask Required: PATCH requests require the
updateMaskquery parameter specifying which fields to update - Course Deletion: Courses must be archived (
courseState: "ARCHIVED") before they can be deleted - Student Submissions: Course work must be in
PUBLISHEDstate to access student submissions - User IDs: Use
meto refer to the current authenticated user - Timestamps: Dates use
{year, month, day}format; times use{hours, minutes}format - IMPORTANT: When piping curl output to
jqor other commands, environment variables like$MATON_API_KEYmay not expand correctly in some shell environments
Error Handling
| Status | Meaning |
|---|---|
| 400 | Bad request, invalid argument, or precondition failed |
| 401 | Invalid API key or expired token |
| 403 | Permission denied |
| 404 | Resource not found |
| 409 | Conflict (e.g., user already enrolled) |
| 429 | Rate limited |
| 4xx/5xx | Passthrough error from Google Classroom API |
Common Errors
Precondition check failed (400)
- When deleting a course: Course must be archived first
- When listing submissions: Course work must be published
Permission denied (403)
- User doesn't have required role (teacher/owner) for the operation
- Attempting to access guardian information without proper scopes