Rate Limit Cooldown

Rate Limited
429

You are asking faster than we answer.

Nothing is broken and nothing was charged. The limit protects everyone sharing this endpoint, and it lifts on its own in under a minute.

Requests used

1,000

Allowed per minute

1,000

Window

Rolling 60s

Your window

The limit lifts in 45 seconds

Raise your limit
About this block

Rate Limit CooldownPRO

Too-many-requests page with a square eyebrow and status code, an accent headline, three bordered usage cells, and a live cooldown counter that swaps itself for a retry seal button when it reaches zero.

Error25: Rate Limit Cooldown

A too-many-requests page that counts itself down: three bordered cells state the usage window, and a live counter holds the place of the retry button until the limit lifts, at which point the seal button appears.

Upgrade to Pro

Pro blocks install through the shadcn CLI with your license key and ship their full source. Docs and live previews stay open to everyone, so you can read every block's details first.

Upgrade Now

Installation

Swap YOUR_EMAIL and YOUR_KEY for the email and license key on your account. Find your license key on your account page.

Radix flavor

bash
npx shadcn add "https://ui.beste.co/r/error25?email=YOUR_EMAIL&license_key=YOUR_KEY"

Base UI flavor

bash
npx shadcn add "https://ui.beste.co/r-base/error25?email=YOUR_EMAIL&license_key=YOUR_KEY"

This installs the block to components/beste/block/error25.tsx, plus the two design-system primitives it renders through: badge6 for the square eyebrow and button1 for the retry seal button.

Quick start

The installed file exports error25Demo alongside the block: the exact props behind the preview above. Spread it and the cooldown starts counting immediately.

tsx
import { Error25, error25Demo } from "@/components/beste/block/error25";

export default function RateLimited() {
  return <Error25 {...error25Demo} />;
}

Feed cooldownSeconds from your Retry-After header and handle the retry yourself:

tsx
"use client";

import { Error25 } from "@/components/beste/block/error25";

export default function RateLimited({ retryAfter }: { retryAfter: number }) {
  return (
    <Error25
      label="Rate Limited"
      code="429"
      heading="You are asking <strong>faster than we answer</strong>."
      description="Nothing is broken and nothing was charged. The limit lifts on its own."
      labels={{
        retry: "Send it again",
        waiting: "The limit lifts in",
        seconds: "seconds",
        usageTitle: "Your window",
      }}
      cooldownSeconds={retryAfter}
      stats={[
        { title: "Requests used", value: "1,000" },
        { title: "Allowed per minute", value: "1,000" },
        { title: "Window", value: "Rolling 60s" },
      ]}
      link={{ label: "Raise your limit", href: "/pricing" }}
      onRetry={() => location.reload()}
    />
  );
}

Props

PropTypeDefaultDescription
labelstring–Eyebrow text rendered through the square-marker badge
codestring–Status code shown after the eyebrow divider
headingstring–Headline HTML, rendered as an h1; <strong> marks the accent clause
descriptionstring–Explanatory paragraph under the headline
labelsError25Labels{}Retry text, waiting sentence, unit word, and the caption under the cells
cooldownSecondsnumber–Starting value of the cooldown counter
statsUsageStat[][]Bordered cells describing the limit
linkActionLink–Text link beside the counter or button
onRetry() => void–Click handler for the retry button
classNamestring–Extra classes for the outer <section>
ts
type Error25Labels = {
  retry?: string;
  waiting?: string;
  seconds?: string;
  usageTitle?: string;
};

type UsageStat = {
  title: string;
  value: string;
};

type ActionLink = {
  label: string;
  href: string;
};

Behavior notes

  • The counter and the retry button occupy the same slot: while remaining is above zero the block shows the waiting sentence, and at zero it swaps in the seal button. Nothing is disabled and greyed out, so there is no dead control on screen.
  • The countdown runs once and stops at zero. It does not restart, and pressing retry does not reset it, so a second cooldown needs a fresh mount or a new cooldownSeconds value.
  • Passing no cooldownSeconds (or 0) starts the block in the retry state, which is the right shape when your API does not send a Retry-After value.
  • The waiting line is marked aria-live="polite", so screen readers hear the remaining time without the page grabbing focus every second.
  • labels.usageTitle renders as a caption underneath the cells, not above them, so the numbers lead and the explanation follows.

More Error blocks

View all Error
PRO

error15

500 Incident Report

FREE

error35

Server Error With Live Status

PRO

error17

Maintenance Window Notice

Get Pro access