Skip to content

API Reference

Complete reference for all AV API endpoints.

Base URL

Development: http://localhost:8000
Production: https://api.yourdomain.com

Authentication

All endpoints except /api/auth/google/ and /api/token/refresh/ require authentication.

Include the access token in the Authorization header:

Authorization: Bearer YOUR_ACCESS_TOKEN

Endpoints Overview

Endpoint Method Auth Required Purpose
/api/auth/google/ POST No Exchange Google token for JWT
/api/token/refresh/ POST No Refresh expired access token
/api/me/ GET Yes Get current user profile
/api/aso/android-search/ POST Yes Search Android apps in Google Play Store
/api/aso/apple-search/ POST Yes Search Apple apps in App Store
/api/aso/android-details/ POST Yes Get Android app details (with caching)
/api/aso/android-history/ POST Yes Get Android app change history
/api/aso/apple-details/ POST Yes Get Apple app details (with caching)
/api/aso/apple-history/ POST Yes Get Apple app change history
Billing Endpoints
/api/billing/plans/ GET No List available subscription plans
/api/billing/plans/{id}/ GET No Get plan details
/api/billing/subscription/ GET Yes Get current subscription
/api/billing/subscription/upgrade/ POST Yes Upgrade subscription
/api/billing/subscription/downgrade/ POST Yes Downgrade subscription
/api/billing/subscription/cancel/ POST Yes Cancel subscription
/api/billing/credits/ GET Yes Get credit balances
/api/billing/credits/consume/ POST Yes Consume credits
/api/billing/credits/bonus/ POST Yes Add bonus credits
/api/billing/credits/history/ GET Yes Get credit usage history
/api/billing/usage/ GET Yes Get usage statistics
/api/billing/limits/ GET Yes Get plan limits
/api/billing/features/ GET Yes List all features
/api/billing/features/workspace/ GET Yes Get workspace features
/api/billing/features/check/ POST Yes Check feature availability
/api/billing/features/consume/ POST Yes Consume feature usage
/api/billing/invoices/ GET Yes List invoices
/api/billing/invoices/{id}/ GET Yes Get invoice details
/api/billing/stripe/checkout/ POST Yes Create Stripe checkout session
/api/billing/stripe/checkout/credits/ POST Yes Create credit purchase session
/api/billing/stripe/portal/ GET Yes Create customer portal session
/api/billing/stripe/payment-history/ GET Yes Get payment history from Stripe
/api/billing/stripe/upcoming-invoice/ GET Yes Preview upcoming invoice
/api/billing/webhooks/stripe/ POST No Stripe webhook endpoint (CSRF exempt)

POST /api/auth/google/

Exchange a Google OAuth 2.0 access token for JWT tokens.

Request

POST /api/auth/google/
Content-Type: application/json

{
  "access_token": "GOOGLE_OAUTH_TOKEN"
}

Parameters:

Field Type Required Description
access_token string Yes Google OAuth 2.0 access token

Response (201 Created)

{
  "access": "eyJ0eXAiOiJKV1QiLCJhbGc...",
  "refresh": "eyJ0eXAiOiJKV1QiLCJhbGc...",
  "user": {
    "id": 1,
    "email": "[email protected]",
    "first_name": "John",
    "last_name": "Doe"
  }
}

Response Fields:

Field Type Description
access string JWT access token (60 min lifetime)
refresh string JWT refresh token (1 day lifetime)
user.id integer User's unique ID
user.email string User's email address
user.first_name string User's first name
user.last_name string User's last name

Error Responses

400 Bad Request - Invalid Google token

{
  "error": "Invalid token",
  "detail": "The token is invalid or has expired"
}

Code Examples

const response = await fetch('http://localhost:8000/api/auth/google/', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    access_token: googleToken
  })
});

const data = await response.json();
console.log('JWT tokens:', data);
import requests

response = requests.post(
    'http://localhost:8000/api/auth/google/',
    json={'access_token': google_token}
)

data = response.json()
print('JWT tokens:', data)
curl -X POST http://localhost:8000/api/auth/google/ \
  -H "Content-Type: application/json" \
  -d '{"access_token":"GOOGLE_OAUTH_TOKEN"}'

