The Copyleaks Grammar Checker API is a powerful tool to help improve the quality of written content by providing detailed feedback on grammar, spelling, sentence structure, and word choice. This guide will walk you through the process of submitting documents for writing assessment and retrieving the detailed feedback.

Get started

1

Before you begin

Before you start, ensure you have the following:
2

Installation

Choose your preferred method for making API calls.
# macOS
brew install curl

# Ubuntu/Debian
sudo apt-get install curl

# Windows - download from https://curl.se
pip install copyleaks
npm install plagiarism-checker
Install-Package Copyleaks
composer require copyleaks/php-plagiarism-checker
gem install plagiarism-checker
<!-- Add to your pom.xml (requires Java 11+) -->
<dependency>
    <groupId>com.copyleaks.sdk</groupId>
    <artifactId>copyleaks-java-sdk</artifactId>
    <version>5.1.0</version>
</dependency>
HTTP needs no installation - call the API with any standard HTTP client, or import our Postman collection for a quicker start.
3

Login

To perform a scan, we first need to generate an access token. For that, we will use the login endpoint. The API key can be found on the Copyleaks API Dashboard.Upon successful authentication, you will receive a token that must be attached to subsequent API calls via the Authorization: Bearer <TOKEN> header. This token remains valid for 48 hours.
POST https://id.copyleaks.com/v3/account/login/api

Headers
Content-Type: application/json

Body
{
    "email": "[email protected]",
    "key": "00000000-0000-0000-0000-000000000000"
}
export COPYLEAKS_EMAIL="[email protected]"
export COPYLEAKS_API_KEY="your-api-key-here"

curl --request POST \
  --url https://id.copyleaks.com/v3/account/login/api \
  --header 'Accept: application/json' \
  --header 'Content-Type: application/json' \
  --data "{
    \"email\": \"${COPYLEAKS_EMAIL}\",
    \"key\": \"${COPYLEAKS_API_KEY}\"
  }"
from copyleaks.copyleaks import Copyleaks

EMAIL_ADDRESS = "[email protected]"
API_KEY = "your-api-key-here"

# Login to Copyleaks
auth_token = Copyleaks.login(EMAIL_ADDRESS, API_KEY)
print("Logged successfully!\nToken:", auth_token)
const { Copyleaks } = require("plagiarism-checker");

const EMAIL_ADDRESS = "[email protected]";
const API_KEY = "your-api-key-here";
const copyleaks = new Copyleaks();

// Login function
function loginToCopyleaks() {
  return copyleaks.loginAsync(EMAIL_ADDRESS, API_KEY).then(
    (loginResult) => {
      console.log("Login successful!");
      console.log("Access Token:", loginResult.access_token);
      return loginResult;
    },
    (err) => {
      console.error('Login failed:', err);
      throw err;
    }
  );
}

loginToCopyleaks();
import classes.Copyleaks;
import models.response.CopyleaksAuthToken;

String EMAIL_ADDRESS = "[email protected]";
String API_KEY = "00000000-0000-0000-0000-000000000000";

// Login to Copyleaks
try {
    CopyleaksAuthToken authToken = Copyleaks.login(EMAIL_ADDRESS, API_KEY);
    System.out.println("Logged in successfully!");
} catch (Exception e) {
    System.out.println("Failed to login: " + e.getMessage());
    System.exit(1);
}
Response
{
    "access_token": "<ACCESS_TOKEN>",
    ".issued": "2025-07-31T10:19:40.0690015Z",
    ".expires": "2025-08-02T10:19:40.0690016Z"
}
Save this token. It is valid for 48 hours and can be reused for subsequent API calls.
4

Submit for writing assessment

File Upload

Submit documents including PDF, DOCX, TXT, and other formats for scanning.

URL Scanning

Submit webpages and online documents directly by providing their URL.

Text from Images

Extract and scan text from images, including screenshots and photos.
For this guide, we’ll demonstrate document submission for writing assessment. Each submission requires a unique scanId for proper tracking and identification.
  • Filename: The file extension in the filename parameter must match your document type (e.g., .pdf, .docx, .txt). See the full list of supported file types.
  • Content Encoding: The file content must be Base64 encoded and sent in the base64 property.
