agentsclimarketplace

Grammarly rate limits

Skill jeremylongshore/claude-code-plugins-plus-skills/plugins/saas-packs/grammarly-pack/skills/grammarly-rate-limits

'Implement Grammarly rate limiting, backoff, and idempotency patterns.From its SKILL.md

Install
npx -y skills add jeremylongshore/claude-code-plugins-plus-skills --skill grammarly-rate-limits

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

What its file declares

Copied from the file, not written here

The file declares its own license as MIT. That is the author’s claim about this one file, and it is not the same thing as the license GitHub reports for the repository, which is listed with the other numbers below.

SKILL.md

4.7 KB, ~1.0k tokens by cl100k_base, as published. Nobody here has run it

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

EndpointLimitWindowScope
Writing score100 req (Business)1 minutePer API key
AI content detection60 req (Business)1 minutePer API key
Plagiarism check (submit)30 req1 minutePer API key
Plagiarism check (poll)120 req1 minutePer API key
Token refresh10 req1 hourPer 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 && attempt < maxRetries) {
      await new Promise(r => setTimeout(r, Math.pow(2, attempt) * 1500));
      continue;
    }
    throw new Error(`Grammarly API ${res.status}: ${await res.text()}`);
  }
  throw new Error("Max retries exceeded");
}

Batch Processing

async function batchScoreDocuments(documents: string[], batchSize = 8) {
  const results: any[] = [];
  for (let i = 0; i < documents.length; i += batchSize) {
    const batch = documents.slice(i, i + batchSize);
    const batchResults = await Promise.all(
      batch.map(text => grammarlyRetry(scoreLimiter, () =>
        fetch("https://api.grammarly.com/ecosystem/api/v2/scores", {
          method: "POST", headers,
          body: JSON.stringify({ text }),
        })
      ))
    );
    results.push(...batchResults);
    if (i + batchSize < documents.length) await new Promise(r => setTimeout(r, 5000));
  }
  return results;
}

Error Handling

IssueCauseFix
429 on writing scoreExceeded plan-tier per-minute capReduce concurrency, use Retry-After
429 on token refreshMore than 10 refreshes/hourCache token, refresh only on 401
Plagiarism poll timeoutResult not ready after 60sIncrease poll interval to 10s, max 5 min
413 on large documentText exceeds 100K character limitSplit into sections, score individually
Empty score responseText too short for analysisValidate minimum 50 characters before submission

Resources

Next Steps

See grammarly-performance-tuning.

What ships with it

Read from the repository

Just SKILL.md. No reference files, no scripts.

Keep looking

Skills are one crate of 326,144. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.