My App
DocExtract API

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 TypePagesCredits
Simple (receipt, form)1 page1 credit
Standard (invoice, PO)1-5 pages2 credits
Complex (contract, report)6-10 pages3 credits
Large document11-20 pages5 credits
Very large document21+ pages8 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

TypeDescriptionAmount
purchaseCredits purchasedPositive
usageDocument processingNegative
refundCredit refundPositive
adjustmentManual adjustmentPositive/Negative

Query Parameters

ParameterTypeDescription
typestringFilter by transaction type
date_fromstringStart date (ISO 8601)
date_tostringEnd date (ISO 8601)
limitnumberMax results (default: 50, max: 100)
offsetnumberResults 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

PackageCreditsPricePrice per CreditSavings
Starter100$10$0.10-
Professional500$40$0.0820%
Business2,000$140$0.0730%
Enterprise10,000$600$0.0640%

Volume Discounts: Contact sales@adteco.com for custom packages over 10,000 credits.

Purchase Flow

  1. Visit the Billing Dashboard
  2. Select a credit package
  3. Complete payment via Stripe
  4. 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 hour

Cost 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.

Next Steps