POST /api/token/refresh/

Get a new access token using a refresh token.

Request

POST /api/token/refresh/
Content-Type: application/json

{
  "refresh": "eyJ0eXAiOiJKV1QiLCJhbGc..."
}

Parameters:

Field Type Required Description
refresh string Yes JWT refresh token

Response (200 OK)

{
  "access": "eyJ0eXAiOiJKV1QiLCJhbGc..."
}

Response Fields:

Field Type Description
access string New JWT access token (60 min lifetime)

Error Responses

401 Unauthorized - Refresh token expired or invalid

{
  "detail": "Token is invalid or expired",
  "code": "token_not_valid"
}

Code Examples

const response = await fetch('http://localhost:8000/api/token/refresh/', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    refresh: refreshToken
  })
});

const data = await response.json();
console.log('New access token:', data.access);
import requests

response = requests.post(
    'http://localhost:8000/api/token/refresh/',
    json={'refresh': refresh_token}
)

data = response.json()
print('New access token:', data['access'])
curl -X POST http://localhost:8000/api/token/refresh/ \
  -H "Content-Type: application/json" \
  -d '{"refresh":"REFRESH_TOKEN"}'

GET /api/me/

Get the authenticated user's profile information.

Request

GET /api/me/
Authorization: Bearer YOUR_ACCESS_TOKEN

Response (200 OK)

{
  "id": 1,
  "email": "[email protected]",
  "first_name": "John",
  "last_name": "Doe",
  "date_joined": "2025-01-15T10:30:00Z"
}

Response Fields:

Field Type Description
id integer User's unique ID
email string User's email address
first_name string User's first name
last_name string User's last name
date_joined string (ISO 8601) When user first authenticated

Error Responses

401 Unauthorized - Missing or invalid access token

{
  "detail": "Authentication credentials were not provided."
}

401 Unauthorized - Access token expired

{
  "detail": "Given token not valid for any token type",
  "code": "token_not_valid"
}

Code Examples

const response = await fetch('http://localhost:8000/api/me/', {
  headers: {
    'Authorization': `Bearer ${accessToken}`
  }
});

const profile = await response.json();
console.log('User profile:', profile);
import requests

headers = {'Authorization': f'Bearer {access_token}'}
response = requests.get(
    'http://localhost:8000/api/me/',
    headers=headers
)

profile = response.json()
print('User profile:', profile)
curl http://localhost:8000/api/me/ \
  -H "Authorization: Bearer ACCESS_TOKEN"

Error Codes

HTTP Status Codes

Code Meaning
200 Success
201 Created (authentication successful)
400 Bad Request (invalid input)
401 Unauthorized (missing/invalid/expired token)
403 Forbidden (no permission)
404 Not Found
500 Internal Server Error

Common Error Responses

Missing Authorization Header:

{
  "detail": "Authentication credentials were not provided."
}

Invalid Token:

{
  "detail": "Given token not valid for any token type",
  "code": "token_not_valid"
}

Invalid Google Token:

{
  "error": "Invalid token",
  "detail": "The token is invalid or has expired"
}


Rate Limiting

Currently no rate limiting is implemented. This will be added in future versions.


CORS

CORS is configured to allow requests from specified origins. Configure in .env:

CORS_ALLOWED_ORIGINS=http://localhost:3000,https://yourdomain.com

Interactive API Explorer

Try these endpoints live in the interactive API documentation:

Open API Explorer →

The Scalar UI provides: - Interactive request testing - Code examples in multiple languages - Automatic schema validation - Real-time responses


POST /api/aso/android-search/

Search for Android apps in Google Play Store.

Request

POST /api/aso/android-search/
Authorization: Bearer YOUR_ACCESS_TOKEN
Content-Type: application/json

{
  "keyword": "messaging",
  "country": "us",
  "language": "en"
}

Parameters:

Field Type Required Default Description
keyword string Yes - Search keyword for Android apps
country string No "us" 2-letter country code
language string No "en" 2-letter language code

Response (200 OK)

