grammarly-upgrade-migration

'Upgrade and migration guidance for Grammarly API version changes. Use

5 Tools
grammarly-pack Plugin
saas packs Category

Allowed Tools

ReadWriteEditBash(curl:*)Grep

Provided by Plugin

grammarly-pack

Claude Code skill pack for Grammarly (24 skills)

saas packs v1.0.0
View Plugin

Installation

This skill is included in the grammarly-pack plugin:

/plugin install grammarly-pack@claude-code-plugins-plus

Click to copy

Instructions

Grammarly Upgrade & Migration

Overview

Grammarly exposes versioned APIs for writing analysis, AI detection, and plagiarism checking. Each API family versions independently — the Writing Score API may be on v2 while AI Detection remains on v1. Tracking these versions matters because Grammarly deprecates older API versions on a fixed timeline, and the response schemas differ significantly between versions (score breakdowns, suggestion categories, and confidence thresholds all change). Missing a version cutover means silent failures or degraded writing feedback quality.

Version Detection


const GRAMMARLY_BASE = "https://api.grammarly.com/ecosystem/api";

interface GrammarlyVersionMap {
  scores: string;
  aiDetection: string;
  plagiarism: string;
  latestAvailable: Record<string, string>;
}

async function detectGrammarlyVersions(apiKey: string): Promise<GrammarlyVersionMap> {
  const endpoints = {
    scores: `${GRAMMARLY_BASE}/v2/scores`,
    aiDetection: `${GRAMMARLY_BASE}/v1/ai-detection`,
    plagiarism: `${GRAMMARLY_BASE}/v1/plagiarism`,
  };

  const versions: Record<string, string> = {};
  for (const [api, url] of Object.entries(endpoints)) {
    const res = await fetch(url, {
      method: "HEAD",
      headers: { Authorization: `Bearer ${apiKey}` },
    });
    versions[api] = res.headers.get("x-grammarly-api-version") ?? "unknown";
    const deprecated = res.headers.get("x-grammarly-deprecated");
    if (deprecated) console.warn(`${api} endpoint deprecated: ${deprecated}`);
  }
  return { scores: "v2", aiDetection: "v1", plagiarism: "v1", latestAvailable: versions };
}

Migration Checklist

  • [ ] Audit codebase for all api.grammarly.com endpoint references
  • [ ] Map each endpoint to its current API version (v1 vs. v2)
  • [ ] Check if Writing Score v2 response includes new sub-score categories
  • [ ] Verify AI Detection confidence threshold changes between versions
  • [ ] Update plagiarism check response parser if source attribution format changed
  • [ ] Test OAuth token flow — scope names may differ between API versions
  • [ ] Validate suggestion category enums (clarity, engagement, delivery, correctness)
  • [ ] Update error handling for new rate limit headers in v2
  • [ ] Check if text submission size limits changed between versions
  • [ ] Run A/B comparison of v1 vs. v2 scores on sample corpus to verify parity

Schema Migration


// Grammarly scores response changed from flat scores to structured breakdown
interface OldScoreResponse {
  overall: number;
  correctness: number;
  clarity: number;
  engagement: number;
  delivery: number;
}

interface NewScoreResponse {
  overall: { score: number; label: string };
  dimensions: Array<{
    name: "correctness" | "clarity" | "engagement" | "delivery";
    score: number;
    label: string;
    suggestions: Array<{ category: string; message: string; severity: "critical" | "warning" | "info" }>;
  }>;
  metadata: { wordCount: number; readingTime: number; apiVersion: string };
}

function migrateScoreResponse(old: OldScoreResponse): NewScoreResponse {
  const toLabel = (s: number) => (s >= 80 ? "Strong" : s >= 60 ? "Good" : "Needs work");
  return {
    overall: { score: old.overall, label: toLabel(old.overall) },
    dimensions: (["correctness", "clarity", "engagement", "delivery"] as const).map((name) => ({
      name,
      score: old[name],
      label: toLabel(old[name]),
      suggestions: [],
    })),
    metadata: { wordCount: 0, readingTime: 0, apiVersion: "v1-migrated" },
  };
}

Rollback Strategy


class GrammarlyClient {
  private versionMap: Record<string, string> = { scores: "v2", aiDetection: "v1", plagiarism: "v1" };
  private fallbackMap: Record<string, string> = { scores: "v1", aiDetection: "v1", plagiarism: "v1" };

  constructor(private apiKey: string) {}

  async checkText(text: string, api: "scores" | "aiDetection" | "plagiarism"): Promise<any> {
    const version = this.versionMap[api];
    try {
      const res = await fetch(`https://api.grammarly.com/ecosystem/api/${version}/${api}`, {
        method: "POST",
        headers: { Authorization: `Bearer ${this.apiKey}`, "Content-Type": "application/json" },
        body: JSON.stringify({ text }),
      });
      if (!res.ok) throw new Error(`Grammarly ${api} ${res.status}`);
      return await res.json();
    } catch (err) {
      const fallback = this.fallbackMap[api];
      if (fallback !== version) {
        console.warn(`Falling back ${api} from ${version} to ${fallback}`);
        this.versionMap[api] = fallback;
        return this.checkText(text, api);
      }
      throw err;
    }
  }
}

Error Handling

Migration Issue Symptom Fix
Score dimension renamed Response missing engagement, has tone instead Update parser to map new dimension names to internal schema
API version sunset 410 Gone on v1 scores endpoint Migrate all calls to v2 and update response parsing
OAuth scope mismatch 403 after upgrading API version Re-authorize with scopes matching new version requirements
Text length limit changed 413 Payload Too Large on previously working requests Chunk text into smaller segments per new version limits
Rate limit structure changed 429 without X-RateLimit-Reset header Switch to Retry-After header or implement exponential backoff

Resources

Next Steps

For CI pipeline integration, see grammarly-ci-integration.

Ready to use grammarly-pack?