Install any skill in seconds. Free to start, no credit card required.
Get Started Free →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://clawhub.ai/byungkyu/api-gateway). Requires network access and valid Maton API key.
| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | 231% | 0% |
| case-02 | ✗→✓ | ▲ Improved | 378% | 0% |
| case-05 | ✗→✓ | ▲ Improved | 641% | 0% |
| case-06 | ✗→✓ | ▲ Improved | 614% | 0% |
| case-09 | ✗→✓ | ▲ Improved | 354% | 0% |
Access the Google Classroom API with managed OAuth authentication. Manage courses, coursework, students, teachers, announcements, and submissions.
bash# 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
https://gateway.maton.ai/google-classroom/{api-path}The Google Classroom API uses the path pattern:
https://gateway.maton.ai/google-classroom/v1/{resource}All requests require the Maton API key in the Authorization header:
Authorization: Bearer $MATON_API_KEYEnvironment Variable: Set your API key as MATON_API_KEY:
bashexport MATON_API_KEY="YOUR_API_KEY"
Manage your Google Classroom OAuth connections at https://ctrl.maton.ai.
bashpython3 <<'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
bashpython3 <<'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
bashpython3 <<'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:
json{ "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.
bashpython3 <<'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
If you have multiple Google Classroom connections, specify which one to use with the Maton-Connection header:
bashpython3 <<'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.
bashGET /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 (use me for current user)studentId - Filter by student ID (use me for current user)pageSize - Number of results per page (max 100)pageToken - Token for next pageResponse:
json{ "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": "..." }
bashGET /v1/courses/{courseId}
bashPOST /v1/courses Content-Type: application/json { "name": "Course Name", "section": "Section A", "descriptionHeading": "Course Title", "description": "Course description", "ownerId": "me" }
Response:
json{ "id": "825637533405", "name": "Course Name", "section": "Section A", "ownerId": "102753038276005039640", "courseState": "PROVISIONED", "enrollmentCode": "abc123" }
bashPATCH /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.
bashDELETE /v1/courses/{courseId}
Note: Courses must be archived before deletion. To archive, update the course with courseState: "ARCHIVED".
bashGET /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 pagebashGET /v1/courses/{courseId}/courseWork/{courseWorkId}
bashPOST /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 questionStates:
DRAFT - Not visible to studentsPUBLISHED - Visible to studentsbashPATCH /v1/courses/{courseId}/courseWork/{courseWorkId}?updateMask=title,description Content-Type: application/json { "title": "Updated Title", "description": "Updated description" }
bashDELETE /v1/courses/{courseId}/courseWork/{courseWorkId}
bashGET /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 pageNote: Course work must be in PUBLISHED state to list submissions.
Response:
json{ "studentSubmissions": [ { "courseId": "825635865485", "courseWorkId": "825637404958", "id": "Cg4I8ufNwwYQ7tSZgYIB", "userId": "102753038276005039640", "creationTime": "2026-02-14T02:30:00.000Z", "state": "NEW", "alternateLink": "https://classroom.google.com/..." } ] }
bashGET /v1/courses/{courseId}/courseWork/{courseWorkId}/studentSubmissions/{submissionId}
bashPATCH /v1/courses/{courseId}/courseWork/{courseWorkId}/studentSubmissions/{submissionId}?updateMask=assignedGrade,draftGrade Content-Type: application/json { "assignedGrade": 95, "draftGrade": 95 }
bashPOST /v1/courses/{courseId}/courseWork/{courseWorkId}/studentSubmissions/{submissionId}:return Content-Type: application/json {}
bashGET /v1/courses/{courseId}/teachers
Response:
json{ "teachers": [ { "courseId": "825635865485", "userId": "102753038276005039640", "profile": { "id": "102753038276005039640", "name": { "givenName": "John", "familyName": "Doe", "fullName": "John Doe" }, "emailAddress": "john.doe@example.com" } } ] }
bashGET /v1/courses/{courseId}/teachers/{userId}
bashPOST /v1/courses/{courseId}/teachers Content-Type: application/json { "userId": "teacher@example.com" }
bashDELETE /v1/courses/{courseId}/teachers/{userId}
bashGET /v1/courses/{courseId}/students
bashGET /v1/courses/{courseId}/students/{userId}
bashPOST /v1/courses/{courseId}/students Content-Type: application/json { "userId": "student@example.com" }
bashDELETE /v1/courses/{courseId}/students/{userId}
bashGET /v1/courses/{courseId}/announcements GET /v1/courses/{courseId}/announcements?announcementStates=PUBLISHED
bashGET /v1/courses/{courseId}/announcements/{announcementId}
bashPOST /v1/courses/{courseId}/announcements Content-Type: application/json { "text": "Announcement text content", "state": "PUBLISHED" }
States:
DRAFT - Not visible to studentsPUBLISHED - Visible to studentsbashPATCH /v1/courses/{courseId}/announcements/{announcementId}?updateMask=text Content-Type: application/json { "text": "Updated announcement text" }
bashDELETE /v1/courses/{courseId}/announcements/{announcementId}
bashGET /v1/courses/{courseId}/topics
bashGET /v1/courses/{courseId}/topics/{topicId}
bashPOST /v1/courses/{courseId}/topics Content-Type: application/json { "name": "Topic Name" }
bashPATCH /v1/courses/{courseId}/topics/{topicId}?updateMask=name Content-Type: application/json { "name": "Updated Topic Name" }
bashDELETE /v1/courses/{courseId}/topics/{topicId}
bashGET /v1/courses/{courseId}/courseWorkMaterials
bashGET /v1/courses/{courseId}/courseWorkMaterials/{courseWorkMaterialId}
bashGET /v1/invitations?courseId={courseId} GET /v1/invitations?userId=me
Note: Either courseId or userId is required.
bashPOST /v1/invitations Content-Type: application/json { "courseId": "825635865485", "userId": "user@example.com", "role": "STUDENT" }
Roles:
STUDENTTEACHEROWNERbashPOST /v1/invitations/{invitationId}:accept
bashDELETE /v1/invitations/{invitationId}
bashGET /v1/userProfiles/me
Response:
json{ "id": "102753038276005039640", "name": { "givenName": "John", "familyName": "Doe", "fullName": "John Doe" }, "emailAddress": "john.doe@example.com", "permissions": [ { "permission": "CREATE_COURSE" } ], "verifiedTeacher": false }
bashGET /v1/userProfiles/{userId}
bashGET /v1/courses/{courseId}/aliases
The Google Classroom API uses token-based pagination. Responses include a nextPageToken when more results are available.
bashGET /v1/courses?pageSize=10
Response:
json{ "courses": [...], "nextPageToken": "Ci8KLRIrEikKDmIMCLK8v8wGEIDQrsYBCgsI..." }
To get the next page:
bashGET /v1/courses?pageSize=10&pageToken=Ci8KLRIrEikKDmIMCLK8v8wGEIDQrsYBCgsI...
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);
pythonimport 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'])
pythonimport 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())
updateMask query parameter specifying which fields to updatecourseState: "ARCHIVED") before they can be deletedPUBLISHED state to access student submissionsme to refer to the current authenticated user{year, month, day} format; times use {hours, minutes} formatjq or other commands, environment variables like $MATON_API_KEY may not expand correctly in some shell environments| 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 |
Precondition check failed (400)
Permission denied (403)
Other measured skills in the registry, with their headline benchmark lift.