{
  "keyword": "messaging",
  "country": "us",
  "language": "en",
  "results": [
    {
      "title": "Google Messages",
      "appId": "com.google.android.apps.messaging",
      "url": "https://play.google.com/store/apps/details?id=com.google.android.apps.messaging",
      "icon": "https://play-lh.googleusercontent.com/...",
      "developer": "Google LLC",
      "price": 0,
      "free": true,
      "summary": "Google Messages is the official Google app for messaging...",
      "score": 4.57,
      "installs": "5,000,000,000+"
    },
    {
      "title": "Messenger",
      "appId": "com.facebook.orca",
      "url": "https://play.google.com/store/apps/details?id=com.facebook.orca",
      "developer": "Meta Platforms, Inc.",
      "price": 0,
      "free": true,
      "summary": "Messenger is a free messaging app...",
      "score": 4.70,
      "installs": "5,000,000,000+"
    }
  ],
  "count": 2
}

Response Fields:

Field Type Description
keyword string The search keyword used
country string Country code used for search
language string Language code used for search
results array List of matching apps
results[].title string App title
results[].appId string Android package ID
results[].url string Play Store URL
results[].icon string App icon URL
results[].developer string Developer name
results[].price number App price
results[].free boolean Whether app is free
results[].summary string App description summary
results[].score number App rating (0-5)
results[].installs string Install count range
count integer Number of results returned

Error Responses

401 Unauthorized - Missing or invalid access token

{
  "detail": "Authentication credentials were not provided."
}

500 Internal Server Error - Search service error

{
  "error": "Failed to search apps"
}

Code Examples

const response = await fetch('http://localhost:8000/api/aso/android-search/', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${accessToken}`,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    keyword: 'messaging',
    country: 'us',
    language: 'en'
  })
});

const data = await response.json();
console.log('Found apps:', data.results);
import requests

headers = {
    'Authorization': f'Bearer {access_token}',
    'Content-Type': 'application/json'
}

payload = {
    'keyword': 'messaging',
    'country': 'us',
    'language': 'en'
}

response = requests.post(
    'http://localhost:8000/api/aso/android-search/',
    headers=headers,
    json=payload
)

data = response.json()
print(f"Found {data['count']} apps")
curl -X POST http://localhost:8000/api/aso/android-search/ \
  -H "Authorization: Bearer ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "keyword": "messaging",
    "country": "us",
    "language": "en"
  }'

POST /api/aso/apple-search/

Search for Apple apps in App Store.

Request

POST /api/aso/apple-search/
Authorization: Bearer YOUR_ACCESS_TOKEN
Content-Type: application/json

{
  "keyword": "messaging",
  "country": "us",
  "language": "en"
}

Parameters:

Field Type Required Default Description
keyword string Yes - Search keyword for Apple apps
country string No "us" 2-letter country code
language string No "en" 2-letter language code

Response (200 OK)

{
  "keyword": "messaging",
  "country": "us",
  "language": "en",
  "results": [
    {
      "id": 310633997,
      "appId": "net.whatsapp.WhatsApp",
      "title": "WhatsApp Messenger",
      "url": "https://apps.apple.com/us/app/whatsapp-messenger/id310633997",
      "developer": "WhatsApp Inc.",
      "primaryGenre": "Social Networking",
      "score": 4.69,
      "reviews": 16707847,
      "price": 0,
      "free": true,
      "version": "25.30.73",
      "requiredOsVersion": "15.1"
    },
    {
      "id": 874139669,
      "appId": "org.whispersystems.signal",
      "title": "Signal - Private Messenger",
      "url": "https://apps.apple.com/us/app/signal-private-messenger/id874139669",
      "developer": "Signal Messenger, LLC",
      "primaryGenre": "Social Networking",
      "score": 4.75,
      "reviews": 958541,
      "price": 0,
      "free": true,
      "version": "7.82",
      "requiredOsVersion": "15.0"
    }
  ],
  "count": 2
}

Response Fields:

Field Type Description
keyword string The search keyword used
country string Country code used for search
language string Language code used for search
results array List of matching apps
results[].id integer Apple app ID
results[].appId string Bundle identifier
results[].title string App title
results[].url string App Store URL
results[].developer string Developer name
results[].primaryGenre string Primary app category
results[].score number App rating (0-5)
results[].reviews integer Number of reviews
results[].price number App price
results[].free boolean Whether app is free
results[].version string Current app version
results[].requiredOsVersion string Minimum iOS version required
count integer Number of results returned

Error Responses

401 Unauthorized - Missing or invalid access token

{
  "detail": "Authentication credentials were not provided."
}

500 Internal Server Error - Search service error

{
  "error": "Failed to search apps"
}

Code Examples

const response = await fetch('http://localhost:8000/api/aso/apple-search/', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${accessToken}`,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    keyword: 'messaging',
    country: 'us',
    language: 'en'
  })
});

