Credits
Manage credits, billing, and usage tracking
Credits
DocExtract uses a credit-based pricing model. Each document processing job consumes credits based on the document complexity and AI model used.
How Credits Work
- Pay as you go: No subscriptions or monthly fees
- Flexible pricing: Purchase credits in packages that fit your needs
- Never expire: Credits remain in your account indefinitely
- Shared across organization: All team members use the same credit pool
Credit Costs
| Document Type | Pages | Credits |
|---|---|---|
| Simple (receipt, form) | 1 page | 1 credit |
| Standard (invoice, PO) | 1-5 pages | 2 credits |
| Complex (contract, report) | 6-10 pages | 3 credits |
| Large document | 11-20 pages | 5 credits |
| Very large document | 21+ pages | 8 credits |
Test Mode: Test API keys don't consume real credits. Use them for development and testing.
Credit Balance
Check your current credit balance.
curl -X GET https://api.adteco.com/v1/credits \
-H "Authorization: Bearer sk_live_your_api_key"const response = await fetch('https://api.adteco.com/v1/credits', {
headers: {
'Authorization': 'Bearer sk_live_your_api_key',
},
});
const data = await response.json();
console.log('Available credits:', data.balance);
console.log('Total used:', data.total_used);response = requests.get(
'https://api.adteco.com/v1/credits',
headers={'Authorization': 'Bearer sk_live_your_api_key'},
)
data = response.json()
print(f"Available credits: {data['balance']}")
print(f"Total used: {data['total_used']}")Response
{
"balance": 1250,
"total_purchased": 2000,
"total_used": 750,
"currency": "USD",
"last_purchase": {
"amount": 500,
"price_paid": 40.00,
"purchased_at": "2024-11-15T10:00:00Z"
}
}Credit Transactions
View your credit transaction history.
curl -X GET "https://api.adteco.com/v1/credits/transactions?limit=50" \
-H "Authorization: Bearer sk_live_your_api_key"const response = await fetch(
'https://api.adteco.com/v1/credits/transactions?limit=50',
{
headers: {
'Authorization': 'Bearer sk_live_your_api_key',
},
}
);
const data = await response.json();
console.log(`${data.transactions.length} transactions found`);response = requests.get(
'https://api.adteco.com/v1/credits/transactions',
headers={'Authorization': 'Bearer sk_live_your_api_key'},
params={'limit': 50},
)
data = response.json()
print(f"{len(data['transactions'])} transactions found")Response
{
"transactions": [
{
"id": "txn_abc123...",
"type": "usage",
"amount": -2,
"balance_after": 1248,
"description": "Document processing: job_xyz789...",
"job_id": "job_xyz789...",
"created_at": "2024-11-23T10:05:05Z"
},
{
"id": "txn_def456...",
"type": "purchase",
"amount": 500,
"balance_after": 1250,
"description": "Credit purchase: Professional Package",
"payment_intent_id": "pi_stripe123...",
"created_at": "2024-11-15T10:00:00Z"
}
],
"total": 127,
"limit": 50,
"offset": 0
}Transaction Types
| Type | Description | Amount |
|---|---|---|
purchase | Credits purchased | Positive |
usage | Document processing | Negative |
refund | Credit refund | Positive |
adjustment | Manual adjustment | Positive/Negative |
Query Parameters
| Parameter | Type | Description |
|---|---|---|
type | string | Filter by transaction type |
date_from | string | Start date (ISO 8601) |
date_to | string | End date (ISO 8601) |
limit | number | Max results (default: 50, max: 100) |
offset | number | Results offset for pagination |
Purchase Credits
Credits are purchased through the Adteco dashboard. You cannot purchase credits directly via the API for security reasons.
Credit Packages
| Package | Credits | Price | Price per Credit | Savings |
|---|---|---|---|---|
| Starter | 100 | $10 | $0.10 | - |
| Professional | 500 | $40 | $0.08 | 20% |
| Business | 2,000 | $140 | $0.07 | 30% |
| Enterprise | 10,000 | $600 | $0.06 | 40% |
Volume Discounts: Contact sales@adteco.com for custom packages over 10,000 credits.
Purchase Flow
- Visit the Billing Dashboard
- Select a credit package
- Complete payment via Stripe
- Credits are added instantly to your account
Usage Tracking
Monitor credit usage to optimize costs.
Daily Usage Report
async function getDailyUsage(days: number = 30) {
const dateFrom = new Date(Date.now() - days * 24 * 60 * 60 * 1000).toISOString();
const response = await fetch(
`https://api.adteco.com/v1/credits/transactions?type=usage&date_from=${dateFrom}`,
{
headers: {
'Authorization': 'Bearer sk_live_your_api_key',
},
}
);
const data = await response.json();
// Group by date
const usageByDate: Record<string, number> = {};
data.transactions.forEach(txn => {
const date = txn.created_at.split('T')[0];
usageByDate[date] = (usageByDate[date] || 0) + Math.abs(txn.amount);
});
return usageByDate;
}
// Usage
const usage = await getDailyUsage(30);
console.log('Daily usage (last 30 days):', usage);Usage by Extractor
async function getUsageByExtractor() {
const response = await fetch(
'https://api.adteco.com/v1/credits/usage-by-extractor',
{
headers: {
'Authorization': 'Bearer sk_live_your_api_key',
},
}
);
return response.json();
}Response:
{
"period": "30d",
"usage": [
{
"extractor_id": "ext_abc123...",
"extractor_name": "Invoice Extractor",
"jobs_processed": 892,
"credits_used": 1784,
"avg_credits_per_job": 2.0,
"percentage": 58.2
},
{
"extractor_id": "ext_def456...",
"extractor_name": "Receipt Extractor",
"jobs_processed": 651,
"credits_used": 651,
"avg_credits_per_job": 1.0,
"percentage": 21.3
}
],
"total_credits": 3065
}Low Balance Alerts
Set up alerts to notify you when credits are running low.
Webhook Notifications
Configure a webhook to receive low balance alerts:
// Your webhook endpoint receives this payload
{
"event": "credits.low_balance",
"data": {
"current_balance": 50,
"threshold": 100,
"org_id": "org_xyz789...",
"timestamp": "2024-11-23T10:00:00Z"
}
}
// Webhook handler example
app.post('/webhooks/docextract', async (req, res) => {
const event = req.body;
if (event.event === 'credits.low_balance') {
// Send notification to admin
await sendEmail({
to: 'admin@company.com',
subject: 'DocExtract Credits Running Low',
body: `
Your DocExtract credit balance is low.
Current balance: ${event.data.current_balance} credits
Purchase more credits: https://app.adteco.com/docextract/billing
`,
});
}
res.status(200).send('OK');
});Polling for Low Balance
async function checkCreditBalance() {
const response = await fetch('https://api.adteco.com/v1/credits', {
headers: {
'Authorization': 'Bearer sk_live_your_api_key',
},
});
const data = await response.json();
// Alert if below threshold
const threshold = 100;
if (data.balance < threshold) {
console.warn(
`⚠️ Low credit balance: ${data.balance} credits remaining`
);
// Send notification
await notifyAdmin({
type: 'low_credits',
balance: data.balance,
url: 'https://app.adteco.com/docextract/billing',
});
}
return data.balance;
}
// Check balance periodically
setInterval(checkCreditBalance, 60 * 60 * 1000); // Every hourCost Optimization
Estimate Costs Before Processing
function estimateJobCost(pageCount: number, complexity: 'simple' | 'standard' | 'complex'): number {
if (complexity === 'simple') return 1;
if (complexity === 'standard') return Math.min(2, Math.ceil(pageCount / 5));
if (complexity === 'complex') return Math.ceil(pageCount / 3);
return 0;
}
async function processWithBudget(
extractorId: string,
documents: string[],
maxCredits: number
) {
let creditsUsed = 0;
const results = [];
for (const docPath of documents) {
// Estimate cost (you'd need to detect pages and complexity)
const estimatedCost = 2; // Assume standard 2-credit document
if (creditsUsed + estimatedCost > maxCredits) {
console.log('Budget limit reached. Stopping processing.');
break;
}
try {
const result = await processDocument(extractorId, docPath);
creditsUsed += result.cost_credits;
results.push(result);
} catch (error) {
console.error(`Failed to process ${docPath}:`, error);
}
}
console.log(`Processed ${results.length} documents using ${creditsUsed} credits`);
return results;
}Batch Processing Optimization
async function optimizedBatchProcessing(
extractorId: string,
documents: string[]
) {
// Check current balance
const balance = await fetch('https://api.adteco.com/v1/credits', {
headers: { 'Authorization': 'Bearer sk_live_your_api_key' },
}).then(r => r.json());
console.log(`Current balance: ${balance.balance} credits`);
// Estimate total cost (assuming 2 credits per document)
const estimatedCost = documents.length * 2;
if (balance.balance < estimatedCost) {
throw new Error(
`Insufficient credits. Need ${estimatedCost}, have ${balance.balance}`
);
}
// Process in batches
const batchSize = 50;
const results = [];
for (let i = 0; i < documents.length; i += batchSize) {
const batch = documents.slice(i, i + batchSize);
console.log(
`Processing batch ${Math.floor(i / batchSize) + 1}/${Math.ceil(documents.length / batchSize)}`
);
const batchResults = await processBatch(extractorId, batch);
results.push(...batchResults);
// Check balance periodically
if ((i + batchSize) % 200 === 0) {
const currentBalance = await fetch('https://api.adteco.com/v1/credits', {
headers: { 'Authorization': 'Bearer sk_live_your_api_key' },
}).then(r => r.json());
console.log(`Remaining credits: ${currentBalance.balance}`);
}
}
return results;
}Retry Failed Jobs Only
Don't waste credits on repeated failures:
async function smartRetry(jobId: string, maxRetries: number = 3) {
let attempt = 0;
while (attempt < maxRetries) {
const job = await getJob(jobId);
if (job.status === 'completed') {
return job;
}
if (job.status === 'failed') {
// Check if error is retryable
const retryableErrors = [
'temporary_error',
'service_unavailable',
'timeout',
];
if (!retryableErrors.includes(job.error_details?.code)) {
console.log('Error is not retryable. Not retrying.');
throw new Error(job.error_details?.message);
}
attempt++;
console.log(`Attempt ${attempt} failed. Retrying...`);
// Resubmit document
const docResponse = await fetch(job.document_url);
const docBlob = await docResponse.blob();
const docBuffer = Buffer.from(await docBlob.arrayBuffer());
const base64Doc = docBuffer.toString('base64');
const newJob = await submitDocument({
extractor_id: job.extractor_id,
document: base64Doc,
mime_type: job.mime_type,
metadata: {
...job.metadata,
retry_attempt: attempt,
original_job_id: jobId,
},
});
jobId = newJob.id;
await sleep(2000); // Wait before checking
}
await sleep(2000);
}
throw new Error('Max retries exceeded');
}Refunds
Credits are non-refundable, but we may issue refunds in these cases:
- System errors that consumed credits incorrectly
- Duplicate charges due to technical issues
- Service outages that affected processing
Contact support@adteco.com with your transaction ID to request a refund.
Enterprise Billing
For high-volume users, we offer:
- Invoice Billing: Monthly invoices instead of prepaid credits
- Volume Discounts: Custom pricing for 50,000+ credits per month
- Dedicated Support: Priority support and account management
- SLA Guarantees: Uptime and processing time guarantees
Contact sales@adteco.com to discuss enterprise options.
Billing FAQs
Do credits expire?
No, credits never expire and remain in your account indefinitely.
Can I get a refund for unused credits?
Credits are non-refundable, but you can use them at any time in the future.
What happens if I run out of credits?
API requests will fail with an insufficient_credits error. Your processing will resume once you purchase more credits.
Can I share credits across multiple projects?
Yes, all projects within your organization share the same credit pool.
Do test API keys use real credits?
No, test keys (starting with sk_test_) use simulated processing and don't consume real credits.
Can I transfer credits to another account?
Credits cannot be transferred between organizations. Each organization maintains its own credit balance.