What is Base64 Encoding?Base64 converts binary files into text strings so they can be sent via JSON. All programming languages have built-in Base64 encoding functions, see the code examples below for your language.
For testing, set "sandbox": true. Sandbox mode is free and returns mock results.
To enable writing assessment, ensure "writingFeedback": {"enable": true} is set in your properties.
PUT https://api.copyleaks.com/v3/scans/submit/file/my-writing-assessment-scan
Content-Type: application/json
Authorization: Bearer YOUR_LOGIN_TOKEN

{
  "base64": "SGVsbG8gd29ybGQuIFRoaXMgaXMgYW4gZXhhbXBsZSB0ZXh0IHRvIGJlIGNoZWNrZWQgZm9yIGdyYW1tYXIgZXJyb3JzLg==",
  "filename": "my-document.txt",
  "properties": {
    "sandbox": true,
    "webhooks": {
      "status": "https://your.server/webhook/{STATUS}"
    },
    "writingFeedback": {
      "enable": true
    }
  }
}
# First, encode your file to base64
base64_content=$(base64 -w 0 my-document.docx)

curl -X PUT "https://api.copyleaks.com/v3/scans/submit/file/my-writing-assessment-scan" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_LOGIN_TOKEN" \
  -d '{
    "base64": "'$base64_content'",
    "filename": "my-document.docx",
    "properties": {
      "sandbox": true,
      "webhooks": {
        "status": "https://your.server/webhook/{STATUS}"
      },
      "writingFeedback": {
        "enable": true
      }
    }
  }'
import base64
import random
from copyleaks.copyleaks import Copyleaks
from copyleaks.models.submit.document import FileDocument
from copyleaks.models.submit.properties.scan_properties import ScanProperties
from copyleaks.models.submit.properties.submit_webhooks import SubmitWebhooks
from copyleaks.models.submit.properties.writing_feedback import WritingFeedback

print("Submitting a document for writing assessment...")

# Read and encode the file
with open('my-document.docx', 'rb') as file:
    file_content = file.read()
    base64_file_content = base64.b64encode(file_content).decode('utf8')

# Generate a random scan ID or use a meaningful identifier
scan_id = f"writing-scan-{random.randint(100, 100000)}"

# Create a file submission
file_submission = FileDocument(base64_file_content, "my-document.docx")

# Configure Grammar Checker
writing_feedback = WritingFeedback()
writing_feedback.enable = True

# Configure webhooks for notifications
webhooks = SubmitWebhooks()
webhooks.set_status('https://your.server/webhook/{STATUS}')

# Set up scan properties
scan_properties = ScanProperties(status_webhook='https://your.server/webhook/{STATUS}')
scan_properties.set_webhooks(webhooks)
scan_properties.set_writing_feedback(writing_feedback)
scan_properties.set_sandbox(True)  # Turn on sandbox mode. Turn off on production.

# Add properties to the submission
file_submission.set_properties(scan_properties)

# Submit the document for assessment
Copyleaks.submit_file(auth_token, scan_id, file_submission)
print("Document sent for writing assessment")
print("You will be notified via webhook when the assessment completes.")
const { Copyleaks, CopyleaksFileSubmissionModel } = require('plagiarism-checker');
const fs = require('fs');

async function submitForWritingAssessment() {
  try {
    // Read the file and encode it as base64
    const fileContent = fs.readFileSync('my-document.docx');
    const base64Content = fileContent.toString('base64');
    
    // Generate a unique scan ID
    const scanId = `writing-scan-${Date.now()}`;
    
    // Create the submission properties
    const properties = {
      sandbox: true,  // Set to false in production
      webhooks: {
        status: 'https://your.server/webhook/{STATUS}'
      },
      writingFeedback: {
        enable: true,
        // Optionally customize scoring weights
        score: {
          grammarScoreWeight: 1.0,
          mechanicsScoreWeight: 1.0,
          sentenceStructureScoreWeight: 1.0,
          wordChoiceScoreWeight: 1.0
        }
      }
    };
    
    // Create the submission model
    const submission = new CopyleaksFileSubmissionModel(
      base64Content,
      'my-document.docx',
      properties
    );
    
    // Get authentication token
    const loginResult = await Copyleaks.login(EMAIL, API_KEY);
    const authToken = loginResult.access_token;
    
    // Submit the document for assessment
    await Copyleaks.submitFile(authToken, scanId, submission);
    console.log(`Document submitted for writing assessment with scan ID: ${scanId}`);
    console.log('You will be notified via webhook when the assessment completes.');
  } catch (error) {
    console.error('Error submitting document:', error);
  }
}