const data = await response.json();
console.log('Found apps:', data.results);
import requests

headers = {
    'Authorization': f'Bearer {access_token}',
    'Content-Type': 'application/json'
}

payload = {
    'keyword': 'messaging',
    'country': 'us',
    'language': 'en'
}

response = requests.post(
    'http://localhost:8000/api/aso/apple-search/',
    headers=headers,
    json=payload
)

data = response.json()
print(f"Found {data['count']} apps")
curl -X POST http://localhost:8000/api/aso/apple-search/ \
  -H "Authorization: Bearer ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "keyword": "messaging",
    "country": "us",
    "language": "en"
  }'

ASO Features

Keyword Tracking

Both Android and Apple search endpoints automatically store searched keywords in the database for tracking and analytics purposes. Keywords are normalized to lowercase for consistency.

Search Limits

  • Returns up to 250 results per search (configurable)
  • Results are ordered by relevance as determined by the app stores
  • Supports localization with country and language parameters

Country and Language Codes

Common country codes: - us - United States - gb - United Kingdom - de - Germany - fr - France - jp - Japan - cn - China

Common language codes: - en - English - es - Spanish - de - German - fr - French - ja - Japanese - zh - Chinese


POST /api/aso/android-details/

Get current Android app details with automatic caching (7-day TTL).

Request

POST /api/aso/android-details/
Authorization: Bearer YOUR_ACCESS_TOKEN
Content-Type: application/json

{
  "app_id": "com.whatsapp",
  "country": "us",
  "language": "en",
  "force_refresh": false
}

Parameters:

Field Type Required Default Description
app_id string Yes - Android package ID (e.g., com.whatsapp)
country string No us 2-letter country code
language string No en 2-letter language code
force_refresh boolean No false Skip cache and fetch fresh data

Response (200 OK)

{
  "success": true,
  "app_id": "com.whatsapp",
  "country": "us",
  "language": "en",
  "data": {
    "title": "WhatsApp Messenger",
    "score": 4.39,
    "ratings": 212040976,
    "installs": "10,000,000,000+",
    "developer": "WhatsApp LLC",
    "genre": "Communication",
    "price": 0,
    "free": true,
    "version": "2.25.2.75",
    "androidVersion": "VARY"
  }
}

POST /api/aso/android-history/

Get Android app change history with intelligent filtering.

Returns field-centric data - each field shows all dates when it changed, making it easy to track specific metrics over time.

Request

POST /api/aso/android-history/
Authorization: Bearer YOUR_ACCESS_TOKEN
Content-Type: application/json

{
  "app_id": "com.whatsapp",
  "country": "us",
  "language": "en",
  "start_date": "2025-09-29",
  "end_date": "2025-10-29",
  "fields": ["score", "ratings"]
}

Parameters:

Field Type Required Default Description
app_id string Yes - Android package ID
country string No us 2-letter country code
language string No en 2-letter language code
start_date string (YYYY-MM-DD) No 30 days ago History start date
end_date string (YYYY-MM-DD) No today History end date
fields array No all fields Fields to track for changes

Response (200 OK)

{
  "success": true,
  "app_id": "com.whatsapp",
  "country": "us",
  "language": "en",
  "start_date": "2025-09-29",
  "end_date": "2025-10-29",
  "tracked_fields": ["score", "ratings"],
  "change_count": 7,
  "data": {
    "score": [
      {
        "date": "2025-10-29",
        "value": 4.39
      },
      {
        "date": "2025-10-28",
        "value": 4.35
      },
      {
        "date": "2025-10-22",
        "value": 4.38
      }
    ],
    "ratings": [
      {
        "date": "2025-10-29",
        "value": 212040976
      },
      {
        "date": "2025-10-28",
        "value": 211500000
      },
      {
        "date": "2025-10-22",
        "value": 210500000
      }
    ]
  }
}

