Helps developers minimize infrastructure costs and stay within Linear API rate limits through efficiency tuning.

Install

mkdir -p .claude/skills/linear-cost-tuning && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/7293" && unzip -o skill.zip -d .claude/skills/linear-cost-tuning && rm skill.zip

Installs to .claude/skills/linear-cost-tuning

Activation

This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.

Optimize Linear API usage, reduce unnecessary calls, and maximize
65 charsno explicit “when” trigger
Intermediate

Key capabilities

  • Audit Linear API usage for requests and complexity
  • Replace API polling with webhooks
  • Minimize GraphQL query complexity
  • Coalesce concurrent identical API requests
  • Cache API responses with specific TTLs
  • Filter webhook events to reduce processing

How it works

The skill provides code examples and strategies to optimize Linear API usage by tracking requests and complexity, replacing polling with webhooks, minimizing query fields, coalescing requests, and caching responses.

Inputs & outputs

You give it
Linear API calls, webhook events
You get back
Optimized API usage, reduced request count, lower complexity, real-time updates

When to use linear-cost-tuning

  • Reducing Linear API costs
  • Optimizing query complexity
  • Batching multiple API requests
  • Monitoring API usage budgets

About this skill

Linear Cost Tuning

Overview

Optimize Linear API usage to stay within rate budgets and minimize infrastructure costs. Linear's API is free (no per-request billing), but rate limits (5,000 requests/hour, 250,000 complexity/hour) constrain throughput. Efficient patterns let you do more within these limits.

Cost Factors

FactorBudget ImpactOptimization
Request count5,000/hr limitBatch operations, coalesce requests
Query complexity250,000/hr limitFlat queries, small page sizes
Payload sizeBandwidth + latencySelect only needed fields
Polling frequencyWastes budgetReplace with webhooks
Webhook volumeProcessing costsFilter by event type and team

Instructions

Step 1: Audit Current Usage

import { LinearClient } from "@linear/sdk";

class UsageTracker {
  private requests = 0;
  private totalComplexity = 0;
  private startTime = Date.now();

  track(complexity: number) {
    this.requests++;
    this.totalComplexity += complexity;
  }

  report() {
    const elapsedHours = (Date.now() - this.startTime) / 3600000;
    return {
      requests: this.requests,
      requestsPerHour: Math.round(this.requests / elapsedHours),
      totalComplexity: this.totalComplexity,
      complexityPerHour: Math.round(this.totalComplexity / elapsedHours),
      budgetUsed: {
        requests: `${Math.round((this.requests / elapsedHours / 5000) * 100)}%`,
        complexity: `${Math.round((this.totalComplexity / elapsedHours / 250000) * 100)}%`,
      },
    };
  }
}

const tracker = new UsageTracker();

Step 2: Replace Polling with Webhooks

The single biggest optimization. A polling loop checking every minute uses 1,440 requests/day. A webhook uses zero.

// BAD: Polling every 60 seconds (1,440 req/day, ~60 req/hr)
setInterval(async () => {
  const issues = await client.issues({
    first: 100,
    filter: { updatedAt: { gte: lastCheck } },
  });
  await syncIssues(issues.nodes);
  lastCheck = new Date().toISOString();
}, 60000);

// GOOD: Webhook receives updates in real-time (0 requests for monitoring)
app.post("/webhooks/linear", express.raw({ type: "*/*" }), (req, res) => {
  // Verify signature, process event
  const event = JSON.parse(req.body.toString());
  if (event.type === "Issue") {
    syncSingleIssue(event.data);
  }
  res.json({ ok: true });
});

Step 3: Minimize Query Complexity

// BAD: ~12,500 pts — deeply nested with large page
// issues(50) * (labels(50 default) * fields + comments(50) * user)
const expensive = `query {
  issues(first: 50) {
    nodes {
      id title
      assignee { name }
      labels { nodes { name } }
      comments(first: 10) { nodes { body user { name } } }
    }
  }
}`;

// GOOD: ~55 pts — flat fields only
const cheap = `query {
  issues(first: 50) {
    nodes { id identifier title priority estimate }
  }
}`;

