Credit System Documentation
Overview
AppVector uses a dynamic credit system built on top of the Feature framework. Credits are consumable resources that track API calls, data scrapes, and other metered usage. The system is fully flexible - new credit types can be added without code changes.
Architecture
How Credits Work
Credits are implemented as CREDIT_BASED features in the dynamic feature system:
- Feature: Defines a credit type (e.g., "API Calls", "Data Scrapes")
- FeaturePlan: Sets how many credits each plan gets per period
- FeatureUsage: Tracks consumption and balances per workspace
Credit Components
Each credit balance consists of:
- Allocated Credits: Monthly/periodic credits from your plan
- Purchased Credits: Additional credits you buy (roll over)
- Bonus Credits: One-time credits given by admin (don't reset)
- Consumed Credits: How many you've used this period
Available Credits = Allocated + Purchased + Bonus - Consumed
Reset Periods
Credits can reset on different schedules: - DAILY: Reset every 24 hours - WEEKLY: Reset every 7 days - MONTHLY: Reset on billing cycle (most common) - NEVER: Never reset (for purchased credits)
API Endpoints
Get Credit Balances
Returns all credit balances for a workspace:
{
"balances": [
{
"feature_key": "api_calls",
"feature_name": "API Calls",
"category": "usage",
"allocated": 5000,
"consumed": 1234,
"purchased": 500,
"bonus": 100,
"available": 4366,
"usage_percentage": 21.9,
"period_start": "2024-01-01T00:00:00Z",
"period_end": "2024-02-01T00:00:00Z",
"reset_period": "MONTHLY"
},
{
"feature_key": "data_scrapes",
"feature_name": "Data Scrapes",
"allocated": 1000,
"consumed": 450,
"available": 550,
"usage_percentage": 45.0
}
],
"summary": {
"total_features": 2,
"total_consumed": 1684,
"total_available": 4916,
"features_near_limit": 0
}
}
Consume Credits
Request body:
{
"workspace_id": "uuid",
"feature_key": "api_calls",
"amount": 10,
"metadata": {
"endpoint": "/api/apps/search",
"request_id": "req_123"
}
}
Response:
Add Bonus Credits (Admin)
Request body:
{
"workspace_id": "uuid",
"feature_key": "api_calls",
"amount": 1000,
"reason": "Compensation for service outage"
}
Get Usage History
Returns historical credit usage for analysis.
Credit Types
Standard Credit Features
These are the default credit types configured in the system:
| Feature Key | Name | Description | Reset |
|---|---|---|---|
api_calls |
API Calls | REST API requests | Monthly |
data_scrapes |
Data Scrapes | App store data fetches | Monthly |
keyword_tracking |
Keyword Tracking | Keyword rank checks | Monthly |
competitor_tracking |
Competitor Tracking | Competitor analysis | Monthly |
ai_analysis |
AI Analysis | AI-powered insights | Monthly |
data_exports |
Data Exports | CSV/JSON exports | Monthly |
Plan Allocations
| Plan | API Calls | Data Scrapes | Keyword Tracking | AI Analysis |
|---|---|---|---|---|
| FREE | 100/mo | 50/mo | 10/mo | 0 |
| STARTER | 1,000/mo | 500/mo | 100/mo | 10/mo |
| PROFESSIONAL | 5,000/mo | 2,500/mo | 500/mo | 100/mo |
| ENTERPRISE | 20,000/mo | 10,000/mo | 2,000/mo | 1,000/mo |
| INTERNAL | Unlimited | Unlimited | Unlimited | Unlimited |
Integration Guide
Checking Credits Before Operations
# In your service layer
from services.billing import DynamicFeatureService
def scrape_app_data(workspace, app_id):
# Check if enough credits
feature_key = 'data_scrapes'
required_credits = 1
available = DynamicFeatureService.get_feature_value(
workspace, feature_key
)
if available < required_credits:
raise InsufficientCreditsError(
f"Need {required_credits} credits, only {available} available"
)
# Consume credits
success, remaining, usage = DynamicFeatureService.consume_credits(
workspace=workspace,
feature_key=feature_key,
amount=required_credits,
metadata={'app_id': app_id}
)
if not success:
raise InsufficientCreditsError("Failed to consume credits")
# Proceed with operation
data = perform_scrape(app_id)
return data
Adding New Credit Types
-
Create a new Feature via Django admin:
-
Link to plans via FeaturePlan:
Credit Purchasing
One-Time Credit Purchase Flow
- User needs more credits
- Frontend calls:
POST /api/billing/stripe/checkout/credits/ - User redirected to Stripe
- Payment processed
- Webhook adds credits to
purchased_amount - Credits immediately available
Pricing
- API Calls: $0.01 per credit
- Data Scrapes: $0.05 per credit
- AI Analysis: $0.10 per credit
- Custom pricing available for Enterprise
Best Practices
1. Always Check Before Consuming
Never assume credits are available. Always check first:
if not DynamicFeatureService.check_feature_available(workspace, 'api_calls'):
return error_response("Feature not available for your plan")
2. Use Metadata for Tracking
Include relevant metadata when consuming credits:
metadata = {
'user_id': request.user.id,
'endpoint': request.path,
'ip_address': request.META.get('REMOTE_ADDR'),
'timestamp': timezone.now().isoformat()
}
3. Handle Insufficient Credits Gracefully
try:
success, remaining, usage = DynamicFeatureService.consume_credits(...)
if not success:
# Offer to purchase more credits
return Response({
'error': 'Insufficient credits',
'remaining': remaining,
'purchase_url': '/billing/credits/purchase'
}, status=402) # Payment Required
except Exception as e:
logger.error(f"Credit consumption failed: {e}")
# Allow operation but log for investigation
pass
4. Monitor Usage Patterns
Set up alerts when workspaces approach limits:
for usage in FeatureUsage.objects.filter(
feature__feature_type=FeatureType.CREDIT_BASED
):
if usage.usage_percentage() > 80:
send_credit_warning_email(usage.workspace)
Migration from Legacy System
If migrating from the old CreditBalance/CreditTransaction models:
- Map old credit types to features:
- CreditType.API_CALL → 'api_calls' feature
-
CreditType.DATA_SCRAPE → 'data_scrapes' feature
-
Migrate balances:
for old_balance in CreditBalance.objects.all(): feature = Feature.objects.get(key=map_credit_type(old_balance.credit_type)) FeatureUsage.objects.create( workspace=old_balance.workspace, feature=feature, allocated_amount=old_balance.allocated_credits, consumed_amount=old_balance.consumed_credits, purchased_amount=old_balance.purchased_credits, bonus_amount=old_balance.bonus_credits, period_start=old_balance.period_start, period_end=old_balance.period_end ) -
Update service calls:
- Replace
CreditService.consume()withDynamicFeatureService.consume_credits() - Replace
CreditBalance.available_credits()withFeatureUsage.available_amount()
Troubleshooting
Credits Not Resetting
Check the reset period configuration:
usage = FeatureUsage.objects.get(workspace=workspace, feature__key='api_calls')
print(f"Period ends: {usage.period_end}")
print(f"Needs reset: {usage.needs_reset()}")
Negative Credit Balance
This shouldn't happen with proper checking, but if it does:
usage.consumed_amount = min(
usage.consumed_amount,
usage.allocated_amount + usage.purchased_amount + usage.bonus_amount
)
usage.save()
Credit Consumption Not Tracked
Ensure the feature exists and is active:
Webhooks
Credit-related webhook events:
payment_intent.succeeded- Adds purchased creditsinvoice.payment_succeeded- Monthly subscription renewal resets creditscustomer.subscription.updated- Plan change updates allocated credits
FAQ
Q: What happens to unused credits at the end of the period? A: Allocated credits don't roll over. Purchased and bonus credits do.
Q: Can I transfer credits between workspaces? A: No, credits are workspace-specific and non-transferable.
Q: How do I give free credits to a customer? A: Use the bonus credits endpoint (admin only) or create a coupon in Stripe.
Q: Can I set custom credit prices per customer? A: Yes, use custom FeaturePlan entries for enterprise customers.
Q: How do I track credit usage by user within a workspace? A: Include user_id in the metadata when consuming credits, then query by metadata.