ApiUsageCard
Real-time API call tracking with usage limits and warnings
ApiUsageCard
The ApiUsageCard component displays real-time API usage tracking with visual progress indicators, warning thresholds, and upgrade prompts when approaching limits.
Plugin Required
This component requires the @bettercone/better-auth-plugin-usage-tracking plugin to be installed and configured in your Better Auth setup. See the UsageDashboard Plugin Installation guide.
This component is part of the UsageDashboard but can be used standalone for custom dashboards or widgets.
Installation
import { ApiUsageCard } from '@bettercone/ui';Features
- ✅ Real-Time Tracking - Live updates via Convex subscriptions
- ✅ Visual Progress - Color-coded progress bars
- ✅ Warning System - Alerts at 80% and 90% usage
- ✅ Upgrade Prompts - Contextual upgrade suggestions
- ✅ Number Formatting - Comma-separated thousands
- ✅ Percentage Display - Clear usage percentage badge
- ✅ Skeleton Loading - Smooth loading states
- ✅ Type-Safe - Full TypeScript support
Usage
import { ApiUsageCard } from '@/components/usage/api-usage-card';
export default function UsagePage() {
return (
<div className="grid gap-6 md:grid-cols-2">
<ApiUsageCard
current={8500}
limit={10000}
/>
</div>
);
}import { ApiUsageCard } from '@/components/usage/api-usage-card';
import { useRouter } from 'next/navigation';
export default function UsagePage() {
const router = useRouter();
return (
<ApiUsageCard
current={9200}
limit={10000}
onUpgrade={() => router.push('/pricing')}
/>
);
}import { ApiUsageCard } from '@/components/usage/api-usage-card';
export default function UsagePage() {
// Show warning at 70% instead of 80%
return (
<ApiUsageCard
current={7500}
limit={10000}
warningThreshold={70}
onUpgrade={() => router.push('/pricing')}
/>
);
}import { ApiUsageCard } from '@/components/usage/api-usage-card';
export default function DashboardWidget() {
// No upgrade button, just display
return (
<ApiUsageCard
current={5000}
limit={10000}
/>
);
}Props
Prop
Type
Warning States
The component shows different visual states based on usage:
| Usage | Badge | Progress Bar | Action |
|---|---|---|---|
| 0-79% | Default (gray) | Blue | None |
| 80-89% | Secondary (yellow) | Orange | "High Usage" warning |
| 90-100% | Destructive (red) | Red | "Upgrade Plan" button |
// Warning logic
const usagePercentage = (current / limit) * 100;
const isWarning = usagePercentage >= 80;
const isNearLimit = usagePercentage >= 90;Examples
Example 1: Usage Dashboard
import { ApiUsageCard, FeatureAccessCard } from '@bettercone/ui';
import { useRouter } from 'next/navigation';
export default function UsagePage() {
const router = useRouter();
const handleUpgrade = () => {
router.push('/pricing?feature=api-upgrade');
};
return (
<div className="container mx-auto py-8">
<h1 className="text-3xl font-bold mb-8">Usage & Limits</h1>
<div className="grid gap-6 lg:grid-cols-2">
<ApiUsageCard
current={85000}
limit={100000}
onUpgrade={handleUpgrade}
/>
<FeatureAccessCard
features={featureList}
onUpgrade={handleUpgrade}
/>
</div>
</div>
);
}Example 2: With Real-Time Convex Data
import { ApiUsageCard } from '@/components/usage/api-usage-card';
import { useQuery } from 'convex/react';
import { api } from '@/convex/_generated/api';
export default function UsagePage() {
const usageData = useQuery(api.usage.getCurrentUsage, {
userId: session?.user?.id,
});
return (
<ApiUsageCard
current={usageData?.apiCalls}
limit={usageData?.apiLimit}
onUpgrade={() => router.push('/pricing')}
/>
);
}Example 3: Dashboard Widget
import { ApiUsageCard } from '@/components/usage/api-usage-card';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
export default function DashboardPage() {
return (
<div className="grid gap-6 md:grid-cols-3">
{/* Other widgets */}
<Card>
<CardHeader>
<CardTitle>Quick Stats</CardTitle>
</CardHeader>
<CardContent>
<ApiUsageCard
current={45000}
limit={100000}
/>
</CardContent>
</Card>
</div>
);
}Example 4: With Alert Dialog
import { ApiUsageCard } from '@/components/usage/api-usage-card';
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
} from '@/components/ui/alert-dialog';
import { useState } from 'react';
export default function UsagePage() {
const [showUpgradeDialog, setShowUpgradeDialog] = useState(false);
const handleUpgrade = () => {
setShowUpgradeDialog(true);
};
return (
<>
<ApiUsageCard
current={9500}
limit={10000}
onUpgrade={handleUpgrade}
/>
<AlertDialog open={showUpgradeDialog} onOpenChange={setShowUpgradeDialog}>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>Upgrade Your Plan?</AlertDialogTitle>
<AlertDialogDescription>
You're approaching your API limit. Upgrade now to avoid service interruption.
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>Cancel</AlertDialogCancel>
<AlertDialogAction onClick={() => router.push('/pricing')}>
View Plans
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
</>
);
}Number Formatting
The component formats large numbers with commas for readability:
// Format: 1,234,567
current.toLocaleString() // 85,000
limit.toLocaleString() // 100,000
// Display: "85,000 / 100,000 calls"Progress Bar Colors
Progress bar color changes based on usage:
<Progress
value={usagePercentage}
className={
isNearLimit ? "*:bg-destructive" :
isWarning ? "*:bg-orange-500" :
"" // default blue
}
/>Warning Messages
Context-aware warning messages:
- 80-89% usage: "You're using more than 80% of your monthly API calls."
- 90-100% usage: "You're approaching your API limit. Upgrade to avoid service interruption."
Loading States
When data is undefined, shows skeleton placeholder:
if (current === undefined || limit === undefined) {
return <ApiUsageCardSkeleton />;
}Rate Limiting Integration
Track API calls in your application:
// In your API route
import { api } from '@/convex/_generated/api';
import { fetchMutation } from 'convex/nextjs';
export async function POST(request: Request) {
// Your API logic...
// Track the call
await fetchMutation(api.usage.trackApiCall, {
userId: session.user.id,
endpoint: request.url,
});
// Check if over limit
const usage = await fetchQuery(api.usage.getCurrentUsage, {
userId: session.user.id,
});
if (usage.apiCalls >= usage.apiLimit) {
return Response.json(
{ error: 'API limit exceeded' },
{ status: 429 }
);
}
return Response.json({ success: true });
}Implement rate limiting at the API level to prevent service abuse when users exceed their limits.
Upgrade Flow
When upgrade button is clicked:
- User sees warning message
- Clicks "Upgrade Plan"
- Redirected to pricing page
- Can select higher tier plan
- Checkout completes
- Usage limits immediately updated
Plan Limits
Typical API limits by plan:
| Plan | Monthly API Calls | Resets |
|---|---|---|
| Free | 10,000 | Monthly |
| Pro | 100,000 | Monthly |
| Team | Unlimited | N/A |
Reset Period
Usage resets monthly on subscription renewal:
// Next reset date
const resetDate = new Date(subscription.currentPeriodEnd);
// Display: "Resets on Nov 23, 2025"Notes
- Usage data updates in real-time via Convex
- Progress bar smoothly animates between states
- Skeleton shows while fetching initial data
- Upgrade button only appears when
onUpgradeis provided - Warning threshold customizable (default 80%)
- Near-limit threshold fixed at 90%
Related Components
- FeatureAccessCard - Display available features
- UsageDashboard - Complete usage view