// Fetch relations separately only when needed
const issueDetail = `query($id: String!) {
  issue(id: $id) {
    id identifier title description priority
    assignee { name email }
    state { name type }
    labels { nodes { name color } }
  }
}`;

Step 4: Request Coalescing

Deduplicate concurrent identical requests.

const inflight = new Map<string, Promise<any>>();

async function coalesce<T>(key: string, fn: () => Promise<T>): Promise<T> {
  if (inflight.has(key)) return inflight.get(key)!;
  const promise = fn().finally(() => inflight.delete(key));
  inflight.set(key, promise);
  return promise;
}

// 10 concurrent requests for same team = 1 actual API call
async function getTeam(teamKey: string) {
  return coalesce(`team:${teamKey}`, async () => {
    const result = await client.teams({ filter: { key: { eq: teamKey } } });
    return result.nodes[0];
  });
}

Step 5: Cache with Smart TTLs

const CACHE_TTLS = {
  teams: 600,        // 10 min — teams almost never change
  workflowStates: 1800, // 30 min — states rarely change
  labels: 600,       // 10 min — labels rarely change
  issues: 60,        // 1 min — issues change frequently
  viewer: 3600,      // 1 hr — your identity doesn't change
};

// Combined with webhook invalidation, even short TTLs
// dramatically reduce redundant requests

Step 6: Filter Webhook Events

Skip irrelevant events to reduce processing costs.

async function processEvent(event: any): Promise<void> {
  // Skip bot/automation events to avoid loops
  if (event.actor?.type === "application") return;

  // Skip trivial field updates (e.g., sortOrder changes)
  if (event.type === "Issue" && event.action === "update") {
    const significantFields = ["stateId", "assigneeId", "priority", "title"];
    const changedFields = Object.keys(event.updatedFrom ?? {});
    if (!changedFields.some(f => significantFields.includes(f))) return;
  }

  // Skip specific teams if not relevant
  const relevantTeamKeys = ["ENG", "PRODUCT"];
  if (event.data?.team?.key && !relevantTeamKeys.includes(event.data.team.key)) return;

  // Process significant event
  await handleEvent(event);
}

Step 7: Incremental Sync Pattern

// Instead of fetching ALL issues every sync:
// Sort by updatedAt, stop when you reach already-synced data

async function incrementalSync(client: LinearClient, lastSyncTime: string) {
  let cursor: string | undefined;
  let synced = 0;

  while (true) {
    const issues = await client.issues({
      first: 100,
      after: cursor,
      filter: { updatedAt: { gte: lastSyncTime } },
      orderBy: "updatedAt",
    });

    for (const issue of issues.nodes) {
      await upsertLocally(issue);
      synced++;
    }

    if (!issues.pageInfo.hasNextPage) break;
    cursor = issues.pageInfo.endCursor;
  }

  console.log(`Synced ${synced} issues since ${lastSyncTime}`);
  return synced;
}

Optimization Checklist

  • Replace all polling with webhooks
  • Implement request caching (static data: 10-30 min TTL)
  • Add request coalescing for concurrent identical calls
  • Filter webhook events (skip bots, trivial updates, irrelevant teams)
  • Keep query complexity under 500 pts per query
  • Use rawRequest() for exact field selection
  • Sort by updatedAt for incremental sync
  • Batch mutations (20 per GraphQL request)
  • Cache teams/states/labels with webhook invalidation

Error Handling

ErrorCauseSolution
Rate limit hit frequentlyToo many requestsImplement coalescing + caching
Stale cache dataTTL too longUse webhook-driven invalidation
High complexity queriesNested relationsFlatten with rawRequest(), fetch relations lazily
Webhook processing overloadUnfiltered eventsAdd type/team/field filtering

Resources

Limitations

  • Linear API has a 5,000 requests/hour rate limit
  • Linear API has a 250,000 complexity/hour rate limit
  • Caching TTLs are examples and may need adjustment based on data volatility

How it compares

This skill offers specific patterns for cost and rate limit optimization, contrasting with a default approach that might lead to excessive API calls and higher complexity.

Compared to similar skills

linear-cost-tuning side by side with the closest alternatives in the catalog.

SkillInstallsUpdatedSafetyDifficulty
linear-cost-tuning (this skill)127dNo flagsIntermediate
deepgram-performance-tuning327dReviewIntermediate
graphql66moNo flagsAdvanced
guidewire-sdk-patterns227dReviewAdvanced

