Zero suggest
Deploy Grammarly integrations to Vercel, Fly.
ReadWriteEditBash(vercel:*)Bash(fly:*)Bash(gcloud:*)
Grammarly Deploy Integration
Overview
Deploy a containerized Grammarly writing assistant integration service with Docker. This skill covers building a production image that connects to the Grammarly Text API for checking grammar, clarity, and tone across submitted text. Includes environment configuration for OAuth client credentials, health checks that verify API token exchange and text analysis endpoints, and rolling update strategies for zero-downtime deployments serving real-time writing feedback.
Docker Configuration
FROM node:20-slim AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY tsconfig.json ./
COPY src/ ./src/
RUN npm run build
FROM node:20-slim
RUN addgroup --system app && adduser --system --ingroup app app
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
COPY package*.json ./
USER app
EXPOSE 3000
HEALTHCHECK --interval=30s --timeout=5s --retries=3 \
CMD curl -f http://localhost:3000/health || exit 1
CMD ["node", "dist/index.js"]
Environment Variables
export GRAMMARLY_CLIENT_ID="client_xxxxxxxxxxxx"
export GRAMMARLY_CLIENT_SECRET="secret_xxxxxxxxxxxx"
export GRAMMARLY_BASE_URL="https://api.grammarly.com/ecosystem/api"
export LOG_LEVEL="info"
export PORT="3000"
export NODE_ENV="production"
Health Check Endpoint
import express from 'express';
const app = express();
app.get('/health', async (req, res) => {
try {
const tokenRes = await fetch(`${process.env.GRAMMARLY_BASE_URL}/v1/oauth/token`, {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
grant_type: 'client_credentials',
client_id: process.env.GRAMMARLY_CLIENT_ID!,
client_secret: process.env.GRAMMARLY_CLIENT_SECRET!,
}),
});
if (!tokenRes.ok) throw new Error(`Grammarly OAuth returned ${tokenRes.status}`);
res.json({ status: 'healthy', service: 'grammarly-integration', timestamp: new Date().toISOString() });
} catch (error) {
res.status(503).json({ status: 'unhealthy', error: (error as Error).message });
}
});
Deployment Steps
Step 1: Build
docker build -t grammarly-integration:latest .
Step 2: Run
docker run -d --name grammarly-integration \
-p 3000:3000 \
-e GRAMMARLY_CLIENT_ID -e GRAMMARLY_CLIENT_SECRET -e GRAMMARLY_BASE_URL \
grammarly-integration:latest
Step 3: Verify
curl -s http://localhost:3000/health | jq .
Step 4: Rolling Update
Configure Grammarly enterprise role-based access control.
ReadWriteGrep
Grammarly Enterprise RBAC
Overview
Grammarly enterprise deployments manage writing quality across teams with different access levels. Organization admins control style guides, tone profiles, and brand rules. Team admins assign seats and configure suggestion types (clarity, engagement, delivery). Members write with team defaults while guests get read-only access to shared documents. HIPAA and SOC 2 compliance in regulated industries require audit trails on who accessed which writing suggestions and AI detection results.
Role Hierarchy
| Role |
Permissions |
Scope |
| Org Admin |
Manage billing, SSO config, all style guides, API credentials |
Organization-wide |
| Team Admin |
Assign seats, configure suggestion settings, manage style guides |
Own team |
| Member |
Write with team settings, access scoring and AI detection APIs |
Own team |
| Guest |
View shared documents, read-only style guide access |
Invited documents only |
| API Service |
OAuth-scoped access to scoring, AI detection, plagiarism APIs |
Per-credential scope |
Permission Check
async function checkGrammarlyAccess(userId: string, team: string, scope: string): Promise<boolean> {
const response = await fetch(`${GRAMMARLY_API}/organizations/${ORG_ID}/permissions`, {
headers: { Authorization: `Bearer ${GRAMMARLY_OAUTH_TOKEN}`, 'Content-Type': 'application/json' },
});
const perms = await response.json();
const userPerms = perms.members.find((m: any) => m.id === userId);
if (!userPerms) return false;
return userPerms.team === team && userPerms.scopes.includes(scope);
}
Role Assignment
async function assignTeamRole(email: string, team: string, role: 'admin' | 'member' | 'guest'): Promise<void> {
await fetch(`${GRAMMARLY_API}/organizations/${ORG_ID}/teams/${team}/members`, {
method: 'POST',
headers: { Authorization: `Bearer ${GRAMMARLY_OAUTH_TOKEN}`, 'Content-Type': 'application/json' },
body: JSON.stringify({ email, role }),
});
}
async function revokeTeamAccess(email: string, team: string): Promise<void> {
await fetch(`${GRAMMARLY_API}/organizations/${ORG_ID}/teams/${team}/members/${email}`, {
method: 'DELETE',
headers: { Authorization: `Bearer ${GRAMMARLY_OAUTH_TOKEN}` },
});
}
Audit Logging
interface GrammarlyAuditEntry {
timestamp: string; userId: string; team: string;
action: 'score_check' | 'ai_detection' | 'plagiarism_scan' | 'style_guide_edit' | 'seat_change';
scope: string; docume
Create a minimal working Grammarly example.
ReadWriteEditBash(npm:*)Bash(curl:*)
Grammarly Hello World
Overview
Score a text document using Grammarly's Writing Score API. Returns an overall score plus sub-scores for engagement, correctness, tone, and clarity.
Prerequisites
- Completed
grammarly-install-auth setup
- Valid access token with
scores-api:read scope
Instructions
Step 1: Score a Document
// hello-grammarly.ts
import 'dotenv/config';
const TOKEN = process.env.GRAMMARLY_ACCESS_TOKEN!;
async function scoreText(text: string) {
const response = await fetch('https://api.grammarly.com/ecosystem/api/v2/scores', {
method: 'POST',
headers: {
'Authorization': `Bearer ${TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ text }),
});
const result = await response.json();
console.log('Overall Score:', result.overallScore);
console.log('Engagement:', result.engagement);
console.log('Correctness:', result.correctness);
console.log('Clarity:', result.clarity);
console.log('Tone:', result.tone);
return result;
}
scoreText('Their going to the store tommorow to buy there supplys for the trip.').catch(console.error);
Step 2: AI Detection
async function detectAI(text: string) {
const response = await fetch('https://api.grammarly.com/ecosystem/api/v1/ai-detection', {
method: 'POST',
headers: {
'Authorization': `Bearer ${TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ text }),
});
const result = await response.json();
console.log('AI Score:', result.score); // 0-100, higher = more likely AI
return result;
}
Step 3: Plagiarism Detection
async function checkPlagiarism(text: string) {
// Step 1: Create score request
const createRes = await fetch('https://api.grammarly.com/ecosystem/api/v1/plagiarism', {
method: 'POST',
headers: { 'Authorization': `Bearer ${TOKEN}`, 'Content-Type': 'application/json' },
body: JSON.stringify({ text }),
});
const { id } = await createRes.json();
// Step 2: Poll for results
let result;
do {
await new Promise(r => setTimeout(r, 3000));
const statusRes = await fetch(`https://api.grammarly.com/ecosystem/api/v1/plagiarism/${id}`, {
headers: { 'Authorization': `Bearer ${TOKEN}` },
});
result = await statusRes.json();
} while (result.status === 'pending');
console.log('Plagiarism score:', result.score);
console.log('Matches:', result.matches?.length || 0);
return result;
}
Step 4: curl Quick Test
curl -X POST https://a
Follow Grammarly incident response runbook for API outages.
ReadBash(curl:*)Grep
Grammarly Incident Runbook
Overview
Incident response procedures for Grammarly writing API integration failures. Covers text check timeouts, suggestion quality degradation, OAuth token failures, and rate limit storms. Grammarly powers real-time writing assistance, so API incidents directly impact user-facing text checking, scoring workflows, and content quality pipelines. Classify severity immediately using the matrix below and follow the corresponding playbook.
Severity Levels
| Level |
Definition |
Response Time |
Example |
| P1 - Critical |
Full API outage, all scoring requests fail |
15 min |
5xx on /v2/scores for all requests |
| P2 - High |
OAuth token failures or sustained timeouts |
30 min |
All authenticated requests return 401 |
| P3 - Medium |
Rate limit storms or elevated latency |
2 hours |
429 responses, scoring takes 10s+ per request |
| P4 - Low |
Suggestion quality drift or single endpoint issue |
8 hours |
Scores returning but correctness values seem off |
Diagnostic Steps
# Test API health (unauthenticated)
curl -s -o /dev/null -w "HTTP %{http_code}\n" \
https://api.grammarly.com/ecosystem/api/v2/scores
# Test authenticated scoring
curl -s -w "\nHTTP %{http_code}\n" \
-H "Authorization: Bearer $GRAMMARLY_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-X POST https://api.grammarly.com/ecosystem/api/v2/scores \
-d '{"text": "Test sentence for Grammarly API diagnostic health check."}'
# Check OAuth token validity
curl -s -o /dev/null -w "HTTP %{http_code}\n" \
-H "Authorization: Bearer $GRAMMARLY_ACCESS_TOKEN" \
https://api.grammarly.com/ecosystem/api/v2/account
Incident Playbooks
API Outage
- Confirm with unauthenticated health check (see diagnostics)
- Check Grammarly status page and developer announcements
- Activate fallback mode — return placeholder scores to avoid blocking users
- Queue text submissions for retry when API recovers
- Notify downstream consumers that scores are unavailable
Authentication Failure
- Test token validity with the account endpoint diagnostic above
- If 401: OAuth access token has expired — trigger token refresh flow
- If refresh token also fails: re-authorize via OAuth consent flow
- Verify client ID and client secret are correct in environment config
- Deploy refreshed tokens and confirm scoring requests succeed
Data Sync Failure
- Identify if scoring results are stale or inconsistent across requests
- Check if Grammarly upda
Install and configure Grammarly SDK/CLI authentication.
ReadWriteEditBash(npm:*)Bash(curl:*)Grep
Grammarly Install & Auth
Overview
Configure Grammarly API authentication using OAuth 2.0 Bearer tokens. Grammarly provides three main APIs: Writing Score API, AI Detection API, and Plagiarism Detection API. All use the same auth pattern via api.grammarly.com.
Prerequisites
- Grammarly Enterprise account with API access
- OAuth credentials from Grammarly admin portal
- Required scopes:
scores-api:read, scores-api:write
Instructions
Step 1: Configure Environment
# .env (NEVER commit)
GRAMMARLY_CLIENT_ID=your_client_id
GRAMMARLY_CLIENT_SECRET=your_client_secret
GRAMMARLY_ACCESS_TOKEN= # Populated after OAuth
Step 2: Obtain Access Token
// auth.ts
import 'dotenv/config';
async function getAccessToken(): Promise<string> {
const response = await fetch('https://api.grammarly.com/ecosystem/api/v1/oauth/token', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
grant_type: 'client_credentials',
client_id: process.env.GRAMMARLY_CLIENT_ID!,
client_secret: process.env.GRAMMARLY_CLIENT_SECRET!,
}),
});
const { access_token, expires_in } = await response.json();
console.log(`Token obtained, expires in ${expires_in}s`);
return access_token;
}
Step 3: Verify Connection
async function verify(token: string) {
const response = await fetch('https://api.grammarly.com/ecosystem/api/v2/scores', {
method: 'POST',
headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' },
body: JSON.stringify({ text: 'This is a test sentence for verification.' }),
});
if (response.ok) console.log('Grammarly API connection verified');
else console.error('Verification failed:', response.status);
}
Error Handling
| Error |
Cause |
Solution |
401 Unauthorized |
Invalid or expired token |
Re-authenticate |
403 Forbidden |
Missing API scopes |
Check enterprise admin settings |
invalid_client |
Wrong credentials |
Verify client ID and secret |
Resources
Next Steps
After auth, proceed to grammarly-hello-world.
Configure Grammarly local development with hot reload and testing.
ReadWriteEditBash(npm:*)Bash(pnpm:*)Grep
Grammarly Local Dev Loop
Overview
Set up a development workflow for Grammarly API integrations with mocked responses and vitest.
Instructions
Step 1: Project Structure
grammarly-integration/
├── src/grammarly/
│ ├── client.ts # API client with token management
│ ├── scoring.ts # Writing Score API
│ ├── detection.ts # AI + Plagiarism detection
│ └── types.ts # TypeScript interfaces
├── tests/
│ ├── fixtures/ # Mock API responses
│ └── scoring.test.ts
├── .env.local
└── package.json
Step 2: Mocked Tests
import { describe, it, expect, vi } from 'vitest';
const mockFetch = vi.fn();
vi.stubGlobal('fetch', mockFetch);
describe('Writing Score', () => {
it('should return scores for valid text', async () => {
mockFetch.mockResolvedValueOnce({
ok: true,
json: async () => ({ overallScore: 85, engagement: 80, correctness: 90, clarity: 85, tone: 82 }),
});
// Test scoring logic
});
it('should reject text under 30 words', async () => {
mockFetch.mockResolvedValueOnce({ ok: false, status: 400, text: async () => 'Text too short' });
// Test error handling
});
});
Resources
Next Steps
See grammarly-sdk-patterns for production patterns.
Deep dive into Grammarly API migration patterns.
ReadWriteEditBash(npm:*)Bash(git:*)
Grammarly Migration Deep Dive
Overview
The Grammarly Text Editor SDK was deprecated January 2024. Current APIs are Writing Score (v2), AI Detection (v1), and Plagiarism Detection (v1). This skill covers migrating from the deprecated SDK to the current REST APIs.
Migration: Text Editor SDK to REST APIs
Before (Deprecated SDK)
<!-- The deprecated approach embedded Grammarly in text editors -->
<script src="https://cdn.grammarly.com/grammarly-sdk.js"></script>
<grammarly-editor-plugin client-id="YOUR_ID">
<textarea></textarea>
</grammarly-editor-plugin>
After (Current REST APIs)
// Server-side scoring replaces client-side editor integration
async function scoreContent(text: string) {
const token = await getAccessToken();
const response = await fetch('https://api.grammarly.com/ecosystem/api/v2/scores', {
method: 'POST',
headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' },
body: JSON.stringify({ text }),
});
return response.json();
}
Key Differences
| Feature |
Deprecated SDK |
Current API |
| Execution |
Client-side |
Server-side |
| Real-time suggestions |
Yes |
No |
| Writing scores |
No |
Yes |
| AI detection |
No |
Yes |
| Plagiarism detection |
No |
Yes |
Resources
Configure Grammarly across multiple environments.
ReadWriteEditGrep
Grammarly Multi-Environment Setup
Overview
Grammarly integration requires environment separation to isolate API credentials, enforce team-scoped style guides, and manage rate limits per tier. Development uses mock API responses for fast iteration without consuming quota, staging connects to the Grammarly sandbox for real grammar checks with test content, and production runs against the live API with full team configurations. Each environment has its own client credentials and style guide settings to prevent dev experiments from affecting production writing standards.
Environment Configuration
const grammarlyConfig = (env: string) => ({
development: {
clientId: process.env.GRAMMARLY_DEV_CLIENT_ID!, clientSecret: process.env.GRAMMARLY_DEV_CLIENT_SECRET!,
baseUrl: "http://localhost:3100/mock-grammarly", useMockApi: true, concurrency: 1, intervalCap: 2,
},
staging: {
clientId: process.env.GRAMMARLY_STG_CLIENT_ID!, clientSecret: process.env.GRAMMARLY_STG_CLIENT_SECRET!,
baseUrl: "https://api.grammarly.com/sandbox", useMockApi: false, concurrency: 2, intervalCap: 5,
},
production: {
clientId: process.env.GRAMMARLY_PROD_CLIENT_ID!, clientSecret: process.env.GRAMMARLY_PROD_CLIENT_SECRET!,
baseUrl: "https://api.grammarly.com", teamId: process.env.GRAMMARLY_TEAM_ID!, concurrency: 5, intervalCap: 10,
},
}[env]);
Environment Files
# Per-env files: .env.development, .env.staging, .env.production
GRAMMARLY_{DEV|STG|PROD}_CLIENT_ID=<client-id>
GRAMMARLY_{DEV|STG|PROD}_CLIENT_SECRET=<secret>
GRAMMARLY_BASE_URL={http://localhost:3100/mock|https://api.grammarly.com/sandbox|https://api.grammarly.com}
GRAMMARLY_TEAM_ID=<team-id> # staging + production only
Environment Validation
function validateGrammarlyEnv(env: string): void {
const suffix = { development: "_DEV", staging: "_STG", production: "_PROD" }[env];
const required = [`GRAMMARLY${suffix}_CLIENT_ID`, `GRAMMARLY${suffix}_CLIENT_SECRET`];
if (env === "production") required.push("GRAMMARLY_TEAM_ID");
const missing = required.filter((k) => !process.env[k]);
if (missing.length) throw new Error(`Missing Grammarly vars for ${env}: ${missing.join(", ")}`);
}
Promotion Workflow
# 1. Verify mock API coverage in dev
npm test -- --grep "grammarly" --env development
# 2. Run style guide checks against Grammarly sandbox
curl -X POST "https://api.grammarly.com/sandbox/check" \
-H "Authorization: Bearer $GRAMMARLY_STG_TOKEN" -d @test-content.json
# 3. Compare suggestion quality between staging and baseline
node scripts/grammarly-diff.js --env staging --baseline expected-suggestions.json
# 4. Rota
Implement Grammarly observability with metrics and logging.
ReadWriteEdit
Grammarly Observability
Overview
Grammarly API integrations process user text through scoring, AI rewriting, and plagiarism endpoints where latency and accuracy directly affect user experience. Monitor text check response times, suggestion quality signals, API error rates, and token consumption to stay within rate limits. Catching degradation early prevents users from seeing stale suggestions or silent failures in real-time editing flows.
Key Metrics
| Metric |
Type |
Target |
Alert Threshold |
| Text check latency p95 |
Histogram |
< 300ms |
> 800ms |
| API error rate |
Gauge |
< 1% |
> 5% |
| Suggestion acceptance rate |
Gauge |
> 40% |
< 20% (quality signal) |
| Token usage (daily) |
Counter |
< 80% quota |
> 90% quota |
| Plagiarism check latency |
Histogram |
< 2s |
> 5s |
| AI rewrite throughput |
Counter |
Stable |
Drop > 30% |
Instrumentation
async function trackGrammarlyCall(api: 'score' | 'ai' | 'plagiarism', textLen: number, fn: () => Promise<any>) {
const start = Date.now();
try {
const result = await fn();
metrics.histogram('grammarly.api.latency', Date.now() - start, { api });
metrics.increment('grammarly.api.calls', { api });
metrics.gauge('grammarly.text.length', textLen, { api });
return result;
} catch (err) {
metrics.increment('grammarly.api.errors', { api, status: err.status });
throw err;
}
}
Health Check Dashboard
async function grammarlyHealth(): Promise<Record<string, string>> {
const latencyP95 = await metrics.query('grammarly.api.latency', 'p95', '5m');
const errorRate = await metrics.query('grammarly.api.error_rate', 'avg', '5m');
const quotaUsed = await grammarlyAdmin.getQuotaUsage();
return {
api_latency: latencyP95 < 300 ? 'healthy' : 'slow',
error_rate: errorRate < 0.01 ? 'healthy' : 'degraded',
quota: quotaUsed < 0.8 ? 'healthy' : 'at_risk',
};
}
Alerting Rules
const alerts = [
{ metric: 'grammarly.api.latency_p95', condition: '> 800ms', window: '10m', severity: 'warning' },
{ metric: 'grammarly.api.error_rate', condition: '> 0.05', window: '5m', severity: 'critical' },
{ metric: 'grammarly.quota.daily_pct', condition: '> 0.90', window: '1h', severity: 'warning' },
{ metric: '
Optimize Grammarly API performance with caching, batching, and connection pooling.
ReadWriteEdit
Grammarly Performance Tuning
Latency Benchmarks
| API |
Typical Latency |
Notes |
| Writing Score |
1-3s |
Depends on text length |
| AI Detection |
1-2s |
Fast for short text |
| Plagiarism |
10-60s |
Async, requires polling |
Instructions
Cache Score Results
import { LRUCache } from 'lru-cache';
import { createHash } from 'crypto';
const scoreCache = new LRUCache<string, any>({ max: 500, ttl: 3600000 });
async function cachedScore(text: string, token: string) {
const key = createHash('sha256').update(text).digest('hex');
const cached = scoreCache.get(key);
if (cached) return cached;
const score = await grammarlyClient.score(text);
scoreCache.set(key, score);
return score;
}
Parallel API Calls
// Score + AI detect in parallel (they're independent)
async function fullAudit(text: string, token: string) {
const [score, ai] = await Promise.all([
grammarlyClient.score(text),
grammarlyClient.detectAI(text),
]);
return { score, ai };
}
Resources
Next Steps
For cost optimization, see grammarly-cost-tuning.
Production readiness checklist for Grammarly API integrations.
ReadWriteEditBash(curl:*)Grep
Grammarly Production Checklist
Overview
Grammarly provides AI-powered writing assistance with grammar checking, tone detection, plagiarism scanning, and style suggestions. A production integration processes user-submitted text through Grammarly's API and returns actionable suggestions. Failures mean unchecked content goes live, suggestion latency degrades UX, or sensitive text leaks outside approved processing boundaries.
Authentication & Secrets
- [ ]
GRAMMARLYAPIKEY stored in secrets manager (not config files)
- [ ] Client credentials (ID + secret) separated from application code
- [ ] Token refresh logic handles expiry before API calls fail
- [ ] Separate credentials for dev/staging/prod environments
- [ ] Key rotation schedule documented (90-day cycle)
API Integration
- [ ] Production base URL configured (
https://api.grammarly.com/v1)
- [ ] Rate limit handling with exponential backoff
- [ ] Text chunking implemented for documents > 100K characters
- [ ] Minimum 30-word validation before sending to API
- [ ] Suggestion categories configured per use case (grammar, tone, clarity)
- [ ] AI detection endpoint integrated if content authenticity required
- [ ] Plagiarism check timeout handling (longer processing for large docs)
Error Handling & Resilience
- [ ] Circuit breaker configured for Grammarly API outages
- [ ] Retry with backoff for 429/5xx responses
- [ ] Writing score thresholds defined per content type
- [ ] Graceful degradation when API is unavailable (queue, not block)
- [ ] Error responses logged with request IDs for support escalation
- [ ] Partial suggestion results handled (incomplete analysis on timeout)
Monitoring & Alerting
- [ ] API latency tracked per request type (check, detect, plagiarism)
- [ ] Error rate alerts set (threshold: >5% over 5 minutes)
- [ ] Token refresh failures trigger immediate notification
- [ ] Suggestion acceptance rate tracked for quality feedback loop
- [ ] Daily API usage against plan limits monitored
Validation Script
async function checkGrammarlyReadiness(): Promise<void> {
const checks: { name: string; pass: boolean; detail: string }[] = [];
// API connectivity
try {
const res = await fetch('https://api.grammarly.com/v1/check', {
method: 'POST',
headers: { Authorization: `Bearer ${process.env.GRAMMARLY_API_KEY}`, 'Content-Type': 'application/json' },
body: JSON.stringify({ text: 'This is a production readiness test sentence for validation.' }),
});
checks.push({ name: 'Grammarly API', pass: res.ok, detail: res.ok ? 'Connected' : `HTTP ${res.status}` });
} catch (
Implement Grammarly rate limiting, backoff, and idempotency patterns.
ReadWriteEdit
Grammarly Rate Limits
Overview
Grammarly's Text API enforces plan-dependent rate limits across its writing score, AI detection, and plagiarism endpoints. The plagiarism checker is asynchronous and requires polling, which adds complexity to rate management. Token endpoints are separately throttled at roughly 10 requests per hour, so credential rotation during high-throughput batch processing of documents (e.g., scanning an entire content library) requires careful token lifecycle management to avoid auth-layer 429s on top of API-layer throttling.
Rate Limit Reference
| Endpoint |
Limit |
Window |
Scope |
| Writing score |
100 req (Business) |
1 minute |
Per API key |
| AI content detection |
60 req (Business) |
1 minute |
Per API key |
| Plagiarism check (submit) |
30 req |
1 minute |
Per API key |
| Plagiarism check (poll) |
120 req |
1 minute |
Per API key |
| Token refresh |
10 req |
1 hour |
Per client credentials |
Rate Limiter Implementation
class GrammarlyRateLimiter {
private tokens: number;
private lastRefill: number;
private readonly max: number;
private readonly refillRate: number;
private queue: Array<{ resolve: () => void }> = [];
constructor(maxPerMinute: number) {
this.max = maxPerMinute;
this.tokens = maxPerMinute;
this.lastRefill = Date.now();
this.refillRate = maxPerMinute / 60_000;
}
async acquire(): Promise<void> {
this.refill();
if (this.tokens >= 1) { this.tokens -= 1; return; }
return new Promise(resolve => this.queue.push({ resolve }));
}
private refill() {
const now = Date.now();
this.tokens = Math.min(this.max, this.tokens + (now - this.lastRefill) * this.refillRate);
this.lastRefill = now;
while (this.tokens >= 1 && this.queue.length) {
this.tokens -= 1;
this.queue.shift()!.resolve();
}
}
}
const scoreLimiter = new GrammarlyRateLimiter(90);
const plagiarismLimiter = new GrammarlyRateLimiter(25);
Retry Strategy
async function grammarlyRetry<T>(
limiter: GrammarlyRateLimiter, fn: () => Promise<Response>, maxRetries = 3
): Promise<T> {
for (let attempt = 0; attempt <= maxRetries; attempt++) {
await limiter.acquire();
const res = await fn();
if (res.ok) return res.json();
if (res.status === 429) {
const retryAfter = parseInt(res.headers.get("Retry-After") || "20", 10);
const jitter = Math.random() * 2000;
await new Promise(r => setTimeout(r, retryAfter * 1000 + jitter));
continue;
}
if (res.status >= 500 &&
Implement Grammarly reference architecture with best-practice project layout.
ReadGrep
Grammarly Reference Architecture
Architecture
┌────────────────────────────────────┐
│ Your Application │
├────────────────────────────────────┤
│ Content Quality Service │
│ (Score, AI Detect, Plagiarism) │
├────────────────────────────────────┤
│ Grammarly API Client │
│ (Auth, Retry, Cache, Chunking) │
├────────────────────────────────────┤
│ Grammarly APIs │
│ api.grammarly.com │
└────────────────────────────────────┘
Project Structure
grammarly-integration/
├── src/grammarly/
│ ├── client.ts # API client with token management
│ ├── scoring.ts # Writing Score API
│ ├── detection.ts # AI + Plagiarism detection
│ ├── chunking.ts # Large document splitting
│ └── types.ts # TypeScript interfaces
├── src/services/
│ ├── quality-gate.ts # Threshold enforcement
│ └── content-audit.ts # Full audit pipeline
├── tests/
└── .env.example
API Decision Matrix
| Need |
API |
Notes |
| Grammar/style quality |
Writing Score v2 |
Sync, fast |
| AI content detection |
AI Detection v1 |
Sync, fast |
| Source matching |
Plagiarism v1 |
Async, poll |
| All three |
Combined pipeline |
Parallel where possible |
Resources
Next Steps
Start with grammarly-install-auth.
Apply production-ready Grammarly SDK patterns for TypeScript and Python.
ReadWriteEdit
Grammarly SDK Patterns
Overview
Production patterns for Grammarly API: typed client, token management, text chunking for large documents, and Python integration.
Instructions
Step 1: Typed API Client
class GrammarlyClient {
private token: string;
private expiresAt: number = 0;
private base = 'https://api.grammarly.com/ecosystem/api';
constructor(private clientId: string, private clientSecret: string) {}
private async ensureToken() {
if (Date.now() < this.expiresAt - 60000) return;
const res = await fetch(`${this.base}/v1/oauth/token`, {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({ grant_type: 'client_credentials', client_id: this.clientId, client_secret: this.clientSecret }),
});
const { access_token, expires_in } = await res.json();
this.token = access_token;
this.expiresAt = Date.now() + expires_in * 1000;
}
async score(text: string) {
await this.ensureToken();
const res = await fetch(`${this.base}/v2/scores`, {
method: 'POST',
headers: { 'Authorization': `Bearer ${this.token}`, 'Content-Type': 'application/json' },
body: JSON.stringify({ text }),
});
return res.json();
}
async detectAI(text: string) {
await this.ensureToken();
const res = await fetch(`${this.base}/v1/ai-detection`, {
method: 'POST',
headers: { 'Authorization': `Bearer ${this.token}`, 'Content-Type': 'application/json' },
body: JSON.stringify({ text }),
});
return res.json();
}
}
Step 2: Text Chunking for Large Documents
function chunkText(text: string, maxChars = 90000): string[] {
if (text.length <= maxChars) return [text];
const chunks: string[] = [];
const paragraphs = text.split('\n\n');
let current = '';
for (const p of paragraphs) {
if ((current + '\n\n' + p).length > maxChars) {
if (current) chunks.push(current);
current = p;
} else {
current = current ? current + '\n\n' + p : p;
}
}
if (current) chunks.push(current);
return chunks;
}
Step 3: Python Client
import os, requests
from dotenv import load_dotenv
load_dotenv()
class GrammarlyClient:
BASE = 'https://api.grammarly.com/ecosystem/api'
def __init__(self):
self.token = None
self._authenticate()
def _authenticate(self):
r = requests.post(f'{self.BASE}/v1/oauth/token', data={
'grant_type': 'client_credentials',
'client_id': os.environ['GRAMMARLY_CLIENT_ID'],
'client_secret': os.environ['GRAMMARLY_CLIENT_SECRET']
Security fundamentals for Grammarly API credential management.
ReadWriteEditBash(curl:*)Grep
Grammarly Security Basics
Overview
Grammarly processes user-written text content for grammar, tone, and style suggestions. Integrations handle document text that may contain confidential business communications, legal drafts, or personal correspondence. Security concerns include OAuth client credential management, ensuring user text is not persisted or logged unnecessarily, and protecting access tokens that grant read/write access to user documents and suggestion history.
API Key Management
function createGrammarlyClient(): { clientId: string; clientSecret: string } {
const clientId = process.env.GRAMMARLY_CLIENT_ID;
const clientSecret = process.env.GRAMMARLY_CLIENT_SECRET;
if (!clientId || !clientSecret) {
throw new Error("Missing GRAMMARLY_CLIENT_ID or GRAMMARLY_CLIENT_SECRET");
}
// Access tokens from client_credentials grant — never persist to disk
console.log("Grammarly client initialized (client ID:", clientId.slice(0, 8), "...)");
return { clientId, clientSecret };
}
Webhook Signature Verification
import crypto from "crypto";
import { Request, Response, NextFunction } from "express";
function verifyGrammarlyWebhook(req: Request, res: Response, next: NextFunction): void {
const signature = req.headers["x-grammarly-signature"] as string;
const secret = process.env.GRAMMARLY_WEBHOOK_SECRET!;
const expected = crypto.createHmac("sha256", secret).update(req.body).digest("hex");
if (!signature || !crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected))) {
res.status(401).send("Invalid signature");
return;
}
next();
}
Input Validation
import { z } from "zod";
const TextAnalysisSchema = z.object({
document_id: z.string().uuid(),
text: z.string().min(1).max(100_000),
language: z.enum(["en-US", "en-GB", "en-AU", "en-CA"]).default("en-US"),
domain: z.enum(["general", "academic", "business", "casual"]).default("general"),
goals: z.array(z.enum(["clarity", "engagement", "delivery", "correctness"])).optional(),
});
function validateTextAnalysis(data: unknown) {
return TextAnalysisSchema.parse(data);
}
Data Protection
const GRAMMARLY_SENSITIVE_FIELDS = ["document_text", "user_email", "access_token", "client_secret", "suggestion_context"];
function redactGrammarlyLog(record: Record<string, unknown>): Record<string, unknown> {
const redacted = { ...record };
for (const field of GRAMMARLY_SENSITIVE_FIELDS) {
if (field in redacted) redacted[field] =
Upgrade and migration guidance for Grammarly API version changes.
ReadWriteEditBash(curl:*)Grep
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: Ar
Implement Grammarly webhook signature validation and event handling.
ReadWriteEditBash(curl:*)
Grammarly Webhooks & Events
Overview
Grammarly's current API is request/response based — there are no push webhooks. For async operations (plagiarism detection), you poll for results. Build your own event system around Grammarly API results.
Instructions
Step 1: Plagiarism Polling with Callback
async function plagiarismWithCallback(
text: string,
token: string,
onComplete: (result: any) => void
) {
const createRes = await fetch('https://api.grammarly.com/ecosystem/api/v1/plagiarism', {
method: 'POST',
headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' },
body: JSON.stringify({ text }),
});
const { id } = await createRes.json();
const poll = async () => {
const res = await fetch(`https://api.grammarly.com/ecosystem/api/v1/plagiarism/${id}`, {
headers: { 'Authorization': `Bearer ${token}` },
});
const result = await res.json();
if (result.status === 'pending') { setTimeout(poll, 3000); return; }
onComplete(result);
};
poll();
}
Step 2: Build Event Bus for Score Results
import { EventEmitter } from 'events';
const grammarlyEvents = new EventEmitter();
grammarlyEvents.on('score.completed', (data) => {
if (data.overallScore < 60) console.warn('Low quality content detected');
});
grammarlyEvents.on('ai.detected', (data) => {
if (data.score > 70) console.warn('Likely AI-generated content');
});
Resources
Next Steps
For performance, see grammarly-performance-tuning.
Ready to use grammarly-pack?
|