Skip to content
StudyFetch API

API References

Assignment Grader

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.

Watch the demo video

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 materialId or textToGrade. Providing both or neither returns 400 Bad Request.

** You must provide at least one of rubric or rubricTemplateId. An inline rubric must 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:

  1. Top-level assignmentContent on the grading request (highest priority)
  2. assignmentContent inside the rubric object on the grading request
  3. assignmentContent saved on the rubric template (if rubricTemplateId is 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]"
        });
    }
}

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:

  1. Reads the rubric criteria (questions + what to look for)
  2. Reads the assignment content (the water cycle passage) as the source of truth
  3. Reads the student's submission (their answers)
  4. Grades each answer against the passage, not against its own knowledge
  5. 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 assignmentContent whenever possible. It dramatically improves grading accuracy because the AI grades against the actual source instead of guessing.
  • Keep assignmentContent focused. Paste the actual passage or prompt students received. Don't include instructions meant for the grader, put those in the criterion description fields 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 assignmentId and studentIdentifier when you plan to run an educator report. Without assignmentId, 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 include assignmentContent at 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 pointsAwarded to 0 (unless the feedback also indicates partial correctness with words like "partially", "mostly").