Skip to content
OpenSmartRoute
Skillv1.0.0

hex-rate-limits

Implement Hex rate limiting, backoff, and idempotency patterns. Use when handling rate limit errors, implementing retry logic, or optimizing API request throughput for Hex. Trigger with phrases like "

by jeremylongshore(0) 0 installs
Free
Sign in to install

Free account. Installing gives you the manifest plus copy-paste snippets.

See reviews

About

Imported from jeremylongshore/tons-of-skills-marketplace (skills/.curated/hex-rate-limits/SKILL.md). Install upstream with npx skills add jeremylongshore/tons-of-skills-marketplace --skill hex-rate-limits. Copyright stays with the author (MIT).

Hex Rate Limits

Overview

Hex's API enforces tight limits on project run triggers (20 per minute, 60 per hour) while leaving read operations like status checks and project listing largely unthrottled. Data teams scheduling batch analytics runs or triggering parameterized notebooks from CI/CD pipelines must carefully manage the hourly cap, since a single pipeline triggering 15 projects can consume a quarter of the hourly budget. Polling run status is free, but triggering runs is the bottleneck that shapes integration architecture.

Rate Limit Reference

Endpoint Limit Window Scope
RunProject (trigger) 20 req 1 minute Per API token
RunProject (trigger) 60 req 1 hour Per API token
GetRunStatus No hard limit - Per API token
ListProjects No hard limit - Per API token
CancelRun No hard limit - Per API token

Rate Limiter Implementation

class HexRateLimiter {
  private minuteTokens: number = 20;
  private hourlyTokens: number = 60;
  private lastMinuteRefill: number = Date.now();
  private lastHourlyRefill: number = Date.now();
  private queue: Array<{ resolve: () => void }> = [];

  async acquire(): Promise<void> {
    this.refill();
    if (this.minuteTokens >= 1 && this.hourlyTokens >= 1) {
      this.minuteTokens -= 1;
      this.hourlyTokens -= 1;
      return;
    }
    return new Promise(resolve => this.queue.push({ resolve }));
  }

  private refill() {
    const now = Date.now();
    this.minuteTokens = Math.min(20, this.minuteTokens + ((now - this.lastMinuteRefill) / 60_000) * 20);
    this.lastMinuteRefill = now;
    this.hourlyTokens = Math.min(60, this.hourlyTokens + ((now - this.lastHourlyRefill) / 3_600_000) * 60);
    this.lastHourlyRefill = now;
    while (this.minuteTokens >= 1 && this.hourlyTokens >= 1 && this.queue.length) {
      this.minuteTokens -= 1;
      this.hourlyTokens -= 1;
      this.queue.shift()!.resolve();
    }
  }
}

const runLimiter = new HexRateLimiter();

Retry Strategy

async function hexRunWithRetry(
  projectId: string, params: Record<string, any>, maxRetries = 3
): Promise<any> {
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
    await runLimiter.acquire();
    const res = await fetch(`${HEX_BASE}/api/v1/run/${projectId}`, {
      method: "POST", headers,
      body: JSON.stringify({ inputParams: params }),
    });
    if (res.ok) return res.json();
    if (res.status === 429) {
      const delay = 30_000 * Math.pow(2, attempt) + Math.random() * 5000;
      await new Promise(r => setTimeout(r, delay));
      continue;
    }
    if (res.status >= 500 && attempt < maxRetries) {
      await new Promise(r => setTimeout(r, Math.pow(2, attempt) * 3000));
      continue;
    }
    throw new Error(`Hex API ${res.status}: ${await res.text()}`);
  }
  throw new Error("Max retries exceeded");
}

Batch Processing

async function batchRunProjects(projects: Array<{ id: string; params: any }>, batchSize = 5) {
  const results: any[] = [];
  for (let i = 0; i < projects.length; i += batchSize) {
    const batch = projects.slice(i, i + batchSize);
    const runs = await Promise.all(
      batch.map(p => hexRunWithRetry(p.id, p.params))
    );
    // Poll for completion
    for (const run of runs) {
      let status = run;
      while (status.status === "RUNNING") {
        await new Promise(r => setTimeout(r, 5000));
        const res = await fetch(`${HEX_BASE}/api/v1/run/${run.runId}/status`, { headers });
        status = await res.json();
      }
      results.push(status);
    }
    if (i + batchSize < projects.length) await new Promise(r => setTimeout(r, 15_000));
  }
  return results;
}

Error Handling

Issue Cause Fix
429 on RunProject Exceeded 20/min or 60/hour trigger limit Queue runs, space 5s apart minimum
Run stuck in RUNNING Long-running query or compute timeout Poll up to 30 min, then CancelRun
401 on scheduled run API token rotated Refresh token in CI secrets before batch
Empty run output Project has no published outputs Verify project has published cells
409 concurrent run Same project triggered twice Check run status before re-triggering