Response Fields:

Field Type Description
success boolean Whether the request was successful
app_id string The Android package ID
country string Country code used
language string Language code used
start_date string Start date of the history range
end_date string End date of the history range
tracked_fields array Fields that were tracked for changes
change_count integer Total number of field changes recorded
data object Field-centric data with dates (see below)
data[field_name] array Array of {date, value} objects for each field
data[field_name][].date string Date when the field changed (YYYY-MM-DD)
data[field_name][].value any The value of the field on that date

POST /api/aso/apple-details/

Get current Apple app details with dual-TTL caching.

  • Fast metadata (ratings, rankings): 1-day cache
  • Slow metadata (descriptions, specs): 7-day cache

Request

POST /api/aso/apple-details/
Authorization: Bearer YOUR_ACCESS_TOKEN
Content-Type: application/json

{
  "app_id": "310633997",
  "country": "us",
  "language": "en",
  "metadata_type": "both",
  "force_refresh": false
}

Parameters:

Field Type Required Default Description
app_id string Yes - Apple app ID (numeric, e.g., 310633997)
country string No us 2-letter country code
language string No en 2-letter language code
metadata_type string No both fast (ratings), slow (descriptions), or both
force_refresh boolean No false Skip cache and fetch fresh data

Response (200 OK)

{
  "success": true,
  "app_id": "310633997",
  "country": "us",
  "language": "en",
  "metadata_type": "both",
  "data": {
    "title": "WhatsApp Messenger",
    "appId": "net.whatsapp.WhatsApp",
    "score": 4.63,
    "ratings": 7203977,
    "developer": "WhatsApp Inc.",
    "primaryGenre": "Social Networking",
    "price": 0,
    "free": true,
    "version": "25.30.73"
  }
}

POST /api/aso/apple-history/

Get Apple app change history with intelligent filtering.

Returns field-centric data - each field shows all dates when it changed, organized by metadata type (fast/slow).

Request

POST /api/aso/apple-history/
Authorization: Bearer YOUR_ACCESS_TOKEN
Content-Type: application/json

{
  "app_id": "310633997",
  "country": "us",
  "language": "en",
  "metadata_type": "fast",
  "start_date": "2025-10-01",
  "end_date": "2025-10-29",
  "fields": ["score", "ratings"]
}

Parameters:

Field Type Required Default Description
app_id string Yes - Apple app ID (numeric)
country string No us 2-letter country code
language string No en 2-letter language code
metadata_type string No both fast (ratings/rankings), slow (descriptions/media), or both
start_date string (YYYY-MM-DD) No 30 days ago History start date
end_date string (YYYY-MM-DD) No today History end date
fields array No all fields Fields to track for changes

Response (200 OK)

{
  "success": true,
  "app_id": "310633997",
  "country": "us",
  "language": "en",
  "metadata_type": "fast",
  "start_date": "2025-10-01",
  "end_date": "2025-10-29",
  "tracked_fields": ["score", "ratings"],
  "change_count": 6,
  "data": {
    "fast": {
      "score": [
        {
          "date": "2025-10-29",
          "value": 4.63
        },
        {
          "date": "2025-10-28",
          "value": 4.59
        },
        {
          "date": "2025-10-22",
          "value": 4.62
        }
      ],
      "ratings": [
        {
          "date": "2025-10-29",
          "value": 7203977
        },
        {
          "date": "2025-10-28",
          "value": 7180000
        },
        {
          "date": "2025-10-22",
          "value": 7195000
        }
      ]
    },
    "slow": {}
  }
}

Response Fields:

