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 toapi/v1):
https://<your-host>/api/v1/assignment-grader
http://localhost:3001/api/v1/assignment-grader
Authentication
Every endpoint is protected byFlexibleAuthGuard, 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. |
401 Unauthorized.
Every record (graded assignment, rubric template) is scoped to the authenticated
organizationId. You can only read/delete records that belong to your org.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)
// 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
# 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
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
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
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 ofmaterialIdortextToGrade. Providing both or neither returns400 Bad Request. ** You must provide at least one ofrubricorrubricTemplateId. 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
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]", // 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}`);
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")
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());
}
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
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
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}`);
});
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}")
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());
}
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
| 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
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}`);
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}")
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());
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
| 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" }
404 Not Found if the assignment doesn’t exist or belongs to a different org.
SDK Examples
const graderId = "grader_123456";
await client.v1.assignmentGrader.delete(graderId);
console.log("Assignment grader deleted successfully");
grader_id = "grader_123456"
client.v1.assignment_grader.delete(grader_id)
print("Assignment grader deleted successfully")
String graderId = "grader_123456";
client.v1().assignmentGrader().delete(graderId);
System.out.println("Assignment grader deleted successfully");
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
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. |
404 Not Found if no submissions match the assignmentId in your organization.
SDK Examples
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})`);
});
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})")
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() + ")");
}
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 anassignmentContent 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
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}`);
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}")
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());
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
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
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}`);
});
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}")
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());
}
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. |
RubricTemplate. Returns 404 Not Found if not found in your organization.
SDK Examples
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);
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}")
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());
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" }
404 Not Found if the template doesn’t exist or belongs to a different org.
SDK Examples
const templateId = "template-123";
await client.v1.assignmentGrader.rubricTemplates.delete(templateId);
console.log("Rubric template deleted successfully");
template_id = "template-123"
client.v1.assignment_grader.rubric_templates().delete(template_id)
print("Rubric template deleted successfully")
String templateId = "template-123";
client.v1().assignmentGrader().rubricTemplates().delete(templateId);
System.out.println("Rubric template deleted successfully");
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
TheassignmentContent 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
WhenassignmentContent 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)
This means you can save a default passage on a rubric template and override it per-request when needed — for example, if you update a passage slightly for one section of students.
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 |
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 inassignmentContent.
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]"
}'
Pattern 2 — Essay / Open-Ended Writing (No Source Material)
When students write an original essay (no assigned reading), you don’t needassignmentContent 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 asassignmentContent, 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
}
]
}
}'
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 }
]
}'
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."
}'
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.Important: Materials need time to process after upload. You must wait for the material to have
status: 'active' before grading it. Attempting to grade a material that is still processing will fail.Tip: Use the createAndProcess method for text materials to avoid manual waiting - it automatically waits for processing to complete.// 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]"
});
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]"
})
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);
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, usecreateAndProcess to automatically wait for processing:
// 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]"
});
# 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]"
})
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);
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 }
]
}'
_id from the response — that’s your TEMPLATE_ID.
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}`);
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 sameassignmentId.
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."
}'
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}`);
});
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}")
- 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 sameassignmentId, 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"
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}`);
});
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: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}`);
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.). |
{
"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”).