Try saying

Example prompts that trigger this skill in your AI assistant.

More by jeremylongshore

View all by jeremylongshore

analyzing-logs

jeremylongshore

Analyze application logs to detect performance issues, identify error patterns, and improve stability by extracting key insights.

14123

ollama-setup

jeremylongshore

Configure auto-configure Ollama when user needs local LLM deployment, free AI alternatives, or wants to eliminate hosted API costs. Trigger phrases: "install ollama", "local AI", "free LLM", "self-hosted AI", "replace OpenAI", "no API costs". Use when appropriate context detected. Trigger with relevant phrases based on skill purpose.

1167

backtesting-trading-strategies

jeremylongshore

Backtest crypto and traditional trading strategies against historical data. Calculates performance metrics (Sharpe, Sortino, max drawdown), generates equity curves, and optimizes strategy parameters. Use when user wants to test a trading strategy, validate signals, or compare approaches. Trigger with phrases like "backtest strategy", "test trading strategy", "historical performance", "simulate trades", "optimize parameters", or "validate signals".

1071

generating-database-seed-data

jeremylongshore

Process this skill enables AI assistant to generate realistic test data and database seed scripts for development and testing environments. it uses faker libraries to create realistic data, maintains relational integrity, and allows configurable data volumes. u... Use when working with databases or data models. Trigger with phrases like 'database', 'query', or 'schema'.

1033

cursor-codebase-indexing

jeremylongshore

Execute set up and optimize Cursor codebase indexing. Triggers on "cursor index setup", "codebase indexing", "index codebase", "cursor semantic search". Use when working with cursor codebase indexing functionality. Trigger with phrases like "cursor codebase indexing", "cursor indexing", "cursor".

885

testing-mobile-apps

jeremylongshore

Execute mobile app testing on iOS and Android devices/simulators. Use when performing specialized testing. Trigger with phrases like "test mobile app", "run iOS tests", or "validate Android functionality".

810

You might also like

deepgram-performance-tuning

jeremylongshore

Optimize Deepgram API performance for faster transcription and lower latency. Use when improving transcription speed, reducing latency, or optimizing audio processing pipelines. Trigger with phrases like "deepgram performance", "speed up deepgram", "optimize transcription", "deepgram latency", "deepgram faster".

333

graphql

davila7

GraphQL gives clients exactly the data they need - no more, no less. One endpoint, typed schema, introspection. But the flexibility that makes it powerful also makes it dangerous. Without proper controls, clients can craft queries that bring down your server. This skill covers schema design, resolvers, DataLoader for N+1 prevention, federation for microservices, and client integration with Apollo/urql. Key insight: GraphQL is a contract. The schema is the API documentation. Design it carefully.

624

guidewire-sdk-patterns

jeremylongshore

Master Guidewire SDK patterns including Digital SDK, REST API Client, and Gosu best practices. Use when implementing integrations, building frontends with Jutro, or writing server-side Gosu code. Trigger with phrases like "guidewire sdk", "digital sdk", "jutro sdk", "guidewire patterns", "gosu best practices", "rest api client".

215

groq-performance-tuning

jeremylongshore

Optimize Groq API performance with caching, batching, and connection pooling. Use when experiencing slow API responses, implementing caching strategies, or optimizing request throughput for Groq integrations. Trigger with phrases like "groq performance", "optimize groq", "groq latency", "groq caching", "groq slow", "groq batch".

111

openrouter-streaming-setup

jeremylongshore

Implement streaming responses with OpenRouter. Use when building real-time chat interfaces or reducing time-to-first-token. Trigger with phrases like 'openrouter streaming', 'openrouter sse', 'stream response', 'real-time openrouter'.

111

perplexity-multi-env-setup

jeremylongshore

Configure Perplexity across development, staging, and production environments. Use when setting up multi-environment deployments, configuring per-environment secrets, or implementing environment-specific Perplexity configurations. Trigger with phrases like "perplexity environments", "perplexity staging", "perplexity dev prod", "perplexity environment setup", "perplexity config by env".

210

Search skills

Search the agent skills registry