Field Type Description
success boolean Whether the request was successful
app_id string The Apple app ID
country string Country code used
language string Language code used
metadata_type string The metadata type(s) returned: fast, slow, or both
start_date string Start date of the history range
end_date string End date of the history range
tracked_fields array Fields that were tracked for changes
change_count integer Total number of field changes recorded
data object Field-centric data organized by metadata type
data.fast object Changes in fast-moving data (ratings, rankings)
data.fast[field_name] array Array of {date, value} objects for each field
data.slow object Changes in slow-moving data (descriptions, media)
data.slow[field_name] array Array of {date, value} objects for each field

Billing & Subscription Endpoints

The billing system provides comprehensive subscription management, credit tracking, dynamic features, and Stripe integration.

Note: For interactive API documentation with try-it-out functionality, visit /api/docs/ (Scalar UI).

Subscription Plans

GET /api/billing/plans/

List all available subscription plans.

Request:

GET /api/billing/plans/

Response:

{
  "plans": [
    {
      "id": "uuid",
      "tier": "PROFESSIONAL",
      "name": "Professional",
      "description": "Perfect for growing teams",
      "monthly_price": "149.00",
      "yearly_price": "1490.00",
      "limits": {
        "max_projects": 10,
        "max_keywords_per_project": 200,
        "max_competitors_per_project": 25
      },
      "credits": {
        "monthly_api_credits": 5000,
        "monthly_scrape_credits": 5000
      },
      "features": [
        "90-day history",
        "Priority support",
        "Advanced analytics"
      ]
    }
  ]
}

GET /api/billing/subscription/

Get current workspace subscription details.

Request:

GET /api/billing/subscription/
Authorization: Bearer YOUR_ACCESS_TOKEN

Response:

{
  "subscription": {
    "id": "uuid",
    "plan": {
      "tier": "PROFESSIONAL",
      "name": "Professional"
    },
    "status": "ACTIVE",
    "billing_cycle": "MONTHLY",
    "current_period_start": "2024-10-01T00:00:00Z",
    "current_period_end": "2024-10-31T23:59:59Z",
    "auto_renew": true,
    "trial_ends_at": null
  }
}

POST /api/billing/subscription/upgrade/

Upgrade to a higher plan tier.

Request:

{
  "plan_tier": "ENTERPRISE",
  "billing_cycle": "YEARLY"
}

Response:

{
  "subscription": {
    "id": "uuid",
    "plan": {
      "tier": "ENTERPRISE",
      "name": "Enterprise"
    },
    "status": "ACTIVE",
    "upgrade_applied": true,
    "prorated_amount": "350.00"
  }
}

POST /api/billing/subscription/downgrade/

Downgrade to a lower plan tier. Downgrade takes effect at the end of current billing period.

Request:

{
  "workspace_id": "uuid",
  "plan_id": "uuid"
}

Response:

{
  "subscription": {
    "id": "uuid",
    "plan": {
      "tier": "STARTER",
      "name": "Starter"
    },
    "status": "ACTIVE",
    "downgrade_scheduled": true,
    "downgrade_effective_date": "2024-10-31T23:59:59Z"
  }
}

POST /api/billing/subscription/cancel/

Cancel subscription. Cancellation takes effect at end of current billing period.

Request:

{
  "workspace_id": "uuid",
  "reason": "optional cancellation reason"
}

Response:

{
  "subscription": {
    "id": "uuid",
    "status": "CANCELLED",
    "cancel_at_period_end": true,
    "period_end": "2024-10-31T23:59:59Z"
  }
}

Credits Management

GET /api/billing/credits/

Get current credit balances.

Request:

GET /api/billing/credits/
Authorization: Bearer YOUR_ACCESS_TOKEN

Response:

{
  "balances": [
    {
      "credit_type": "DATA_SCRAPE",
      "allocated_credits": 5000,
      "consumed_credits": 1234,
      "purchased_credits": 500,
      "bonus_credits": 0,
      "available_credits": 4266,
      "usage_percentage": 24.68,
      "period_end": "2024-10-31T23:59:59Z"
    },
    {
      "credit_type": "API_CALL",
      "allocated_credits": 5000,
      "consumed_credits": 567,
      "available_credits": 4433
    }
  ]
}

POST /api/billing/credits/consume/

Consume credits for an operation (typically called internally by services).

Request:

