The Assignment Grader API allows you to submit student work (text or uploaded materials), define grading criteria via a rubric, and receive AI-generated scores and feedback for every criterion. It supports inline rubrics, reusable rubric templates, assignment content for source-based grading, and aggregate educator reports.
Key Features
- Custom Rubrics: Define grading criteria with specific point values
- Flexible Input: Grade from uploaded materials or direct text
- Consistent Grading: AI ensures uniform application of rubric criteria
- Objective Grading: Get numerical grades based on your rubric
- Assignment Content: Provide source material (reading passages, case studies, prompts) that the AI uses as the authoritative reference when grading
- User Tracking: Optional user ID for tracking grading history
- Assignment Groups: Group submissions by assignment ID
- Student Identification: Track submissions by student email or ID
- Rubric Templates: Save and reuse common rubrics
Base URL
All endpoints are relative to your API prefix (defaults to api/v1):
https://<your-host>/api/v1/assignment-grader
For local development:
http://localhost:3001/api/v1/assignment-grader
Authentication
Every endpoint is protected by FlexibleAuthGuard, which accepts either:
| Method | Header | Notes |
|---|---|---|
| API key | x-api-key: <YOUR_API_KEY> |
Server-to-server. Resolves to the owning organization. |
| JWT | Authorization: Bearer <JWT> |
User session. Resolves to both organization and user. |
If neither is present the request returns 401 Unauthorized.
Common Headers
| Header | Required | Value |
|---|---|---|
Content-Type |
On POST requests |
application/json |
x-api-key |
Yes (or Authorization) |
Your API key |
Authorization |
Yes (or x-api-key) |
Bearer <jwt> |
Endpoint Summary
| # | Method | Path | Purpose |
|---|---|---|---|
| 1 | POST |
/assignment-grader/create |
Grade a new assignment |
| 2 | GET |
/assignment-grader/get |
List all graded assignments for the org |
| 3 | GET |
/assignment-grader/get/:id |
Fetch one graded assignment by ID |
| 4 | DELETE |
/assignment-grader/delete/:id |
Delete a graded assignment |
| 5 | POST |
/assignment-grader/rubric-templates |
Create a rubric template |
| 6 | GET |
/assignment-grader/rubric-templates |
List all rubric templates |
| 7 | GET |
/assignment-grader/rubric-templates/:id |
Fetch one rubric template |
| 8 | DELETE |
/assignment-grader/rubric-templates/:id |
Delete a rubric template |
| 9 | GET |
/assignment-grader/educator-report/:assignmentId |
Aggregate report across all submissions for an assignmentId |
Creating a Rubric Definition
Before grading assignments, you need to define a rubric with specific criteria. Each criterion has:
- pointsPossible: The maximum points for this criterion (required)
- title: The name of the criterion (required)
- description: Detailed explanation of what's being evaluated (optional but recommended)
JavaScript
// Define a comprehensive rubric
const rubricDefinition = {
criteria: [
{
pointsPossible: 25,
title: "Thesis Statement",
description: "Clear, arguable thesis that addresses the prompt"
},
{
pointsPossible: 30,
title: "Evidence and Analysis",
description: "Strong evidence with thorough analysis"
},
{
pointsPossible: 25,
title: "Organization",
description: "Logical structure with smooth transitions"
},
{
pointsPossible: 20,
title: "Writing Mechanics",
description: "Grammar, spelling, and citation format"
}
],
assignmentContent: "Optional — source material or reading passage" // Optional
};
// Total points: 100
Python
# Define a comprehensive rubric
rubric_definition = {
"criteria": [
{
"pointsPossible": 25,
"title": "Thesis Statement",
"description": "Clear, arguable thesis that addresses the prompt"
},
{
"pointsPossible": 30,
"title": "Evidence and Analysis",
"description": "Strong evidence with thorough analysis"
},
{
"pointsPossible": 25,
"title": "Organization",
"description": "Logical structure with smooth transitions"
},
{
"pointsPossible": 20,
"title": "Writing Mechanics",
"description": "Grammar, spelling, and citation format"
}
],
"assignmentContent": "Optional — source material or reading passage" # Optional
}
# Total points: 100
Java
import com.studyfetch.javasdk.models.v1.assignmentgrader.AssignmentGraderCreateParams;
import com.studyfetch.javasdk.models.v1.assignmentgrader.rubrictemplates.RubricCriterion;
// Define a comprehensive rubric
AssignmentGraderCreateParams.Rubric rubricDefinition = AssignmentGraderCreateParams.Rubric.builder()
.addCriterion(RubricCriterion.builder()
.pointsPossible(25.0)
.title("Thesis Statement")
.description("Clear, arguable thesis that addresses the prompt")
.build())
.addCriterion(RubricCriterion.builder()
.pointsPossible(30.0)
.title("Evidence and Analysis")
.description("Strong evidence with thorough analysis")
.build())
.addCriterion(RubricCriterion.builder()
.pointsPossible(25.0)
.title("Organization")
.description("Logical structure with smooth transitions")
.build())
.addCriterion(RubricCriterion.builder()
.pointsPossible(20.0)
.title("Writing Mechanics")
.description("Grammar, spelling, and citation format")
.build())
.assignmentContent("Optional — source material or reading passage") // Optional
.build();
// Total points: 100
C#
using StudyfetchSDK.Models.V1.AssignmentGrader.AssignmentGraderCreateParamsProperties;
using StudyfetchSDK.Models.V1.AssignmentGrader.RubricTemplates;
using System.Collections.Generic;
// Define a comprehensive rubric
var rubricDefinition = new Rubric()
{
Criteria = new List<RubricCriterion>
{
new RubricCriterion()
{
PointsPossible = 25,
Title = "Thesis Statement",
Description = "Clear, arguable thesis that addresses the prompt"
},
new RubricCriterion()
{
PointsPossible = 30,
Title = "Evidence and Analysis",
Description = "Strong evidence with thorough analysis"
},
new RubricCriterion()
{
PointsPossible = 25,
Title = "Organization",
Description = "Logical structure with smooth transitions"
},
new RubricCriterion()
{
PointsPossible = 20,
Title = "Writing Mechanics",
Description = "Grammar, spelling, and citation format"
}
},
AssignmentContent = "Optional — source material or reading passage" // Optional
};
// Total points: 100
Create Assignment Grader
POST /assignment-grader/create
Grade a new assignment by providing the rubric and content to grade. Returns the saved graded assignment with AI-generated scores and feedback.
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
title |
string |
Yes | Title of the assignment. |
materialId |
string |
Conditional* | ID of an uploaded material (PDF, doc, EPUB, etc.) to grade. |
textToGrade |
string |
Conditional* | Raw text content to grade. |
rubric |
Rubric |
Conditional** | Inline rubric. |
rubricTemplateId |
string |
Conditional** | ID of a saved rubric template to use. |
assignmentContent |
string |
No | Source material / passage / instructions students were given. The AI uses this as the authoritative reference. See Assignment Content. |
assignmentId |
string |
No | Tag to group multiple submissions under one logical assignment (used by educator reports). |
studentIdentifier |
string |
No | Student email or ID. |
userId |
string |
No | User ID for usage tracking (defaults to authenticated user, or anonymous). |
model |
string |
No | AI model key (defaults to gpt-4o-mini-2024-07-18). |
* You must provide exactly one of
materialIdortextToGrade. Providing both or neither returns400 Bad Request.** You must provide at least one of
rubricorrubricTemplateId. An inlinerubricmust contain at least one criterion.
Example Request (curl)
curl -X POST https://api.yourapp.com/api/v1/assignment-grader/create \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"title": "Pop Quiz — Chapter 3",
"textToGrade": "1. Photosynthesis turns sunlight into sugar.\n2. Plants give off oxygen.",
"assignmentContent": "Chapter 3 covers photosynthesis, the process by which plants use sunlight, water, and carbon dioxide to produce glucose and oxygen...",
"rubric": {
"criteria": [
{ "title": "Defines photosynthesis", "description": "Mentions sunlight + sugar/glucose", "pointsPossible": 5 },
{ "title": "Mentions oxygen", "description": "Identifies oxygen as a byproduct", "pointsPossible": 5 }
]
},
"assignmentId": "BIO-CH3-QUIZ",
"studentIdentifier": "[email protected]"
}'
Response, 201 Created
{
"success": true,
"gradedAssignment": {
"_id": "665a1c0e8f3b1a0012abcdef",
"title": "Pop Quiz — Chapter 3",
"grade": 90,
"rubric": {
"attemptFeedback": "Strong response. You captured the core concepts...",
"criteria": [
{
"title": "Defines photosynthesis",
"description": "Mentions sunlight + sugar/glucose",
"pointsAwarded": 5,
"pointsPossible": 5,
"feedback": "Correct — you mentioned sunlight and sugar."
},
{
"title": "Mentions oxygen",
"description": "Identifies oxygen as a byproduct",
"pointsAwarded": 4,
"pointsPossible": 5,
"feedback": "Good. For full marks, mention that oxygen is released as a byproduct of the reaction."
}
]
},
"organizationId": "664f...",
"userId": "665b...",
"textToGrade": "1. Photosynthesis turns sunlight...",
"assignmentContent": "Chapter 3 covers photosynthesis...",
"assignmentId": "BIO-CH3-QUIZ",
"studentIdentifier": "[email protected]",
"createdAt": "2026-04-28T07:14:00.000Z",
"updatedAt": "2026-04-28T07:14:00.000Z"
}
}
SDK Examples
JavaScript
import StudyfetchSDK from '@studyfetch/sdk';
const client = new StudyfetchSDK({
apiKey: 'your-api-key',
baseURL: 'https://studyfetchapi.com',
});
// Use the rubric definition from above
const params = {
title: "Essay on Climate Change",
textToGrade: "Student essay content here...",
rubric: rubricDefinition, // Optional: Custom rubric definition
rubricTemplateId: "template-123", // Optional: Use existing rubric template
assignmentContent: "The source material or reading passage students were given...", // Optional: Reference material for grading
assignmentId: "assign-456", // Optional: Group submissions
studentIdentifier: "[[email protected]](mailto:[email protected])", // Optional: Student email or ID
userId: "student123", // Optional: For tracking
model: "gpt-4.1-2025-04-14" // Optional: AI model to use
};
const result = await client.v1.assignmentGrader.create(params);
console.log(`Success: ${result.success}`);
console.log(`Grade: ${result.gradedAssignment?.grade || 0}/100`);
console.log(`Assignment ID: ${result.gradedAssignment?._id}`);
Python
from studyfetch_sdk import StudyfetchSDK
client = StudyfetchSDK(
api_key="your-api-key",
base_url="https://studyfetchapi.com",
)
# Use the rubric definition from above
params = {
"title": "Essay on Climate Change",
"textToGrade": "Student essay content here...",
"rubric": rubric_definition, # Optional: Custom rubric definition
"rubricTemplateId": "template-123", # Optional: Use existing rubric template
"assignmentContent": "The source material or reading passage students were given...", # Optional: Reference material for grading
"assignmentId": "assign-456", # Optional: Group submissions
"studentIdentifier": "[email protected]", # Optional: Student email or ID
"userId": "student123", # Optional: For tracking
"model": "gpt-4.1-2025-04-14" # Optional: AI model to use
}
result = client.v1.assignment_grader.create(params)
print(f"Grade: {result.grade}/100")
Java
import com.studyfetch.javasdk.client.StudyfetchSdkClient;
import com.studyfetch.javasdk.client.okhttp.StudyfetchSdkOkHttpClient;
import com.studyfetch.javasdk.models.v1.assignmentgrader.AssignmentGraderCreateParams;
import com.studyfetch.javasdk.models.v1.assignmentgrader.AssignmentGraderCreateResponse;
StudyfetchSdkClient client = StudyfetchSdkOkHttpClient.builder()
.apiKey("your-api-key")
.baseUrl("https://studyfetchapi.com")
.build();
// Use the rubric definition from above
AssignmentGraderCreateParams params = AssignmentGraderCreateParams.builder()
.title("Essay on Climate Change")
.textToGrade("Student essay content here...")
.rubric(rubricDefinition) // Optional: Custom rubric definition
.rubricTemplateId("template-123") // Optional: Use existing rubric template
.assignmentContent("The source material or reading passage students were given...") // Optional: Reference material for grading
.assignmentId("assign-456") // Optional: Group submissions
.studentIdentifier("[email protected]") // Optional: Student email or ID
.userId("student123") // Optional: For tracking
.model("gpt-4.1-2025-04-14") // Optional: AI model to use
.build();
AssignmentGraderCreateResponse result = client.v1().assignmentGrader().create(params);
System.out.println("Success: " + result.success());
if (result.gradedAssignment().isPresent()) {
System.out.println("Grade: " + result.gradedAssignment().get().grade() + "/100");
System.out.println("Assignment ID: " + result.gradedAssignment().get()._id());
}
C#
using StudyfetchSDK;
using StudyfetchSDK.Models.V1.AssignmentGrader;
using System;
using System.Threading.Tasks;
public class CreateAssignmentGrader
{
public static async Task GradeAssignment()
{
var client = new StudyfetchSDKClient()
{
APIKey = Environment.GetEnvironmentVariable("STUDYFETCH_API_KEY"),
BaseUrl = new Uri("https://studyfetchapi.com")
};
// Use the rubric definition from above
var params_ = new AssignmentGraderCreateParams()
{
Title = "Essay on Climate Change",
TextToGrade = "Student essay content here...",
Rubric = rubricDefinition, // Optional: Custom rubric definition
RubricTemplateID = "template-123", // Optional: Use existing rubric template
AssignmentContent = "The source material or reading passage students were given...", // Optional: Reference material for grading
AssignmentID = "assign-456", // Optional: Group submissions
StudentIdentifier = "[email protected]", // Optional: Student email or ID
UserID = "student123", // Optional: For tracking
Model = "gpt-4.1-2025-04-14" // Optional: AI model to use
};
var result = await client.V1.AssignmentGrader.Create(params_);
Console.WriteLine($"Success: {result.Success}");
Console.WriteLine($"Grade: {result.GradedAssignment?.Grade ?? 0}/100");
Console.WriteLine($"Assignment ID: {result.GradedAssignment?._ID}");
}
}
Get All Assignment Graders
GET /assignment-grader/get
Returns every graded assignment for the authenticated organization.
Example Request (curl)
curl -X GET https://api.yourapp.com/api/v1/assignment-grader/get \
-H "x-api-key: YOUR_API_KEY"
Response, 200 OK
An array of AssignmentGrader objects.
SDK Examples
JavaScript
const graders = await client.v1.assignmentGrader.getAll();
graders.forEach(grader => {
console.log(`ID: ${grader._id}`);
console.log(`Title: ${grader.title}`);
console.log(`Grade: ${grader.grade}`);
console.log(`Created: ${grader.createdAt}`);
console.log(`Assignment ID: ${grader.assignmentId}`);
console.log(`Student: ${grader.studentIdentifier}`);
});
Python
graders = client.v1.assignment_grader.get_all()
for grader in graders:
print(f"Title: {grader.title}")
print(f"Grade: {grader.grade}")
print(f"Created: {grader.created_at}")
print(f"Assignment ID: {grader.assignment_id}")
print(f"Student: {grader.student_identifier}")
Java
import com.studyfetch.javasdk.models.v1.assignmentgrader.AssignmentGraderResponse;
List<AssignmentGraderResponse> graders = client.v1().assignmentGrader().getAll();
for (AssignmentGraderResponse grader : graders) {
System.out.println("ID: " + grader._id());
System.out.println("Title: " + grader.title());
System.out.println("Grade: " + grader.grade());
System.out.println("Created: " + grader.createdAt());
System.out.println("Assignment ID: " + grader.assignmentId());
System.out.println("Student: " + grader.studentIdentifier());
}
C#
using StudyfetchSDK;
using StudyfetchSDK.Models.V1.AssignmentGrader;
using System;
using System.Threading.Tasks;
public class GetAllGraders
{
public static async Task GetAll()
{
var client = new StudyfetchSDKClient()
{
APIKey = Environment.GetEnvironmentVariable("STUDYFETCH_API_KEY"),
BaseUrl = new Uri("https://studyfetchapi.com")
};
var graders = await client.V1.AssignmentGrader.GetAll(new());
foreach (var grader in graders)
{
Console.WriteLine($"ID: {grader._ID}");
Console.WriteLine($"Title: {grader.Title}");
Console.WriteLine($"Grade: {grader.Grade}");
Console.WriteLine($"Created: {grader.CreatedAt}");
Console.WriteLine($"Assignment ID: {grader.AssignmentID}");
Console.WriteLine($"Student: {grader.StudentIdentifier}");
}
}
}
Get Assignment Grader by ID
GET /assignment-grader/get/:id
Retrieve a specific graded assignment by its ID.
| Path param | Description |
|---|---|
id |
Assignment grader document ID (_id). |
Example Request (curl)
curl -X GET https://api.yourapp.com/api/v1/assignment-grader/get/665a1c0e8f3b1a0012abcdef \
-H "x-api-key: YOUR_API_KEY"
Response, 200 OK
A single AssignmentGrader object. Returns 404 Not Found if no assignment matches the ID.
SDK Examples
JavaScript
const graderId = "grader_123456";
const grader = await client.v1.assignmentGrader.getByID(graderId);
console.log(`ID: ${grader._id}`);
console.log(`Assignment: ${grader.title}`);
console.log(`Grade: ${grader.grade}/100`);
console.log(`Student: ${grader.studentIdentifier}`);
console.log(`Assignment ID: ${grader.assignmentId}`);
Python
grader_id = "grader_123456"
grader = client.v1.assignment_grader.get_by_id(grader_id)
print(f"Assignment: {grader.title}")
print(f"Grade: {grader.grade}/100")
print(f"Student: {grader.student_identifier}")
print(f"Assignment ID: {grader.assignment_id}")
Java
import com.studyfetch.javasdk.models.v1.assignmentgrader.AssignmentGraderGetByIdParams;
import com.studyfetch.javasdk.models.v1.assignmentgrader.AssignmentGraderResponse;
String graderId = "grader_123456";
AssignmentGraderResponse grader = client.v1().assignmentGrader().getById(
AssignmentGraderGetByIdParams.builder()
.id(graderId)
.build()
);
System.out.println("ID: " + grader._id());
System.out.println("Assignment: " + grader.title());
System.out.println("Grade: " + grader.grade() + "/100");
System.out.println("Student: " + grader.studentIdentifier());
System.out.println("Assignment ID: " + grader.assignmentId());
C#
using StudyfetchSDK;
using StudyfetchSDK.Models.V1.AssignmentGrader;
using System;
using System.Threading.Tasks;
public class GetGraderById
{
public static async Task GetById()
{
var client = new StudyfetchSDKClient()
{
APIKey = Environment.GetEnvironmentVariable("STUDYFETCH_API_KEY"),
BaseUrl = new Uri("https://studyfetchapi.com")
};
string graderID = "grader_123456";
var grader = await client.V1.AssignmentGrader.GetByID(new()
{
ID = graderID
});
Console.WriteLine($"ID: {grader._ID}");
Console.WriteLine($"Assignment: {grader.Title}");
Console.WriteLine($"Grade: {grader.Grade}/100");
Console.WriteLine($"Student: {grader.StudentIdentifier}");
Console.WriteLine($"Assignment ID: {grader.AssignmentID}");
}
}
Delete Assignment Grader
DELETE /assignment-grader/delete/:id
Delete a specific graded assignment by its ID.
| Path param | Description |
|---|---|
id |
Assignment grader document ID. |
Example Request (curl)
curl -X DELETE https://api.yourapp.com/api/v1/assignment-grader/delete/665a1c0e8f3b1a0012abcdef \
-H "x-api-key: YOUR_API_KEY"
Response, 200 OK
{ "success": true, "message": "Assignment deleted successfully" }
Returns 404 Not Found if the assignment doesn't exist or belongs to a different org.
SDK Examples
JavaScript
const graderId = "grader_123456";
await client.v1.assignmentGrader.delete(graderId);
console.log("Assignment grader deleted successfully");
Python
grader_id = "grader_123456"
client.v1.assignment_grader.delete(grader_id)
print("Assignment grader deleted successfully")
Java
String graderId = "grader_123456";
client.v1().assignmentGrader().delete(graderId);
System.out.println("Assignment grader deleted successfully");
C#
using StudyfetchSDK;
using System;
using System.Threading.Tasks;
public class DeleteGrader
{
public static async Task Delete()
{
var client = new StudyfetchSDKClient()
{
APIKey = Environment.GetEnvironmentVariable("STUDYFETCH_API_KEY"),
BaseUrl = new Uri("https://studyfetchapi.com")
};
string graderID = "grader_123456";
await client.V1.AssignmentGrader.Delete(new()
{
ID = graderID
});
Console.WriteLine("Assignment grader deleted successfully");
}
}
Generate Educator Report
GET /assignment-grader/educator-report/:assignmentId
Aggregates statistics across every graded submission in your organization that shares the given assignmentId. Use this after grading multiple students for the same logical assignment.
| Path param | Description |
|---|---|
assignmentId |
The assignmentId you tagged on each submission when calling POST /create. |
Example Request (curl)
curl -X GET https://api.yourapp.com/api/v1/assignment-grader/educator-report/WATER-CYCLE-2026 \
-H "x-api-key: YOUR_API_KEY"
Response, 200 OK
{
"assignmentId": "WATER-CYCLE-2026",
"title": "Water Cycle Reading Comprehension",
"totalSubmissions": 27,
"statistics": {
"averageGrade": "82.4",
"minGrade": "55.0",
"maxGrade": "100.0",
"standardDeviation": "11.7"
},
"gradeDistribution": { "A": 9, "B": 11, "C": 5, "D": 1, "F": 1 },
"criteriaAnalysis": [
{ "title": "Q1 — Evaporation", "avgScore": 4.6, "maxPossible": 5, "submissionCount": 27 },
{ "title": "Q4 — Groundwater", "avgScore": 2.9, "maxPossible": 5, "submissionCount": 27 }
],
"strengths": [
{ "title": "Q1 — Evaporation", "avgScore": "4.6", "maxPossible": 5, "performanceRatio": "92.0" }
],
"weaknesses": [
{ "title": "Q4 — Groundwater", "avgScore": "2.9", "maxPossible": 5, "performanceRatio": "58.0" }
],
"submissions": [
{
"id": "665a1c0e8f3b1a0012abcdef",
"studentIdentifier": "[email protected]",
"grade": 90,
"createdAt": "2026-04-28T07:14:00.000Z"
}
]
}
Field Semantics
| Field | Meaning |
|---|---|
statistics.averageGrade |
Mean of grade across all matching submissions, formatted to 1 decimal. |
statistics.standardDeviation |
Population stddev of grade, 1 decimal. |
gradeDistribution |
Letter-grade buckets, A: 90–100, B: 80–89, C: 70–79, D: 60–69, F: below 60. |
criteriaAnalysis |
Per-criterion average score across all submissions. |
strengths |
Criteria where avgScore / maxPossible >= 0.80. |
weaknesses |
Criteria where avgScore / maxPossible < 0.70. |
submissions |
Flat list of every submission feeding the report. |
Returns 404 Not Found if no submissions match the assignmentId in your organization.
SDK Examples
JavaScript
const assignmentId = "assign-456";
const report = await client.v1.assignmentGrader.generateReport(assignmentId);
console.log("Assignment Report:");
console.log(`Title: ${report.title}`);
console.log(`Total Submissions: ${report.totalSubmissions}`);
console.log(`Average Grade: ${report.statistics.averageGrade}`);
console.log(`Max Grade: ${report.statistics.maxGrade}`);
console.log(`Min Grade: ${report.statistics.minGrade}`);
console.log(`Standard Deviation: ${report.statistics.standardDeviation}`);
// Grade distribution
console.log("Grade Distribution:");
console.log(` A (90-100): ${report.gradeDistribution.A}`);
console.log(` B (80-89): ${report.gradeDistribution.B}`);
console.log(` C (70-79): ${report.gradeDistribution.C}`);
console.log(` D (60-69): ${report.gradeDistribution.D}`);
console.log(` F (0-59): ${report.gradeDistribution.F}`);
// Top performing criteria
console.log("Strengths:");
report.strengths.forEach(strength => {
console.log(` ${strength.title}: ${strength.avgScore}/${strength.maxPossible} (${strength.performanceRatio})`);
});
// Criteria needing improvement
console.log("Weaknesses:");
report.weaknesses.forEach(weakness => {
console.log(` ${weakness.title}: ${weakness.avgScore}/${weakness.maxPossible} (${weakness.performanceRatio})`);
});
Python
assignment_id = "assign-456"
report = client.v1.assignment_grader.generate_report(assignment_id)
print("Assignment Report:")
print(f"Title: {report.title}")
print(f"Total Submissions: {report.total_submissions}")
print(f"Average Grade: {report.statistics.average_grade}")
print(f"Max Grade: {report.statistics.max_grade}")
print(f"Min Grade: {report.statistics.min_grade}")
print(f"Standard Deviation: {report.statistics.standard_deviation}")
# Grade distribution
print("Grade Distribution:")
print(f" A (90-100): {report.grade_distribution.A}")
print(f" B (80-89): {report.grade_distribution.B}")
print(f" C (70-79): {report.grade_distribution.C}")
print(f" D (60-69): {report.grade_distribution.D}")
print(f" F (0-59): {report.grade_distribution.F}")
# Top performing criteria
print("Strengths:")
for strength in report.strengths:
print(f" {strength.title}: {strength.avg_score}/{strength.max_possible} ({strength.performance_ratio})")
# Criteria needing improvement
print("Weaknesses:")
for weakness in report.weaknesses:
print(f" {weakness.title}: {weakness.avg_score}/{weakness.max_possible} ({weakness.performance_ratio})")
Java
import com.studyfetch.javasdk.models.v1.assignmentgrader.AssignmentGraderGenerateReportResponse;
String assignmentId = "assign-456";
AssignmentGraderGenerateReportResponse report = client.v1().assignmentGrader().generateReport(assignmentId);
System.out.println("Assignment Report:");
System.out.println("Title: " + report.title());
System.out.println("Total Submissions: " + report.totalSubmissions());
System.out.println("Average Grade: " + report.statistics().averageGrade());
System.out.println("Max Grade: " + report.statistics().maxGrade());
System.out.println("Min Grade: " + report.statistics().minGrade());
System.out.println("Standard Deviation: " + report.statistics().standardDeviation());
// Grade distribution
System.out.println("Grade Distribution:");
System.out.println(" A (90-100): " + report.gradeDistribution().a());
System.out.println(" B (80-89): " + report.gradeDistribution().b());
System.out.println(" C (70-79): " + report.gradeDistribution().c());
System.out.println(" D (60-69): " + report.gradeDistribution().d());
System.out.println(" F (0-59): " + report.gradeDistribution().f());
// Top performing criteria
System.out.println("Strengths:");
for (AssignmentGraderGenerateReportResponse.Strength strength : report.strengths()) {
System.out.println(" " + strength.title() + ": " + strength.avgScore() + "/" +
strength.maxPossible() + " (" + strength.performanceRatio() + ")");
}
// Criteria needing improvement
System.out.println("Weaknesses:");
for (AssignmentGraderGenerateReportResponse.Weakness weakness : report.weaknesses()) {
System.out.println(" " + weakness.title() + ": " + weakness.avgScore() + "/" +
weakness.maxPossible() + " (" + weakness.performanceRatio() + ")");
}
C#
using StudyfetchSDK;
using StudyfetchSDK.Models.V1.AssignmentGrader;
using System;
using System.Threading.Tasks;
public class GenerateReport
{
public static async Task GenerateEducatorReport()
{
var client = new StudyfetchSDKClient()
{
APIKey = Environment.GetEnvironmentVariable("STUDYFETCH_API_KEY"),
BaseUrl = new Uri("https://studyfetchapi.com")
};
string assignmentID = "assign-456";
var report = await client.V1.AssignmentGrader.GenerateReport(new()
{
AssignmentID = assignmentID
});
Console.WriteLine("Assignment Report:");
Console.WriteLine($"Title: {report.Title}");
Console.WriteLine($"Total Submissions: {report.TotalSubmissions}");
Console.WriteLine($"Average Grade: {report.Statistics.AverageGrade}");
Console.WriteLine($"Max Grade: {report.Statistics.MaxGrade}");
Console.WriteLine($"Min Grade: {report.Statistics.MinGrade}");
Console.WriteLine($"Standard Deviation: {report.Statistics.StandardDeviation}");
// Grade distribution
Console.WriteLine("Grade Distribution:");
Console.WriteLine($" A (90-100): {report.GradeDistribution.A}");
Console.WriteLine($" B (80-89): {report.GradeDistribution.B}");
Console.WriteLine($" C (70-79): {report.GradeDistribution.C}");
Console.WriteLine($" D (60-69): {report.GradeDistribution.D}");
Console.WriteLine($" F (0-59): {report.GradeDistribution.F}");
// Top performing criteria
Console.WriteLine("Strengths:");
foreach (var strength in report.Strengths)
{
Console.WriteLine($" {strength.Title}: {strength.AvgScore}/{strength.MaxPossible} ({strength.PerformanceRatio})");
}
// Criteria needing improvement
Console.WriteLine("Weaknesses:");
foreach (var weakness in report.Weaknesses)
{
Console.WriteLine($" {weakness.Title}: {weakness.AvgScore}/{weakness.MaxPossible} ({weakness.PerformanceRatio})");
}
}
}
Rubric Templates
Rubric templates are reusable rubrics scoped to your organization. They optionally store an assignmentContent so the same source material is automatically supplied every time the template is used.
Create Rubric Template
POST /assignment-grader/rubric-templates
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
name |
string |
Yes | Display name for the template. |
description |
string |
No | Notes about when/how to use the template. |
assignmentContent |
string |
No | Default source material applied to any grading request that uses this template (overridable per request). |
criteria |
RubricCriterion[] |
Yes | Grading criteria. Must contain at least one entry. |
Example Request (curl)
curl -X POST https://api.yourapp.com/api/v1/assignment-grader/rubric-templates \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Water Cycle — Reading Comprehension",
"description": "Grades student answers about the water cycle reading passage",
"assignmentContent": "The Water Cycle\n\nWater on Earth is constantly moving...",
"criteria": [
{ "title": "Q1 — Evaporation", "description": "Mentions sun heating water", "pointsPossible": 5 },
{ "title": "Q2 — Condensation", "description": "Describes vapor cooling/clouds", "pointsPossible": 5 },
{ "title": "Q3 — Precipitation", "description": "Names ≥3 forms of precipitation", "pointsPossible": 5 }
]
}'
Response, 201 Created
A RubricTemplate document.
SDK Examples
JavaScript
const templateParams = {
name: "Standard Essay Rubric",
description: "Basic 100-point essay grading rubric",
assignmentContent: "Optional — default source material for this template", // Optional: used when no assignmentContent is provided at grading time
criteria: [
{
pointsPossible: 25,
title: "Thesis Statement",
description: "Clear, arguable thesis that addresses the prompt"
},
{
pointsPossible: 30,
title: "Evidence and Analysis",
description: "Strong evidence with thorough analysis"
},
{
pointsPossible: 25,
title: "Organization",
description: "Logical structure with smooth transitions"
},
{
pointsPossible: 20,
title: "Writing Mechanics",
description: "Grammar, spelling, and citation format"
}
]
};
const template = await client.v1.assignmentGrader.rubricTemplates.create(templateParams);
console.log(`Template created: ${template._id}`);
Python
template_params = {
"name": "Standard Essay Rubric",
"description": "Basic 100-point essay grading rubric",
"assignmentContent": "Optional — default source material for this template", # Optional: used when no assignmentContent is provided at grading time
"criteria": [
{
"pointsPossible": 25,
"title": "Thesis Statement",
"description": "Clear, arguable thesis that addresses the prompt"
},
{
"pointsPossible": 30,
"title": "Evidence and Analysis",
"description": "Strong evidence with thorough analysis"
},
{
"pointsPossible": 25,
"title": "Organization",
"description": "Logical structure with smooth transitions"
},
{
"pointsPossible": 20,
"title": "Writing Mechanics",
"description": "Grammar, spelling, and citation format"
}
]
}
template = client.v1.assignment_grader.rubric_templates().create(template_params)
print(f"Template created: {template._id}")
Java
RubricTemplateCreateParams templateParams = RubricTemplateCreateParams.builder()
.name("Standard Essay Rubric")
.description("Basic 100-point essay grading rubric")
.assignmentContent("Optional — default source material for this template") // Optional: used when no assignmentContent is provided at grading time
.addCriterion(RubricTemplateCreateParams.Criterion.builder()
.pointsPossible(25.0)
.title("Thesis Statement")
.description("Clear, arguable thesis that addresses the prompt")
.build())
.addCriterion(RubricTemplateCreateParams.Criterion.builder()
.pointsPossible(30.0)
.title("Evidence and Analysis")
.description("Strong evidence with thorough analysis")
.build())
.addCriterion(RubricTemplateCreateParams.Criterion.builder()
.pointsPossible(25.0)
.title("Organization")
.description("Logical structure with smooth transitions")
.build())
.addCriterion(RubricTemplateCreateParams.Criterion.builder()
.pointsPossible(20.0)
.title("Writing Mechanics")
.description("Grammar, spelling, and citation format")
.build())
.build();
RubricTemplateCreateResponse template = client.v1().assignmentGrader()
.rubricTemplates().create(templateParams);
System.out.println("Template created: " + template._id());
C#
using StudyfetchSDK;
using StudyfetchSDK.Models.V1.AssignmentGrader;
using StudyfetchSDK.Models.V1.AssignmentGrader.RubricTemplates;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
public class CreateRubricTemplate
{
public static async Task CreateTemplate()
{
var client = new StudyfetchSDKClient()
{
APIKey = Environment.GetEnvironmentVariable("STUDYFETCH_API_KEY"),
BaseUrl = new Uri("https://studyfetchapi.com")
};
var templateParams = new RubricTemplateCreateParams()
{
Name = "Standard Essay Rubric",
Description = "Basic 100-point essay grading rubric",
AssignmentContent = "Optional — default source material for this template", // Optional: used when no assignmentContent is provided at grading time
Criteria = new List<RubricCriterion>
{
new RubricCriterion()
{
PointsPossible = 25,
Title = "Thesis Statement",
Description = "Clear, arguable thesis that addresses the prompt"
},
new RubricCriterion()
{
PointsPossible = 30,
Title = "Evidence and Analysis",
Description = "Strong evidence with thorough analysis"
},
new RubricCriterion()
{
PointsPossible = 25,
Title = "Organization",
Description = "Logical structure with smooth transitions"
},
new RubricCriterion()
{
PointsPossible = 20,
Title = "Writing Mechanics",
Description = "Grammar, spelling, and citation format"
}
}
};
var template = await client.V1.AssignmentGrader.RubricTemplates.Create(templateParams);
Console.WriteLine($"Template created: {template._ID}");
}
}
List Rubric Templates
GET /assignment-grader/rubric-templates
Returns every rubric template for the authenticated organization, sorted newest first.
Example Request (curl)
curl -X GET https://api.yourapp.com/api/v1/assignment-grader/rubric-templates \
-H "x-api-key: YOUR_API_KEY"
SDK Examples
JavaScript
const templates = await client.v1.assignmentGrader.rubricTemplates.list();
templates.forEach(template => {
console.log(`Name: ${template.name}`);
console.log(`Description: ${template.description}`);
console.log(`Created: ${template.createdAt}`);
});
Python
templates = client.v1.assignment_grader.rubric_templates().list()
for template in templates:
print(f"Name: {template.name}")
print(f"Description: {template.description}")
print(f"Created: {template.created_at}")
Java
import com.studyfetch.javasdk.models.v1.assignmentgrader.rubrictemplates.RubricTemplateListParams;
import com.studyfetch.javasdk.models.v1.assignmentgrader.rubrictemplates.RubricTemplateListResponse;
List<RubricTemplateListResponse> templates = client.v1().assignmentGrader()
.rubricTemplates().list(
RubricTemplateListParams.builder()
.build()
);
for (RubricTemplateListResponse template : templates) {
System.out.println("Name: " + template.name());
System.out.println("Description: " + template.description());
System.out.println("Created: " + template.createdAt());
}
C#
using StudyfetchSDK;
using StudyfetchSDK.Models.V1.AssignmentGrader;
using System;
using System.Threading.Tasks;
public class ListRubricTemplates
{
public static async Task ListTemplates()
{
var client = new StudyfetchSDKClient()
{
APIKey = Environment.GetEnvironmentVariable("STUDYFETCH_API_KEY"),
BaseUrl = new Uri("https://studyfetchapi.com")
};
var templates = await client.V1.AssignmentGrader.RubricTemplates.List();
foreach (var template in templates)
{
Console.WriteLine($"Name: {template.Name}");
Console.WriteLine($"Description: {template.Description}");
Console.WriteLine($"Created: {template.CreatedAt}");
}
}
}
Get Rubric Template by ID
GET /assignment-grader/rubric-templates/:id
| Path param | Description |
|---|---|
id |
Rubric template _id. |
Returns a single RubricTemplate. Returns 404 Not Found if not found in your organization.
SDK Examples
JavaScript
const templateId = "template-123";
const template = await client.v1.assignmentGrader.rubricTemplates.getByID(templateId);
console.log(`Template: ${template.name}`);
console.log(`Description: ${template.description}`);
console.log(`Criteria:`, template.criteria);
Python
template_id = "template-123"
template = client.v1.assignment_grader.rubric_templates().get_by_id(template_id)
print(f"Template: {template.name}")
print(f"Description: {template.description}")
print(f"Criteria: {template.criteria}")
Java
import com.studyfetch.javasdk.models.v1.assignmentgrader.rubrictemplates.RubricTemplateGetByIdParams;
import com.studyfetch.javasdk.models.v1.assignmentgrader.rubrictemplates.RubricTemplateGetByIdResponse;
String templateId = "template-123";
RubricTemplateGetByIdResponse template = client.v1().assignmentGrader()
.rubricTemplates().getById(
RubricTemplateGetByIdParams.builder()
.id(templateId)
.build()
);
System.out.println("Template: " + template.name());
System.out.println("Description: " + template.description());
System.out.println("Criteria: " + template.criteria());
C#
using StudyfetchSDK;
using StudyfetchSDK.Models.V1.AssignmentGrader;
using System;
using System.Threading.Tasks;
public class GetRubricTemplateById
{
public static async Task GetTemplateById()
{
var client = new StudyfetchSDKClient()
{
APIKey = Environment.GetEnvironmentVariable("STUDYFETCH_API_KEY"),
BaseUrl = new Uri("https://studyfetchapi.com")
};
string templateID = "template-123";
var template = await client.V1.AssignmentGrader.RubricTemplates.GetByID(new()
{
ID = templateID
});
Console.WriteLine($"Template: {template.Name}");
Console.WriteLine($"Description: {template.Description}");
Console.WriteLine($"Criteria: {template.Criteria}");
}
}
Delete Rubric Template
DELETE /assignment-grader/rubric-templates/:id
Response, 200 OK
{ "success": true, "message": "Rubric template deleted successfully" }
Returns 404 Not Found if the template doesn't exist or belongs to a different org.
SDK Examples
JavaScript
const templateId = "template-123";
await client.v1.assignmentGrader.rubricTemplates.delete(templateId);
console.log("Rubric template deleted successfully");
Python
template_id = "template-123"
client.v1.assignment_grader.rubric_templates().delete(template_id)
print("Rubric template deleted successfully")
Java
String templateId = "template-123";
client.v1().assignmentGrader().rubricTemplates().delete(templateId);
System.out.println("Rubric template deleted successfully");
C#
using StudyfetchSDK;
using System;
using System.Threading.Tasks;
using StudyfetchSDK.Models.V1.AssignmentGrader.RubricTemplates;
public class DeleteRubricTemplate
{
public static async Task DeleteTemplate()
{
var client = new StudyfetchSDKClient()
{
APIKey = Environment.GetEnvironmentVariable("STUDYFETCH_API_KEY"),
BaseUrl = new Uri("https://studyfetchapi.com")
};
string templateID = "template-123";
await client.V1.AssignmentGrader.RubricTemplates.Delete(new()
{
ID = templateID
});
Console.WriteLine("Rubric template deleted successfully");
}
}
Assignment Content
The assignmentContent field provides the AI grader with the original material students were working from, such as a reading passage, case study, or prompt. This is critical for accurate grading because the AI can verify student answers against the actual source instead of relying on its own knowledge.
Where You Can Set It
| Location | When it's used |
|---|---|
Top-level field on POST /assignment-grader/create |
Used for that single grading request |
Inside the rubric object on POST /assignment-grader/create |
Same as above (top-level takes priority if both set) |
On a rubric template (POST /assignment-grader/rubric-templates) |
Automatically used whenever that template is selected for grading, unless overridden at the request level |
Priority Order
When assignmentContent is specified in multiple places, the following priority applies:
- Top-level
assignmentContenton the grading request (highest priority) assignmentContentinside therubricobject on the grading requestassignmentContentsaved on the rubric template (ifrubricTemplateIdis used, lowest priority)
Usage Patterns
Every grading request has up to four pieces working together:
| Piece | What it is | Where it goes |
|---|---|---|
| Source material | The essay, passage, case study, or prompt the student reads first | assignmentContent |
| Question | What the student is asked to do or answer | Criterion title and description |
| Rubric | The grading criteria, what counts as a good answer | rubric.criteria[] or a saved rubric template |
| Student answer | The student's actual submission | textToGrade or materialId |
The AI grader reads all four, then scores the student's answer against the rubric using the source material as the ground truth. Below are the most common patterns for combining them.
Pattern 1, Reading Comprehension (Passage + Questions)
A teacher assigns a reading passage and asks students to answer questions about it. Each question becomes a rubric criterion, and the passage goes in assignmentContent.
curl -X POST https://api.yourapp.com/api/v1/assignment-grader/create \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"title": "Chapter 5 Reading Comprehension",
"assignmentContent": "The Industrial Revolution began in Britain in the late 18th century. Factories replaced cottage industries, steam power transformed transportation, and urban populations surged as workers migrated from rural areas. Working conditions were often dangerous, with long hours and child labor common until reform acts were passed in the mid-19th century.",
"textToGrade": "1. The Industrial Revolution started in Britain.\n2. Steam power was used for transportation.\n3. Working conditions were great because factories were new.\n4. Child labor ended because of the Reform Acts.",
"rubric": {
"criteria": [
{
"title": "Q1 — Origin",
"description": "Where and when did the Industrial Revolution begin? Student should mention Britain and the late 18th century.",
"pointsPossible": 5
},
{
"title": "Q2 — Steam power",
"description": "How did steam power change society? Student should mention transportation.",
"pointsPossible": 5
},
{
"title": "Q3 — Working conditions",
"description": "Describe working conditions. Student should mention dangerous conditions, long hours, or child labor — NOT that conditions were good.",
"pointsPossible": 5
},
{
"title": "Q4 — Reform",
"description": "What ended child labor? Student should reference reform acts in the mid-19th century.",
"pointsPossible": 5
}
]
},
"assignmentId": "HISTORY-CH5",
"studentIdentifier": "[email protected]"
}'
The AI will flag Q3 as incorrect because the student said conditions were "great", the passage says they were dangerous.
Pattern 2, Essay / Open-Ended Writing (No Source Material)
When students write an original essay (no assigned reading), you don't need assignmentContent at all. The rubric criteria describe what a good essay looks like.
curl -X POST https://api.yourapp.com/api/v1/assignment-grader/create \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"title": "Persuasive Essay — School Uniforms",
"textToGrade": "I believe schools should require uniforms because they reduce bullying and help students focus on learning instead of fashion...",
"rubric": {
"criteria": [
{
"title": "Thesis Statement",
"description": "Clear, arguable thesis in the introduction that takes a position on the topic.",
"pointsPossible": 20
},
{
"title": "Supporting Arguments",
"description": "At least two distinct arguments supporting the thesis, each with reasoning or evidence.",
"pointsPossible": 30
},
{
"title": "Counterargument",
"description": "Acknowledges and responds to at least one opposing viewpoint.",
"pointsPossible": 20
},
{
"title": "Organization & Clarity",
"description": "Logical paragraph structure, clear transitions, and a conclusion that reinforces the thesis.",
"pointsPossible": 20
},
{
"title": "Grammar & Mechanics",
"description": "Minimal spelling, punctuation, and grammar errors.",
"pointsPossible": 10
}
]
},
"assignmentId": "ESSAY-UNIFORMS",
"studentIdentifier": "[email protected]"
}'
Pattern 3, Case Study Analysis (Scenario + Specific Questions)
Give the AI a case study as assignmentContent, then define criteria that test whether the student applied the right frameworks or identified the right issues.
curl -X POST https://api.yourapp.com/api/v1/assignment-grader/create \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"title": "Business Ethics Case Study",
"assignmentContent": "Case Study: GreenTech Corp discovered that one of their suppliers uses child labor. The supplier accounts for 40% of GreenTech raw materials and switching would increase costs by 25%. GreenTech CEO must decide whether to continue the relationship, find alternatives, or publicly disclose the issue. Stakeholders include shareholders, employees, the children involved, and consumers.",
"textToGrade": "GreenTech should drop the supplier immediately because child labor is wrong. They can find another supplier easily.",
"rubric": {
"criteria": [
{
"title": "Stakeholder Identification",
"description": "Student identifies at least 3 stakeholders from the case and explains how each is affected.",
"pointsPossible": 10
},
{
"title": "Ethical Framework",
"description": "Student applies at least one ethical framework (utilitarian, deontological, virtue ethics, etc.) to analyze the dilemma.",
"pointsPossible": 15
},
{
"title": "Trade-off Analysis",
"description": "Student acknowledges the cost/supply trade-off (25% cost increase, 40% of materials) rather than oversimplifying.",
"pointsPossible": 10
},
{
"title": "Recommendation Quality",
"description": "Student provides a nuanced recommendation with implementation steps, not just a one-line answer.",
"pointsPossible": 15
}
]
}
}'
The AI will notice the student oversimplified, they didn't acknowledge the trade-offs or apply an ethical framework.
Pattern 4, Reusable Template for Repeated Assignments
When many students answer the same questions about the same material, save everything in a rubric template so you only define the rubric and passage once.
Create the template once:
curl -X POST https://api.yourapp.com/api/v1/assignment-grader/rubric-templates \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Photosynthesis Quiz",
"assignmentContent": "Photosynthesis is the process by which green plants convert sunlight, water, and carbon dioxide into glucose and oxygen. It occurs primarily in the leaves, within organelles called chloroplasts. The light-dependent reactions happen in the thylakoid membranes, while the Calvin cycle occurs in the stroma.",
"criteria": [
{ "title": "Inputs", "description": "Student names sunlight, water, and CO2 as inputs.", "pointsPossible": 5 },
{ "title": "Outputs", "description": "Student names glucose and oxygen as outputs.", "pointsPossible": 5 },
{ "title": "Location", "description": "Student mentions chloroplasts or leaves.", "pointsPossible": 5 }
]
}'
Grade each student, just send the answer:
curl -X POST https://api.yourapp.com/api/v1/assignment-grader/create \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"title": "Photosynthesis Quiz",
"rubricTemplateId": "TEMPLATE_ID",
"assignmentId": "BIO-PHOTOSYNTHESIS",
"studentIdentifier": "[email protected]",
"textToGrade": "Plants use sunlight and water to make food. This happens in the leaves."
}'
The passage and rubric are pulled from the template automatically, no need to re-send them.
Choosing the Right Pattern
| Scenario | Use assignmentContent? |
Use a template? |
|---|---|---|
| Students answer questions about a specific reading | Yes | Yes, if grading multiple students |
| Students write an original essay with no assigned reading | No | Yes, if the rubric is reused across assignments |
| One-off grading of a single submission | Depends on whether there's source material | No, use an inline rubric |
| Same assignment given to 30+ students | Yes (bake it into the template) | Yes |
| Case study or lab report with reference material | Yes | Optional |
Grading from Material
Grade an assignment that has already been uploaded as a material.
JavaScript
// First upload the assignment as a material
const material = await client.v1.materials.create({
name: "Student Essay - John Doe",
content: {
type: "pdf",
file: studentEssayFile
},
references: [
{
title: 'Assignment Instructions',
url: 'https://example.com/assignment-instructions'
}
] // Optional: source references
});
// Wait for material to finish processing
let materialStatus = 'processing';
while (materialStatus !== 'active') {
const updatedMaterial = await client.v1.materials.retrieve(material._id);
materialStatus = updatedMaterial.status;
if (materialStatus === 'error') {
throw new Error('Material processing failed');
}
if (materialStatus === 'processing') {
await new Promise(resolve => setTimeout(resolve, 2000)); // Wait 2 seconds
}
}
// Then grade using the material ID
const gradeResult = await client.v1.assignmentGrader.create({
rubric: rubricDefinition,
title: "Essay Assignment",
materialId: material._id,
assignmentId: "assign-456",
studentIdentifier: "[[email protected]](mailto:[email protected])"
});
Python
import time
# First upload the assignment as a material
material = client.v1.materials.create({
"name": "Student Essay - John Doe",
"content": {
"type": "pdf",
"file": student_essay_file
},
"references": [
{
"title": "Assignment Instructions",
"url": "https://example.com/assignment-instructions"
}
] # Optional: source references
})
# Wait for material to finish processing
material_status = "processing"
while material_status != "active":
updated_material = client.v1.materials.retrieve(material["_id"])
material_status = updated_material.status
if material_status == "error":
raise Exception("Material processing failed")
if material_status == "processing":
time.sleep(2) # Wait 2 seconds
# Then grade using the material ID
grade_result = client.v1.assignment_grader.create({
"rubric": rubric_definition,
"title": "Essay Assignment",
"materialId": material["_id"],
"assignmentId": "assign-456",
"studentIdentifier": "[email protected]"
})
Java
import com.studyfetch.javasdk.models.v1.materials.upload.UploadUploadFileParams;
import com.studyfetch.javasdk.models.v1.materials.MaterialResponse;
import com.studyfetch.javasdk.models.v1.materials.MaterialRetrieveParams;
import java.io.File;
import java.io.FileInputStream;
// First upload the assignment as a material
File studentEssayFile = new File("student-essay.pdf");
UploadUploadFileParams uploadParams = UploadUploadFileParams.builder()
.file(new FileInputStream(studentEssayFile))
.name("Student Essay - John Doe")
.build();
MaterialResponse material = client.v1().materials().upload().uploadFile(uploadParams);
// Wait for material to finish processing
MaterialResponse.Status status = MaterialResponse.Status.PROCESSING;
while (status.equals(MaterialResponse.Status.PROCESSING)) {
Thread.sleep(2000); // Wait 2 seconds
MaterialResponse updatedMaterial = client.v1().materials().retrieve(
MaterialRetrieveParams.builder()
.id(material._id())
.build()
);
status = updatedMaterial.status();
if (status.equals(MaterialResponse.Status.ERROR)) {
throw new Exception("Material processing failed");
}
}
// Then grade using the material ID
AssignmentGraderCreateParams gradeParams = AssignmentGraderCreateParams.builder()
.rubric(rubricDefinition)
.title("Essay Assignment")
.materialId(material._id())
.assignmentId("assign-456")
.studentIdentifier("[email protected]")
.build();
AssignmentGraderCreateResponse gradeResult = client.v1().assignmentGrader().create(gradeParams);
C#
using StudyfetchSDK;
using StudyfetchSDK.Models.V1.Materials;
using StudyfetchSDK.Models.V1.AssignmentGrader;
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
public class GradeFromMaterial
{
public static async Task GradeMaterialAssignment()
{
var client = new StudyfetchSDKClient()
{
APIKey = Environment.GetEnvironmentVariable("STUDYFETCH_API_KEY"),
BaseUrl = new Uri("https://studyfetchapi.com")
};
// First upload the assignment as a material
var fileBytes = await File.ReadAllBytesAsync("student-essay.pdf");
var material = await client.V1.Materials.Upload.UploadFile(new()
{
File = fileBytes,
FileName = "student-essay.pdf",
Name = "Student Essay - John Doe"
});
// Wait for material to finish processing
string materialStatus = "processing";
while (materialStatus != "active")
{
var updatedMaterial = await client.V1.Materials.Retrieve(new()
{
ID = material._ID
});
materialStatus = updatedMaterial.Status.Raw();
if (materialStatus == "error")
{
throw new Exception("Material processing failed");
}
if (materialStatus == "processing")
{
await Task.Delay(2000); // Wait 2 seconds
}
}
// Then grade using the material ID
var gradeResult = await client.V1.AssignmentGrader.Create(new AssignmentGraderCreateParams()
{
Rubric = rubricDefinition,
Title = "Essay Assignment",
MaterialID = material._ID,
AssignmentID = "assign-456",
StudentIdentifier = "[email protected]"
});
}
}
Using Create and Process (Recommended for Text)
For text assignments, use createAndProcess to automatically wait for processing:
JavaScript
// Create and process text material in one step
const material = await client.v1.materials.createAndProcess({
content: {
type: 'text',
text: studentEssayText
},
name: 'Student Essay - John Doe',
references: [
{
title: 'Assignment Prompt',
url: 'https://example.com/assignment-prompt'
}
] // Optional: source references
});
// Material is immediately ready for grading
const gradeResult = await client.v1.assignmentGrader.create({
rubric: rubricDefinition,
title: "Essay Assignment",
materialId: material._id,
assignmentId: "assign-456",
studentIdentifier: "[[email protected]](mailto:[email protected])"
});
Python
# Create and process text material in one step
material = client.v1.materials.create_and_process({
'content': {
'type': 'text',
'text': student_essay_text
},
'name': 'Student Essay - John Doe',
'references': [
{
'title': 'Assignment Prompt',
'url': 'https://example.com/assignment-prompt'
}
] # Optional: source references
})
# Material is immediately ready for grading
grade_result = client.v1.assignment_grader.create({
"rubric": rubric_definition,
"title": "Essay Assignment",
"materialId": material["_id"],
"assignmentId": "assign-456",
"studentIdentifier": "[email protected]"
})
Java
import com.studyfetch.javasdk.models.v1.materials.MaterialCreateAndProcessParams;
import com.studyfetch.javasdk.models.v1.materials.MaterialResponse;
import com.studyfetch.javasdk.models.v1.materials.Content;
import com.studyfetch.javasdk.models.v1.materials.Reference;
import java.util.List;
// Create and process text material in one step
MaterialCreateAndProcessParams createParams = MaterialCreateAndProcessParams.builder()
.content(Content.builder()
.type(Content.Type.TEXT)
.text(studentEssayText)
.build())
.name("Student Essay - John Doe")
.references(List.of(
Reference.builder()
.title("Assignment Prompt")
.url("https://example.com/assignment-prompt")
.build()
)) // Optional: source references
.build();
MaterialResponse material = client.v1().materials().createAndProcess(createParams);
// Material is immediately ready for grading
AssignmentGraderCreateParams gradeParams = AssignmentGraderCreateParams.builder()
.rubric(rubricDefinition)
.title("Essay Assignment")
.materialId(material._id())
.assignmentId("assign-456")
.studentIdentifier("[email protected]")
.build();
AssignmentGraderCreateResponse gradeResult = client.v1().assignmentGrader().create(gradeParams);
C#
using StudyfetchSDK;
using StudyfetchSDK.Models.V1.Materials;
using StudyfetchSDK.Models.V1.AssignmentGrader;
using System;
using System.Threading.Tasks;
public class CreateAndProcessGrading
{
public static async Task GradeWithCreateAndProcess()
{
var client = new StudyfetchSDKClient()
{
APIKey = Environment.GetEnvironmentVariable("STUDYFETCH_API_KEY"),
BaseUrl = new Uri("https://studyfetchapi.com")
};
// Create and process text material in one step
string studentEssayText = "Student essay content here...";
var material = await client.V1.Materials.CreateAndProcess(new()
{
Content = new()
{
Type = StudyfetchSDK.Models.V1.Materials.ContentProperties.Type.Text,
Text = studentEssayText
},
Name = "Student Essay - John Doe"
});
// Material is immediately ready for grading
var gradeResult = await client.V1.AssignmentGrader.Create(new AssignmentGraderCreateParams()
{
Rubric = rubricDefinition,
Title = "Essay Assignment",
MaterialID = material._ID,
AssignmentID = "assign-456",
StudentIdentifier = "[email protected]"
});
}
}
Full Example: Passage Reading + Question Answering
This end-to-end walkthrough shows the full lifecycle: create a reusable template with a passage baked in, grade multiple students against it, then pull a class-wide educator report.
Step 1, Create a Rubric Template with the Passage
curl -X POST https://api.yourapp.com/api/v1/assignment-grader/rubric-templates \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "The Water Cycle — Reading Comprehension",
"description": "Grades student answers about the water cycle passage",
"assignmentContent": "The Water Cycle\n\nWater on Earth is constantly moving. The sun heats water in oceans, lakes, and rivers, causing it to evaporate into water vapor that rises into the atmosphere. As the vapor rises, it cools and condenses into tiny droplets that form clouds — a process called condensation. When enough droplets gather, they fall back to Earth as precipitation (rain, snow, sleet, or hail). Some precipitation flows into rivers and streams as runoff, eventually returning to the ocean. Some seeps into the ground and becomes groundwater, which feeds wells and springs. This continuous movement of water — from surface to atmosphere and back — is called the water cycle, or hydrological cycle.",
"criteria": [
{ "title": "Q1 — Evaporation", "description": "Mentions the sun heating bodies of water", "pointsPossible": 5 },
{ "title": "Q2 — Condensation", "description": "Describes vapor cooling and forming droplets/clouds", "pointsPossible": 5 },
{ "title": "Q3 — Precipitation", "description": "Names ≥3 forms of precipitation from the passage", "pointsPossible": 5 },
{ "title": "Q4 — Groundwater", "description": "Mentions groundwater, wells, or springs", "pointsPossible": 5 },
{ "title": "Q5 — Closed loop", "description": "Conveys that the cycle has no beginning or end", "pointsPossible": 5 }
]
}'
Capture _id from the response, that's your TEMPLATE_ID.
JavaScript
const template = await client.v1.assignmentGrader.rubricTemplates.create({
name: "The Water Cycle — Reading Comprehension",
description: "Grades student answers about the water cycle reading passage",
assignmentContent: "The Water Cycle\n\nWater on Earth is constantly moving. The sun heats water in oceans, lakes, and rivers, causing it to evaporate into water vapor that rises into the atmosphere. As the vapor rises, it cools and condenses into tiny droplets that form clouds — a process called condensation. When enough droplets gather, they fall back to Earth as precipitation (rain, snow, sleet, or hail). Some precipitation flows into rivers and streams as runoff, eventually returning to the ocean. Some seeps into the ground and becomes groundwater, which feeds wells and springs. This continuous movement of water — from surface to atmosphere and back — is called the water cycle, or hydrological cycle. It has no beginning or end; it is a closed loop that has been running for billions of years.",
criteria: [
{
title: "Question 1 — Evaporation",
description: "What causes water to evaporate? Student should mention the sun heating bodies of water.",
pointsPossible: 5
},
{
title: "Question 2 — Condensation",
description: "Explain what condensation is. Student should describe water vapor cooling and forming droplets/clouds.",
pointsPossible: 5
},
{
title: "Question 3 — Precipitation Types",
description: "Name at least 3 forms of precipitation mentioned in the passage.",
pointsPossible: 5
},
{
title: "Question 4 — Groundwater",
description: "What happens to precipitation that seeps into the ground? Student should mention groundwater, wells, or springs.",
pointsPossible: 5
},
{
title: "Question 5 — Why is it called a cycle?",
description: "Why is the water cycle described as a closed loop? Student should convey that it has no beginning or end and repeats continuously.",
pointsPossible: 5
}
]
});
console.log(`Template created: ${template._id}`);
Python
template = client.v1.assignment_grader.rubric_templates().create({
"name": "The Water Cycle — Reading Comprehension",
"description": "Grades student answers about the water cycle reading passage",
"assignmentContent": "The Water Cycle\n\nWater on Earth is constantly moving. The sun heats water in oceans, lakes, and rivers, causing it to evaporate into water vapor that rises into the atmosphere. As the vapor rises, it cools and condenses into tiny droplets that form clouds — a process called condensation. When enough droplets gather, they fall back to Earth as precipitation (rain, snow, sleet, or hail). Some precipitation flows into rivers and streams as runoff, eventually returning to the ocean. Some seeps into the ground and becomes groundwater, which feeds wells and springs. This continuous movement of water — from surface to atmosphere and back — is called the water cycle, or hydrological cycle. It has no beginning or end; it is a closed loop that has been running for billions of years.",
"criteria": [
{
"title": "Question 1 — Evaporation",
"description": "What causes water to evaporate? Student should mention the sun heating bodies of water.",
"pointsPossible": 5
},
{
"title": "Question 2 — Condensation",
"description": "Explain what condensation is. Student should describe water vapor cooling and forming droplets/clouds.",
"pointsPossible": 5
},
{
"title": "Question 3 — Precipitation Types",
"description": "Name at least 3 forms of precipitation mentioned in the passage.",
"pointsPossible": 5
},
{
"title": "Question 4 — Groundwater",
"description": "What happens to precipitation that seeps into the ground? Student should mention groundwater, wells, or springs.",
"pointsPossible": 5
},
{
"title": "Question 5 — Why is it called a cycle?",
"description": "Why is the water cycle described as a closed loop? Student should convey that it has no beginning or end and repeats continuously.",
"pointsPossible": 5
}
]
})
print(f"Template created: {template._id}")
Step 2, Grade Each Student's Submission
Repeat this for every student, keeping the same assignmentId.
curl -X POST https://api.yourapp.com/api/v1/assignment-grader/create \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"title": "Water Cycle Reading Comprehension",
"rubricTemplateId": "TEMPLATE_ID",
"assignmentId": "WATER-CYCLE-2026",
"studentIdentifier": "[email protected]",
"textToGrade": "1. Water evaporates because the sun heats it...\n2. Condensation is when vapor turns into clouds...\n3. Rain, snow, sleet.\n4. It becomes part of rivers.\n5. It just keeps going."
}'
JavaScript
const result = await client.v1.assignmentGrader.create({
title: "Water Cycle Reading Comprehension",
rubricTemplateId: template._id, // Reuse the template from Step 1
assignmentId: "WATER-CYCLE-2026",
studentIdentifier: "[email protected]",
textToGrade: "1. Water evaporates because the sun heats it up from oceans and lakes.\n2. Condensation is when water vapor goes up and turns into clouds.\n3. Rain, snow, and sleet.\n4. It goes into the ground and becomes part of rivers.\n5. It is called a cycle because it keeps going around and around without stopping."
});
console.log(`Grade: ${result.gradedAssignment.grade}/100`);
// Per-criterion feedback
result.gradedAssignment.rubric.criteria.forEach(c => {
console.log(`${c.title}: ${c.pointsAwarded}/${c.pointsPossible}`);
console.log(` Feedback: ${c.feedback}`);
});
Python
result = client.v1.assignment_grader.create({
"title": "Water Cycle Reading Comprehension",
"rubricTemplateId": template._id, # Reuse the template from Step 1
"assignmentId": "WATER-CYCLE-2026",
"studentIdentifier": "[email protected]",
"textToGrade": "1. Water evaporates because the sun heats it up from oceans and lakes.\n2. Condensation is when water vapor goes up and turns into clouds.\n3. Rain, snow, and sleet.\n4. It goes into the ground and becomes part of rivers.\n5. It is called a cycle because it keeps going around and around without stopping."
})
print(f"Grade: {result.graded_assignment.grade}/100")
# Per-criterion feedback
for c in result.graded_assignment.rubric.criteria:
print(f"{c.title}: {c.points_awarded}/{c.points_possible}")
print(f" Feedback: {c.feedback}")
What the AI grader does:
- Reads the rubric criteria (questions + what to look for)
- Reads the assignment content (the water cycle passage) as the source of truth
- Reads the student's submission (their answers)
- Grades each answer against the passage, not against its own knowledge
- Returns per-criterion scores and feedback
Step 3, Pull the Educator Report
After grading multiple students with the same assignmentId, generate an aggregate report:
curl -X GET https://api.yourapp.com/api/v1/assignment-grader/educator-report/WATER-CYCLE-2026 \
-H "x-api-key: YOUR_API_KEY"
You'll receive aggregate statistics, grade distribution, per-criterion analysis, strengths, weaknesses, and a list of every submission.
JavaScript
const report = await client.v1.assignmentGrader.generateReport("WATER-CYCLE-2026");
console.log(`Average Grade: ${report.statistics.averageGrade}`);
console.log(`Total Submissions: ${report.totalSubmissions}`);
// See which criteria students struggled with
report.weaknesses.forEach(w => {
console.log(`Weakness: ${w.title} — avg ${w.avgScore}/${w.maxPossible}`);
});
Python
report = client.v1.assignment_grader.generate_report("WATER-CYCLE-2026")
print(f"Average Grade: {report.statistics.average_grade}")
print(f"Total Submissions: {report.total_submissions}")
# See which criteria students struggled with
for w in report.weaknesses:
print(f"Weakness: {w.title} — avg {w.avg_score}/{w.max_possible}")
Inline Rubric with Assignment Content (No Template)
If you don't need a reusable template, you can pass everything in a single request:
JavaScript
const result = await client.v1.assignmentGrader.create({
title: "Pop Quiz — Chapter 3",
assignmentContent: "Chapter 3 discusses the three branches of the US government: the Legislative branch (Congress), the Executive branch (the President), and the Judicial branch (the Supreme Court). The Legislative branch makes laws, the Executive branch enforces laws, and the Judicial branch interprets laws...",
textToGrade: "1. The three branches are Legislative, Executive, and Military.\n2. Congress makes the laws.\n3. The Supreme Court enforces the laws.",
rubric: {
criteria: [
{
title: "Three Branches",
description: "Student correctly names all three branches of government",
pointsPossible: 10
},
{
title: "Legislative Role",
description: "Student explains what the Legislative branch does",
pointsPossible: 5
},
{
title: "Judicial Role",
description: "Student explains what the Judicial branch does",
pointsPossible: 5
}
]
}
});
// The AI will catch that the student said "Military" instead of "Judicial"
// and mixed up what the Supreme Court does
console.log(`Grade: ${result.gradedAssignment.grade}`);
Python
result = client.v1.assignment_grader.create({
"title": "Pop Quiz — Chapter 3",
"assignmentContent": "Chapter 3 discusses the three branches of the US government: the Legislative branch (Congress), the Executive branch (the President), and the Judicial branch (the Supreme Court). The Legislative branch makes laws, the Executive branch enforces laws, and the Judicial branch interprets laws...",
"textToGrade": "1. The three branches are Legislative, Executive, and Military.\n2. Congress makes the laws.\n3. The Supreme Court enforces the laws.",
"rubric": {
"criteria": [
{
"title": "Three Branches",
"description": "Student correctly names all three branches of government",
"pointsPossible": 10
},
{
"title": "Legislative Role",
"description": "Student explains what the Legislative branch does",
"pointsPossible": 5
},
{
"title": "Judicial Role",
"description": "Student explains what the Judicial branch does",
"pointsPossible": 5
}
]
}
})
# The AI will catch that the student said "Military" instead of "Judicial"
# and mixed up what the Supreme Court does
print(f"Grade: {result.graded_assignment.grade}")
Object Reference
RubricCriterion Object
| Field | Type | Required | Description |
|---|---|---|---|
title |
string |
Yes | Short name of the criterion. |
description |
string |
No | What you want graded. Written as instructions for the grader. |
pointsPossible |
number |
Yes | Maximum points this criterion can earn. |
Rubric Object (Request Input)
| Field | Type | Required | Description |
|---|---|---|---|
criteria |
RubricCriterion[] |
Yes | Must have ≥ 1 entry. |
assignmentContent |
string |
No | Optional source material (superseded by top-level assignmentContent). |
GradedRubricCriterion Object (Response)
| Field | Type | Description |
|---|---|---|
title |
string |
Mirrors the input criterion title. |
description |
string |
Mirrors the input criterion description. |
pointsAwarded |
number |
Points the AI awarded. |
pointsPossible |
number |
Maximum points for this criterion. |
feedback |
string |
Per-criterion feedback (second-person, addressed to the student). |
AssignmentGrader Object
| Field | Type | Description |
|---|---|---|
_id |
string (ObjectId) |
Document ID. |
title |
string |
Assignment title. |
grade |
number |
Overall percentage (0–100), rounded to nearest whole number. |
rubric.attemptFeedback |
string |
Holistic feedback for the whole submission. |
rubric.criteria |
GradedRubricCriterion[] |
Per-criterion results. |
organizationId |
string |
Owning organization. |
userId |
string | null |
User who triggered the grading (or null for API-key auth). |
materialId |
string? |
Material that was graded (mutually exclusive with textToGrade). |
textToGrade |
string? |
Raw text that was graded. |
rubricTemplateId |
string? |
Template used (if any). |
assignmentId |
string? |
Grouping tag for educator reports. |
studentIdentifier |
string? |
Student email or ID. |
assignmentContent |
string? |
Source material the AI used. |
createdAt |
Date |
Timestamp. |
updatedAt |
Date |
Timestamp. |
RubricTemplate Object
| Field | Type | Description |
|---|---|---|
_id |
string (ObjectId) |
Template ID. |
name |
string |
Template name. |
description |
string? |
Optional description. |
assignmentContent |
string? |
Default source material attached to this template. |
criteria |
RubricCriterion[] |
Grading criteria. |
organizationId |
string |
Owning organization. |
createdBy |
string |
User who created the template. |
createdAt |
Date |
Timestamp. |
updatedAt |
Date |
Timestamp. |
Error Responses
All errors follow the standard exception envelope.
| Status | When |
|---|---|
400 Bad Request |
Invalid body, missing required fields, both materialId and textToGrade supplied, neither rubric nor template provided, empty rubric, or unknown grader action. |
401 Unauthorized |
Missing or invalid x-api-key / Authorization header. |
404 Not Found |
Assignment, template, or report target not found in your organization. |
500 Internal Server Error |
Unexpected failure (AI service error, DB failure, etc.). |
Example error body:
{
"statusCode": 400,
"message": "Must provide either materialId or textToGrade",
"error": "Bad Request"
}
Tips & Best Practices
- Provide
assignmentContentwhenever possible. It dramatically improves grading accuracy because the AI grades against the actual source instead of guessing. - Keep
assignmentContentfocused. Paste the actual passage or prompt students received. Don't include instructions meant for the grader, put those in the criteriondescriptionfields instead. - Use criterion descriptions as grading instructions. For example: "Award full points only if the student names ≥2 specific dates from the passage."
- Save templates for repeated assignments. If 30 students are all answering the same reading comprehension, create a template once and reuse it with
rubricTemplateId. - Always set
assignmentIdandstudentIdentifierwhen you plan to run an educator report. WithoutassignmentId, the report can't aggregate submissions. - Top-level wins for
assignmentContent. If you need a one-off override of the template's saved content, just includeassignmentContentat the top level of the grading request. - Cap on inputs. Materials larger than ~30k chars per item are truncated for grading; very large materials (>100k chars) are also truncated when extracted.
- Strict grading. The grader awards 0 points for factually incorrect answers. Post-processing also detects keywords like "incorrect", "wrong", "opposite" in the AI's per-criterion feedback and forces
pointsAwardedto 0 (unless the feedback also indicates partial correctness with words like "partially", "mostly").
