GlexirDocs
Dashboard

Billing & limits

Rate limits & spend limits

Glexir limits each organization in two ways:

  • Rate limits cap how many requests you send: requests per minute and concurrent requests. They are set by your trust tier. Going over returns 429.
  • Spend limits cap how much you spend in a calendar month: an optional organization budget and optional per-key limits. You set them. Going over returns 402.

Handle limit errors

The SDK retries 429 responses for you, up to maxRetries times (default 2). It waits for the retry-after header when the server sends one (up to 60 seconds), and backs off exponentially otherwise. It never retries 402.

TypeScript
import Glexir, { APIError, RateLimitError } from "@glexir/sdk";

const glexir = new Glexir({
  apiKey: process.env.GLEXIR_API_KEY,
  maxRetries: 4, // retries for 408, 409, 429 and 5xx responses
});

try {
  const response = await glexir.responses.create(
    { model: "gpt-5.4-mini", input: "Summarize this ticket in one line.", max_output_tokens: 100 },
    { maxRetries: 6 }, // override for this call only
  );
  console.log(response.output_text);
} catch (err) {
  if (err instanceof RateLimitError) {
    // Retries ran out. code is rate_limit_exceeded, concurrency_limit_exceeded or rate_limited.
    console.error(err.code, "retry after", err.headers?.get("retry-after") ?? "unknown", "seconds");
  } else if (err instanceof APIError && err.status === 402 && err.code === "spend_limit_exceeded") {
    // Retrying won't help until next month or until the limit is raised.
    console.error("Monthly limit reached for", err.param); // "organization" or "api_key"
  }
  throw err;
}

Retries are safe. The SDK sends one Idempotency-Key per call and reuses it on every retry, so a retried request is never run or billed twice. A 429 frees the key, so the retry goes through as a new attempt.

Trust tiers

Every organization has a trust tier from 0 to 3. New organizations start at tier 0. The tier sets your rate limits, the service tiers you can request, and your overdraft:

Trust tierRequests per minuteConcurrent requestsService tiersOverdraft
Tier 0604standard, flex, batchNone
Tier 150020standard, flex, batchNone
Tier 23,000100standard, flex, batch, fast$25
Tier 310,000300standard, flex, batch, fast, ultrafast$250
  • Service tiers are the service_tier values you can request. Asking for another returns 403 tier_not_allowed. See Service tiers.
  • Overdraft lets holds take your available credit below zero by up to that amount, so a request isn't refused for a few cents.

The tier also caps top-ups and auto recharge:

Trust tierLargest single top-upLargest auto rechargeAuto recharge monthly cap
Tier 0$100$100$500
Tier 1$500$200$1,000
Tier 2$2,000$1,000$5,000
Tier 3$10,000$5,000$50,000

Moving up a tier

The Limits page in the dashboard shows your current tier and your progress toward the next one. The bar for each tier is:

Trust tierLifetime credit purchasedDays since first payment
Tier 0Starting tier-
Tier 1$507
Tier 2$50030
Tier 3$5,00060

When you meet it, use Request higher limits on the Limits page. If you need limits above tier 3, use the same link.

Requests per minute

Each organization may send its tier's number of requests per minute, across all of its API keys. The count covers every authenticated /v1 request, including GET requests such as listing models or retrieving a response.

The limit is counted over 60-second windows. Once you reach it, further requests get:

JSON
{
  "error": {
    "message": "Rate limit reached: 60 requests per minute for your account tier.",
    "type": "rate_limit_error",
    "param": null,
    "code": "rate_limit_exceeded"
  }
}

The response is 429 with a retry-after header, in whole seconds, of at most 60. Wait that long before sending more. The SDK does this for you.

On api.glexir.com the counters are kept per Cloudflare location and are eventually consistent, so the limit is approximate: a burst spread across locations can briefly go over it, and retry-after is always 60. Treat the tier's number as a ceiling to stay under, not an exact quota.

Concurrency

Each organization may have a limited number of requests in flight at once. A request counts from the moment its credit is held until it is settled or released. See Holds and settlement.

The count includes:

  • streaming requests, until the stream ends,
  • background responses, until they finish,
  • batches, for their whole run, which can be up to 24 hours.

A request over the limit gets 429, type rate_limit_error, code concurrency_limit_exceeded. There is no retry-after header, so the SDK backs off exponentially: about 0.5, 1, 2 and 4 seconds, up to 8 seconds per retry.

Requests that don't hold credit, such as listing models or files, don't count toward concurrency.

Stay under the limit yourself

Cap the number of requests your code runs at once at your tier's limit. That is cheaper than relying on retries.

TypeScript
import Glexir from "@glexir/sdk";

const glexir = new Glexir();
const MAX_IN_FLIGHT = 4; // your tier's concurrency limit

async function runAll(prompts: string[]): Promise<string[]> {
  const results: string[] = new Array(prompts.length);
  let next = 0;
  async function worker() {
    while (next < prompts.length) {
      const i = next++;
      const response = await glexir.responses.create({ model: "gpt-5.4-mini", input: prompts[i]!, max_output_tokens: 200 });
      results[i] = response.output_text;
    }
  }
  await Promise.all(Array.from({ length: Math.min(MAX_IN_FLIGHT, prompts.length) }, worker));
  return results;
}

Spend limits

Spend limits stop requests before they would push spending past a monthly amount. They are checked when the request's credit is held, so a request that would cross the limit is refused rather than cut off midway. Months are calendar months, and spending resets at the start of each month.

Organization budget

Set a monthly budget for the whole organization on the dashboard's Limits page, under Organization budget. Leave it blank for no budget. Only owners and admins can change it.

A request is refused when this month's spending, plus credit already held for running requests, plus this request's hold would be more than the budget. The error is:

JSON
{
  "error": {
    "message": "This request would exceed your organization's monthly limit of $500.00.",
    "type": "insufficient_quota",
    "param": "organization",
    "code": "spend_limit_exceeded"
  }
}

You can also add usage alert thresholds at percentages of the budget. Alerts never block requests; only the budget does.

Per-key limits

Give an API key its own monthly limit when you create or edit it on the API keys page. The key is refused when its spending this month, plus its running holds, plus the new request's hold would be more than the limit. Other keys keep working.

JSON
{
  "error": {
    "message": "This request would exceed the API key's monthly limit of $50.00.",
    "type": "insufficient_quota",
    "param": "api_key",
    "code": "spend_limit_exceeded"
  }
}

Both errors are 402. Holds are worst-case estimates, so a request can be refused while there is still some room under the limit. Setting max_output_tokens keeps holds close to real cost. See Holds and settlement.

Other 429 and 503 errors

These come from OpenAI capacity, not your limits:

HTTPcodeMeaning
429rate_limitedOpenAI is busy. Retry shortly. Flex requests can get this when there is no flex capacity. Not charged.
503capacity_unavailableGlexir is temporarily out of upstream capacity. Retry later. Not charged.

The SDK retries both.

Limit errors reference

HTTPtypecodeparamRetry?
429rate_limit_errorrate_limit_exceedednullYes, after retry-after seconds (at most 60).
429rate_limit_errorconcurrency_limit_exceedednullYes, when one of your running requests finishes.
429rate_limit_errorrate_limitednullYes, shortly.
402insufficient_quotaspend_limit_exceededorganization or api_keyNo. Raise the limit or wait for next month.
402insufficient_quotacredit_balance_exhaustednullNo. Add credit.
403permission_errortier_not_allowedservice_tierNo. Use an allowed service tier.

Errors carry an x-request-id header. In the SDK it is err.requestID. Include it when you contact support.