{
  "workspace_id": "uuid",
  "feature_key": "api_calls",
  "amount": 1,
  "metadata": {
    "operation": "android_app_fetch",
    "app_id": "com.example.app"
  }
}

Response:

{
  "success": true,
  "remaining": 4999,
  "consumed": 1,
  "feature": {
    "key": "api_calls",
    "name": "API Calls",
    "allocated": 5000,
    "consumed": 1
  }
}

POST /api/billing/credits/bonus/

Add bonus credits to a workspace (admin only).

Request:

{
  "workspace_id": "uuid",
  "feature_key": "api_calls",
  "amount": 500,
  "reason": "Customer appreciation bonus"
}

Response:

{
  "success": true,
  "feature_key": "api_calls",
  "amount_added": 500,
  "new_total": 5500
}

GET /api/billing/credits/history/

Get credit usage history for a workspace.

Request:

GET /api/billing/credits/history/?workspace_id=uuid&feature_key=api_calls&limit=50
Authorization: Bearer YOUR_ACCESS_TOKEN

Parameters: - workspace_id (required): Workspace UUID - feature_key (optional): Filter by specific feature - limit (optional): Number of records (default: 100)

Response:

{
  "history": [
    {
      "feature_key": "api_calls",
      "feature_name": "API Calls",
      "consumed_amount": 1,
      "operation": "android_app_fetch",
      "metadata": {
        "app_id": "com.example.app"
      },
      "timestamp": "2024-10-22T14:32:15Z"
    }
  ],
  "total_count": 1234
}

Usage & Limits

GET /api/billing/usage/

Get usage statistics for workspace.

Request:

GET /api/billing/usage/?workspace_id=uuid
Authorization: Bearer YOUR_ACCESS_TOKEN

Response:

{
  "workspace_id": "uuid",
  "period_start": "2024-10-01T00:00:00Z",
  "period_end": "2024-10-31T23:59:59Z",
  "usage": {
    "projects": {
      "current": 5,
      "limit": 10,
      "percentage": 50.0
    },
    "api_calls": {
      "consumed": 1234,
      "allocated": 5000,
      "percentage": 24.68
    },
    "data_scrapes": {
      "consumed": 567,
      "allocated": 5000,
      "percentage": 11.34
    }
  }
}

GET /api/billing/limits/

Get all plan limits for workspace.

Request:

GET /api/billing/limits/?workspace_id=uuid
Authorization: Bearer YOUR_ACCESS_TOKEN

Response:

{
  "workspace_id": "uuid",
  "plan": {
    "tier": "PROFESSIONAL",
    "name": "Professional"
  },
  "limits": {
    "max_projects": 10,
    "max_keywords_per_project": 200,
    "max_competitors_per_project": 25,
    "max_team_members": 10,
    "historical_data_days": 90,
    "api_calls_per_month": 5000,
    "data_scrapes_per_month": 5000
  }
}

Dynamic Features

GET /api/billing/features/

List all available features in the system.

Request:

GET /api/billing/features/
Authorization: Bearer YOUR_ACCESS_TOKEN

Response:

{
  "features": [
    {
      "id": "uuid",
      "key": "max_projects",
      "name": "Maximum Projects",
      "description": "Number of projects that can be created",
      "feature_type": "HARD_LIMIT",
      "category": "WORKSPACE",
      "unit": "projects"
    },
    {
      "id": "uuid",
      "key": "api_calls",
      "name": "API Calls",
      "description": "Monthly API call credits",
      "feature_type": "CREDIT_BASED",
      "category": "API",
      "reset_period": "MONTHLY",
      "unit": "calls"
    }
  ]
}

GET /api/billing/features/workspace/

Get all features for a specific workspace with usage information.

Request:

GET /api/billing/features/workspace/?workspace_id=uuid
Authorization: Bearer YOUR_ACCESS_TOKEN

Response:

{
  "workspace_id": "uuid",
  "plan": {
    "tier": "PROFESSIONAL",
    "name": "Professional"
  },
  "features": [
    {
      "key": "max_projects",
      "name": "Maximum Projects",
      "feature_type": "HARD_LIMIT",
      "limit_value": 10,
      "current_usage": 5,
      "available": 5
    },
    {
      "key": "api_calls",
      "name": "API Calls",
      "feature_type": "CREDIT_BASED",
      "allocated": 5000,
      "consumed": 1234,
      "available": 3766,
      "usage_percentage": 24.68,
      "period_end": "2024-10-31T23:59:59Z"
    }
  ]
}

