Automated summary
This skill explains how to handle Linear API rate limits and quotas, with TypeScript examples for header tracking, exponential backoff, and request queuing.
Strengths
- Provides a concrete handler based on X-RateLimit and X-Complexity headers.
- Details exponential backoff with jitter and Retry-After support.
- Explicitly targets 429 errors and Linear's RATE_LIMITED code.
- Lists standard and enterprise limits in a clear table.
Limitations
- The quoted quotas may change and are not dated or linked to an official source.
- The request queue example is incomplete in the provided content.
- Examples are TypeScript-only and assume familiarity with HTTP headers.
Use when a Linear integration hits 429 errors, complexity quotas, or needs a throttling strategy.
Avoid for non-Linear APIs or when you need an official, up-to-date reference for current quotas.
Security analysis
SafeThe skill is a pure documentation/pattern guide for coping with Linear API rate limits. It contains no bash instructions, no destructive commands, no obfuscated payloads, no attempts to disable safety mechanisms, and no guidance to transmit tokens or data outside the local environment. The embedded TypeScript samples (backoff, queue, batching) are conventional and only touch the Linear SDK, so listing this carries no meaningful execution risk.
- •No executable actions: tool set is limited to Read/Write/Edit/Grep and all code blocks are illustrative examples, not commands the agent is instructed to run
- •No network calls, credential handling, or secret exfiltration; the only external references are public Linear documentation links
Examples
My TypeScript Linear integration is getting 429 errors; help me add exponential backoff with jitter and respect the Retry-After header.Implement a Linear rate limiter that reads X-RateLimit-Remaining, X-RateLimit-Reset, and X-Complexity-Remaining before each request.How should I structure a Linear request queue to stay under 1,500 requests/min and 250,000 complexity/min?name: linear-rate-limits description: | Handle Linear API rate limiting and quotas effectively. Use when dealing with rate limit errors, implementing throttling, or optimizing API usage patterns. Trigger with phrases like "linear rate limit", "linear throttling", "linear API quota", "linear 429 error", "linear request limits". allowed-tools: Read, Write, Edit, Grep version: 1.0.0 license: MIT author: Jeremy Longshore jeremy@intentsolutions.io
Linear Rate Limits
Overview
Understand and handle Linear API rate limits for reliable integrations.
Prerequisites
- Linear SDK configured
- Understanding of HTTP headers
- Familiarity with async patterns
Linear Rate Limit Structure
Current Limits
| Tier | Requests/min | Complexity/min | Notes | |------|-------------|----------------|-------| | Standard | 1,500 | 250,000 | Most integrations | | Enterprise | Higher | Higher | Contact Linear |
Headers Returned
X-RateLimit-Limit: 1500
X-RateLimit-Remaining: 1499
X-RateLimit-Reset: 1640000000
X-Complexity-Limit: 250000
X-Complexity-Cost: 50
X-Complexity-Remaining: 249950
Instructions
Step 1: Basic Rate Limit Handler
// lib/rate-limiter.ts
interface RateLimitState {
remaining: number;
reset: Date;
complexityRemaining: number;
}
class LinearRateLimiter {
private state: RateLimitState = {
remaining: 1500,
reset: new Date(),
complexityRemaining: 250000,
};
updateFromHeaders(headers: Headers): void {
const remaining = headers.get("x-ratelimit-remaining");
const reset = headers.get("x-ratelimit-reset");
const complexityRemaining = headers.get("x-complexity-remaining");
if (remaining) this.state.remaining = parseInt(remaining);
if (reset) this.state.reset = new Date(parseInt(reset) * 1000);
if (complexityRemaining) {
this.state.complexityRemaining = parseInt(complexityRemaining);
}
}
async waitIfNeeded(): Promise<void> {
// If very low on requests, wait until reset
if (this.state.remaining < 10) {
const waitMs = this.state.reset.getTime() - Date.now();
if (waitMs > 0) {
console.log(`Rate limit low, waiting ${waitMs}ms...`);
await new Promise(r => setTimeout(r, waitMs));
}
}
}
getState(): RateLimitState {
return { ...this.state };
}
}
export const rateLimiter = new LinearRateLimiter();
Step 2: Exponential Backoff
// lib/backoff.ts
interface BackoffOptions {
maxRetries?: number;
baseDelayMs?: number;
maxDelayMs?: number;
jitter?: boolean;
}
export async function withBackoff<T>(
fn: () => Promise<T>,
options: BackoffOptions = {}
): Promise<T> {
const {
maxRetries = 5,
baseDelayMs = 1000,
maxDelayMs = 30000,
jitter = true,
} = options;
let lastError: Error | undefined;
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
return await fn();
} catch (error: any) {
lastError = error;
// Only retry on rate limit errors
const isRateLimited =
error?.extensions?.code === "RATE_LIMITED" ||
error?.response?.status === 429;
if (!isRateLimited || attempt === maxRetries - 1) {
throw error;
}
// Calculate delay with exponential backoff
let delay = Math.min(baseDelayMs * Math.pow(2, attempt), maxDelayMs);
// Add jitter to prevent thundering herd
if (jitter) {
delay += Math.random() * delay * 0.1;
}
// Check Retry-After header if available
const retryAfter = error?.response?.headers?.get?.("retry-after");
if (retryAfter) {
delay = Math.max(delay, parseInt(retryAfter) * 1000);
}
console.log(
`Rate limited, attempt ${attempt + 1}/${maxRetries}, ` +
`retrying in ${Math.round(delay)}ms...`
);
await new Promise(r => setTimeout(r, delay));
}
}
throw lastError;
}
Step 3: Request Queue
// lib/queue.ts
type QueuedRequest<T> = {
fn: () => Promise<T>;
resolve: (value: T) => void;
reject: (error: Error) => void;
};
class RequestQueue {
private queue: QueuedRequest<any>[] = [];
private processing = false;
private requestsPerSecond = 20; // Conservative rate
async add<T>(fn: () => Promise<T>): Promise<T> {
return new Promise((resolve, reject) => {
this.queue.push({ fn, resolve, reject });
this.process();
});
}
private async process(): Promise<void> {
if (this.processing) return;
this.processing = true;
while (this.queue.length > 0) {
const request = this.queue.shift()!;
try {
const result = await request.fn();
request.resolve(result);
} catch (error) {
request.reject(error as Error);
}
// Throttle requests
await new Promise(r =>
setTimeout(r, 1000 / this.requestsPerSecond)
);
}
this.processing = false;
}
get pending(): number {
return this.queue.length;
}
}
export const requestQueue = new RequestQueue();
Step 4: Batch Operations
// lib/batch.ts
import { LinearClient } from "@linear/sdk";
interface BatchConfig {
batchSize: number;
delayBetweenBatches: number;
}
export async function batchProcess<T, R>(
items: T[],
processor: (item: T) => Promise<R>,
config: BatchConfig = { batchSize: 10, delayBetweenBatches: 1000 }
): Promise<R[]> {
const results: R[] = [];
const batches: T[][] = [];
// Split into batches
for (let i = 0; i < items.length; i += config.batchSize) {
batches.push(items.slice(i, i + config.batchSize));
}
for (let i = 0; i < batches.length; i++) {
const batch = batches[i];
console.log(`Processing batch ${i + 1}/${batches.length}...`);
// Process batch in parallel
const batchResults = await Promise.all(batch.map(processor));
results.push(...batchResults);
// Delay between batches (except last)
if (i < batches.length - 1) {
await new Promise(r => setTimeout(r, config.delayBetweenBatches));
}
}
return results;
}
// Usage example
async function updateManyIssues(
client: LinearClient,
updates: { id: string; priority: number }[]
) {
return batchProcess(
updates,
({ id, priority }) => client.updateIssue(id, { priority }),
{ batchSize: 10, delayBetweenBatches: 2000 }
);
}
Step 5: Query Optimization
// Reduce complexity by limiting fields
const optimizedQuery = `
query Issues($filter: IssueFilter) {
issues(filter: $filter, first: 50) {
nodes {
id
identifier
title
# Avoid nested connections in loops
}
}
}
`;
// Use SDK efficiently
async function getIssuesOptimized(client: LinearClient, teamKey: string) {
// Good: Single query with filter
return client.issues({
filter: { team: { key: { eq: teamKey } } },
first: 50,
});
// Bad: N+1 queries
// const teams = await client.teams();
// for (const team of teams.nodes) {
// const issues = await team.issues(); // N queries!
// }
}
Output
- Rate limit monitoring
- Automatic retry with backoff
- Request queuing and throttling
- Batch processing utilities
- Optimized query patterns
Error Handling
| Error | Cause | Solution |
|-------|-------|----------|
| 429 Too Many Requests | Rate limit exceeded | Use backoff and queue |
| Complexity exceeded | Query too expensive | Simplify query structure |
| Timeout | Long-running query | Paginate or split queries |
Resources
Next Steps
Learn security best practices with linear-security-basics.
From the file to a first result
The download contains exactly the content displayed on this page. This copy may differ from the latest version in the source repository.
View the source and supporting filesThis download contains SKILL.md only. Any scripts, references, templates and dependencies are not included. Get the required files from the source, preserving their directory structure, before continuing.
1. Download the file
Download SKILL.mdFrom the root of a test project, save the file as .skill-downloads/linear-api-rate-limits-management-mmh8qqox/SKILL.md, or use the command below. This folder is for inspecting the skill before installation.
Command for macOS/Linux or WSL: requires a POSIX shell and curl. On Windows without WSL, use the download button and create the folder manually.
mkdir -p '.skill-downloads/linear-api-rate-limits-management-mmh8qqox' && curl --fail --show-error --location 'https://skills-guides.com/api/skills/linear-api-rate-limits-management-mmh8qqox/download' --output '.skill-downloads/linear-api-rate-limits-management-mmh8qqox/SKILL.md.part' && mv '.skill-downloads/linear-api-rate-limits-management-mmh8qqox/SKILL.md.part' '.skill-downloads/linear-api-rate-limits-management-mmh8qqox/SKILL.md'2. Check prerequisites
Read SKILL.md before using it. If it references scripts or external services, inspect them and prepare the required access in your test environment.
Tools declared by the author (check availability in your agent): Read, Write, Edit, Grep
Original excerpt: Prerequisites
- Linear SDK configured
- Understanding of HTTP headers
- Familiarity with async patterns
The platforms on this page are catalog indications. The paths below follow tool documentation; they do not prove this skill has been run in those tools. No minimum tool version is confirmed for this listing.
3. Load the skill
After inspection, copy the complete folder into your project using one of the commands below, then open a new tool session. The command stops if a folder with the same name already exists: compare the files before replacing it.
Claude Code
test -f '.skill-downloads/linear-api-rate-limits-management-mmh8qqox/SKILL.md' && test -s '.skill-downloads/linear-api-rate-limits-management-mmh8qqox/SKILL.md' && mkdir -p '.claude/skills' && test ! -e '.claude/skills/linear-rate-limits' && cp -R '.skill-downloads/linear-api-rate-limits-management-mmh8qqox' '.claude/skills/linear-rate-limits'Cursor
test -f '.skill-downloads/linear-api-rate-limits-management-mmh8qqox/SKILL.md' && test -s '.skill-downloads/linear-api-rate-limits-management-mmh8qqox/SKILL.md' && mkdir -p '.cursor/skills' && test ! -e '.cursor/skills/linear-rate-limits' && cp -R '.skill-downloads/linear-api-rate-limits-management-mmh8qqox' '.cursor/skills/linear-rate-limits'Codex
test -f '.skill-downloads/linear-api-rate-limits-management-mmh8qqox/SKILL.md' && test -s '.skill-downloads/linear-api-rate-limits-management-mmh8qqox/SKILL.md' && mkdir -p '.agents/skills' && test ! -e '.agents/skills/linear-rate-limits' && cp -R '.skill-downloads/linear-api-rate-limits-management-mmh8qqox' '.agents/skills/linear-rate-limits'OpenSkills
OpenSkills must already be installed. The local install/read procedure is checked with OpenSkills 1.5.0, which requires Node.js ≥ 20.6.0. Catalog slugs are not resolved automatically.
Official OpenSkills installationopenskills install './.skill-downloads/linear-api-rate-limits-management-mmh8qqox'openskills read 'linear-api-rate-limits-management-mmh8qqox'Reading should display the installed file content and its base directory. It does not run the skill or install its dependencies.
For an agent that reads AGENTS.md, then sync the index. This command changes AGENTS.md: review the diff before using it.
openskills syncManual method or another tool: attach SKILL.md to a conversation, or open it in an agent that can read files. Include any required supporting resources. No universal folder is recognized by every tool.
4. Try a first task
Use a short, synthetic input: an API endpoint, a small document or an interface brief. In a new conversation, attach the file or give the agent its path, then adapt this request:
Read .skill-downloads/linear-api-rate-limits-management-mmh8qqox/SKILL.md and apply its instructions to this example: [describe a test input]. Identify any missing files, dependencies or access. Produce the result requested by the skill without inventing data or running unreviewed scripts.Expected result: output matching the skill instructions, or a precise list of missing prerequisites. A read confirmation alone does not validate a first use. If an external resource is unavailable, that path remains untested.
React Hook Form Best Practices
Development
Master React Hook Form: avoid anti-patterns, manage subscriptions, and handle reset, dirty state, and controlled inputs correctly.
Emotion to Mantine Migration
Development
Migrate Emotion styled-components to Mantine components with style props and CSS modules. Use when converting .styled.tsx files or removing @emotion imports.
Scope Creep Detector
Development
Analyzes git diffs against a stated intent to detect scope creep, unrelated files, broad PRs, and oversized changes, then suggests keep, split, or justify actions.