Prerequisites

  • An approved request budget, observed header baseline, and sandbox run workload using opaque project IDs.
  • A bounded queue, idempotency key for mutations, and reviewed recovery path for exhausted work.

Instructions

  1. Classify requests by project and operation, applying per-scope concurrency limits before dispatch.
  2. Honor server retry guidance when available; otherwise use bounded exponential backoff with jitter and a maximum attempt count.
  3. Never replay a run, publish, or schedule mutation without its idempotency key, and defer nonessential work before backlog threatens freshness.
  4. Monitor aggregate limited/deferred counts and synthetic canary success, tuning a single scope at a time with rollback.
  5. Route exhausted work to reviewed recovery rather than silently dropping or duplicating runs.

Output

Return a rate-limit receipt with scope, requested/limited/deferred counts, retry revision, idempotency state, queue health, canary result, and rollback reference. Exclude query text, SQL, output, and credentials.

Examples

scope=sandbox-projects; requested=100; limited=3; deferred=3; retry=v2; idempotent=pass; queue=healthy; rollback=limits-r7 proves bounded handling.

Resources

Next Steps

See hex-performance-tuning.

Use it

Copy one of these into your project. Installing also returns the manifest and these snippets.

yaml
targets:
  - https://api.opensmartroute.ai/api/v1/registry/jeremylongshore-tons-of-skills-marketplace-hex-rate-limits/manifest   # or paste the manifest below

Manifest

An Open Capability Manifest: the router reads it to know what this does, what it costs and when to pick it.

jeremylongshore-tons-of-skills-marketplace-hex-rate-limits.ocm.jsonjson
{
  "ocm": "1",
  "id": "jeremylongshore-tons-of-skills-marketplace-hex-rate-limits",
  "kind": "skill",
  "name": "hex-rate-limits",
  "description": "Implement Hex rate limiting, backoff, and idempotency patterns. Use when handling rate limit errors, implementing retry logic, or optimizing API request throughput for Hex. Trigger with phrases like \"hex rate limit\", \"hex throttling\", \"hex 429\", \"hex retry\", \"hex backoff\".",
  "publisher": "jeremylongshore",
  "version": "1.0.0",
  "capabilities": {
    "domains": [
      "coding"
    ],
    "tags": [
      "skill-md",
      "saas",
      "hex",
      "data",
      "analytics",
      "skills-sh"
    ],
    "languages": [
      "en"
    ]
  },
  "quality_prior": 0.6,
  "examples": [
    "Implement Hex rate limiting, backoff, and idempotency patterns. Use when handling rate limit errors, implementing retry logic, or optimizing API request throughput for Hex. Trigger with phrases like \"hex rate limit\", \"hex throttling\", \"hex 429\", \"hex retry\", \"hex backoff\"."
  ],
  "primary": false,
  "metadata": {
    "source": {
      "provider": "skills.sh",
      "repository": "https://github.com/jeremylongshore/tons-of-skills-marketplace",
      "path": "skills/.curated/hex-rate-limits/SKILL.md",
      "ref": "HEAD",
      "url": "https://github.com/jeremylongshore/tons-of-skills-marketplace/blob/HEAD/skills/.curated/hex-rate-limits/SKILL.md",
      "key": "jeremylongshore/tons-of-skills-marketplace/skills/.curated/hex-rate-limits/SKILL.md"
    },
    "compatibility": "Designed for Claude Code",
    "allowed_tools": [
      "Read,",
      "Write,",
      "Edit"
    ],
    "license": "MIT"
  },
  "instructions": "# Hex Rate Limits\n\n## Overview\n\nHex's API enforces tight limits on project run triggers (20 per minute, 60 per hour) while leaving read operations like status checks and project listing largely unthrottled. Data teams scheduling batch analytics runs or triggering parameterized notebooks from CI/CD pipelines must carefully manage the hourly cap, since a single pipeline triggering 15 projects can consume a quarter of the hourly budget. Polling run status is free, but triggering runs is the bottleneck that shapes integration architecture.\n\n## Rate Limit Reference\n\n| Endpoint | Limit | Window | Sc",
  "cost": {
    "context_tokens": 1416
  }
}

Fetch it by URL: GET /api/v1/registry/jeremylongshore-tons-of-skills-marketplace-hex-rate-limits/manifest?version=1.0.0

Reviews

Star ratings from people who tried it. One review per account; edit yours any time.

No reviews yet. Install it, try it, and be the first to rate it.