POST /api/billing/features/check/

Check if a feature is available for a workspace.

Request:

{
  "workspace_id": "uuid",
  "feature_key": "api_access"
}

Response:

{
  "feature_key": "api_access",
  "is_available": true,
  "details": {
    "feature_type": "BOOLEAN",
    "is_enabled": true
  }
}

POST /api/billing/features/consume/

Consume feature usage (for CREDIT_BASED or HARD_LIMIT features).

Request:

{
  "workspace_id": "uuid",
  "feature_key": "api_calls",
  "amount": 1
}

Response:

{
  "success": true,
  "feature_key": "api_calls",
  "consumed": 1,
  "remaining": 3765
}

Stripe Integration

POST /api/billing/stripe/checkout/

Create a Stripe checkout session for subscription signup.

Request:

{
  "workspace_id": "uuid",
  "plan_id": "uuid",
  "success_url": "https://yourdomain.com/billing/success?session_id={CHECKOUT_SESSION_ID}",
  "cancel_url": "https://yourdomain.com/billing/cancel"
}

Response:

{
  "checkout_url": "https://checkout.stripe.com/c/pay/cs_xxx...",
  "session_id": "cs_xxx"
}

POST /api/billing/stripe/checkout/credits/

Create a Stripe checkout session for credit purchase.

Request:

{
  "workspace_id": "uuid",
  "feature_key": "api_calls",
  "amount": 1000,
  "success_url": "https://yourdomain.com/credits/success",
  "cancel_url": "https://yourdomain.com/credits/cancel"
}

Response:

{
  "checkout_url": "https://checkout.stripe.com/c/pay/cs_xxx...",
  "session_id": "cs_xxx",
  "amount": 1000
}

GET /api/billing/stripe/portal/

Create a Stripe customer portal session for self-service billing management.

Request:

GET /api/billing/stripe/portal/?workspace_id=uuid&return_url=https://yourdomain.com/billing
Authorization: Bearer YOUR_ACCESS_TOKEN

Response:

{
  "portal_url": "https://billing.stripe.com/p/session/xxx..."
}

GET /api/billing/stripe/payment-history/

Get payment history from Stripe.

Request:

GET /api/billing/stripe/payment-history/?workspace_id=uuid&limit=10
Authorization: Bearer YOUR_ACCESS_TOKEN

Response:

{
  "payments": [
    {
      "id": "in_xxx",
      "amount": 149.00,
      "currency": "USD",
      "status": "paid",
      "paid": true,
      "date": 1704067200,
      "invoice_pdf": "https://pay.stripe.com/invoice/xxx/pdf",
      "hosted_invoice_url": "https://invoice.stripe.com/i/xxx",
      "description": "Subscription for Professional Plan"
    }
  ],
  "has_more": false
}

GET /api/billing/stripe/upcoming-invoice/

Preview the next invoice for a workspace.

Request:

GET /api/billing/stripe/upcoming-invoice/?workspace_id=uuid
Authorization: Bearer YOUR_ACCESS_TOKEN

Response:

{
  "amount_due": 149.00,
  "currency": "USD",
  "period_start": "2024-11-01T00:00:00Z",
  "period_end": "2024-11-30T23:59:59Z",
  "next_payment_attempt": "2024-11-01T00:00:00Z"
}

Invoices

GET /api/billing/invoices/

List all invoices for the workspace.

Request:

GET /api/billing/invoices/
Authorization: Bearer YOUR_ACCESS_TOKEN

Response:

{
  "invoices": [
    {
      "id": "uuid",
      "invoice_number": "INV-202410-00001",
      "total_amount": "149.00",
      "currency": "USD",
      "status": "PAID",
      "issued_at": "2024-10-01T00:00:00Z",
      "period_start": "2024-10-01",
      "period_end": "2024-10-31",
      "pdf_url": "https://stripe.com/..."
    }
  ]
}


Next Steps