submitForWritingAssessment();
import classes.Copyleaks;
import models.submissions.CopyleaksFileSubmissionModel;
import models.submissions.properties.*;
import java.util.Base64;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.io.IOException;

public class WritingAssessmentExample {
    public static void main(String[] args) {
        try {
            // Read the file and encode it as base64
            byte[] fileBytes = Files.readAllBytes(Paths.get("my-document.docx"));
            String base64Content = Base64.getEncoder().encodeToString(fileBytes);
            
            // Set scan ID
            String scanId = "writing-scan-" + System.currentTimeMillis();
            
            // Configure webhooks
            SubmissionWebhooks webhooks = new SubmissionWebhooks("https://your.server/webhook/{STATUS}");
            
            // Create submission properties
            SubmissionProperties properties = new SubmissionProperties(webhooks);
            properties.setSandbox(true);  // Set to false in production
            
            // Enable writing feedback
            WritingFeedback writingFeedback = new WritingFeedback();
            writingFeedback.setEnable(true);
            properties.setWritingFeedback(writingFeedback);
            
            // Create and submit the file
            CopyleaksFileSubmissionModel submission = new CopyleaksFileSubmissionModel(
                base64Content, 
                "my-document.docx", 
                properties
            );
            
            // Submit the document
            Copyleaks.submitFile(authToken, scanId, submission);
            System.out.println("Document sent for writing assessment with scan ID: " + scanId);
            System.out.println("You will be notified via webhook when the assessment completes.");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
5

Wait for the completion webhook

Once the scan is complete, Copyleaks will send a completed webhook to the status URL you provided.When Grammar Checker is enabled, the webhook response will include a writingFeedback section with detailed information about the writing quality.
For complete details on the webhook response structure, see the Scan Completed Webhook Reference.
6

Interpreting writing assessment results

The completed webhook contains a writingFeedback object with the Correction Types:
  1. textStatistics: Basic metrics about the text, including sentence count, average word and sentence length, and estimated reading time.
  2. score: Detailed breakdown of writing quality across four categories:
    • Grammar
    • Mechanics (spelling, punctuation)
    • Sentence Structure
    • Word Choice
    Each category includes both a count of corrections and a score (0-100).
  3. readability: An assessment of how easy the text is to read, including:
    • Overall readability score (0-100)
    • Readability level (grade level)
    • Text description of the readability
7

Export detailed results

To get the specific writing corrections, you need to export the detailed results using the export endpoint:
POST https://api.copyleaks.com/v3/downloads/my-writing-assessment-scan/export/my-export-1
Content-Type: application/json
Authorization: Bearer YOUR_LOGIN_TOKEN

{
  "writingFeedback": {
    "corrections": true,
    "verb": "POST",
    "headers": [
      ["Content-Type", "application/json"]
    ],
    "endpoint": "https://your.server/export/writing-feedback"
  },
  "completionWebhook": "https://your.server/webhook/export/completion",
  "maxRetries": 3,
  "developerPayload": "writing-assessment-export"
}
curl -X POST "https://api.copyleaks.com/v3/downloads/my-writing-assessment-scan/export/my-export-1" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_LOGIN_TOKEN" \
  -d '{
    "writingFeedback": {
      "corrections": true,
      "verb": "POST",
      "headers": [
        ["Content-Type", "application/json"]
      ],
      "endpoint": "https://your.server/export/writing-feedback"
    },
    "completionWebhook": "https://your.server/webhook/export/completion",
    "maxRetries": 3,
    "developerPayload": "writing-assessment-export"
  }'
from copyleaks.copyleaks import Copyleaks
from copyleaks.models.export import Export, ExportResults, WritingFeedbackExport
from copyleaks.models.export.http_method import HttpMethod
from copyleaks.models.export.endpoint import Endpoint

# Set up the export
export_id = "my-export-1"

# Configure what to export
export = Export()
export.set_completion_webhook('https://your.server/webhook/export/completion')

# Add optional parameters
export.set_max_retries(3)  # Default is 3, can be 1-12
export.set_developer_payload("writing-assessment-export")  # Custom identifier

# Request writing feedback corrections with endpoint
writing_feedback = WritingFeedbackExport()
writing_feedback.set_corrections(True)
writing_feedback.set_endpoint(Endpoint(
    HttpMethod.POST, 
    "https://your.server/export/writing-feedback",
    [["Content-Type", "application/json"]]
))
export.set_writing_feedback(writing_feedback)

# Start the export process
Copyleaks.export(auth_token, scan_id, export_id, export)
print(f"Export requested with ID: {export_id}")
print("You will be notified via webhook when the export is ready.")
const { Copyleaks, CopyleaksExportModel } = require('plagiarism-checker');

async function exportWritingFeedback() {
  try {
    const scanId = 'my-writing-assessment-scan';
    const exportId = 'my-export-1';
    
    // Configure the export
    const exportRequest = {
      writingFeedback: {
        corrections: true,
        verb: "POST",
        headers: [
          ["Content-Type", "application/json"]
        ],
        endpoint: "https://your.server/export/writing-feedback"
      },
      completionWebhook: 'https://your.server/webhook/export/completion',
      maxRetries: 3,  // Default is 3, can be 1-12
      developerPayload: "writing-assessment-export"  // Custom identifier
    };
    
    // Get authentication token
    const loginResult = await Copyleaks.login(EMAIL, API_KEY);
    const authToken = loginResult.access_token;
    
    // Request the export
    await Copyleaks.export(authToken, scanId, exportId, exportRequest);
    console.log(`Export requested with ID: ${exportId}`);
    console.log('You will be notified via webhook when the export is ready.');
  } catch (error) {
    console.error('Error exporting results:', error);
  }
}

exportWritingFeedback();
import classes.Copyleaks;
import models.exports.*;
import java.util.ArrayList;
import java.util.List;

public class ExportWritingFeedbackExample {
    public static void main(String[] args) {
        try {
            String scanId = "my-writing-assessment-scan";
            String exportId = "my-export-1";
            
            // Create headers for endpoints
            List<EndpointHeader> headers = new ArrayList<>();
            headers.add(new EndpointHeader("Content-Type", "application/json"));
            
            // Configure writing feedback export with endpoint
            WritingFeedbackExport writingFeedback = new WritingFeedbackExport();
            writingFeedback.setCorrections(true);
            writingFeedback.setEndpoint(
                new ExportEndpoint(
                    HttpMethod.POST,
                    "https://your.server/export/writing-feedback",
                    headers
                )
            );
            
            // Create complete export request
            CopyleaksExportModel export = new CopyleaksExportModel();
            export.setCompletionWebhook("https://your.server/webhook/export/completion");
            export.setWritingFeedback(writingFeedback);
            
            // Add optional parameters
            export.setMaxRetries(3);  // Default is 3, can be 1-12
            export.setDeveloperPayload("writing-assessment-export");  // Custom identifier
            
            // Request the export
            Copyleaks.export(authToken, scanId, exportId, export);
            System.out.println("Export requested with ID: " + exportId);
            System.out.println("You will be notified via webhook when the export is ready.");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
8

Summary

You have successfully submitted a document for writing assessment and exported the detailed correction recommendations. You can now integrate these corrections into your application, display them to users, or use them to improve the document’s quality.

Next steps

API Reference

View the complete API reference for the Grammar Checker endpoints.

Grammar Checker Response

Learn about the detailed structure of Grammar Checker data, including corrections and scores.

Ways to Integrate the Report

Explore different ways to integrate writing feedback into your applications.

Learn About Webhooks

Understand how to use webhooks to receive notifications about scan completions and results.