📖 API Reference

Complete documentation for ExtractBill REST API. Build invoice and receipt parsing into your application with our simple, powerful API.

Base URL:
https://www.extractbill.com/api/v1
Current Version:
v1
Rate Limit:
100 requests/minute

Need higher limits? Contact us

Authentication

All API requests require authentication via Bearer token in the Authorization header. Your API tokens carry many privileges, so be sure to keep them secure! Do not share your secret API tokens in publicly accessible areas such as GitHub, client-side code, and so forth.

Get your API token

  1. Go to Dashboard → Settings → API Tokens
  2. Click "Generate New Token"
  3. Copy the token (shown only once!) and store it securely

Quick Admin Link

Already logged in? Create a token directly in admin panel:

Create API Token in Settings

Token Format

xb_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

API tokens are 43 characters long and start with xb_

Request Header

Include your API token in the Authorization header of every request:

Authorization: Bearer xb_your_token_here

Endpoints

GET

/account/usage

Get detailed statistics about your account usage including total documents processed, success rate, average confidence scores, and processing times. This endpoint helps you monitor your API usage and performance metrics.

Request Example

curl https://www.extractbill.com/api/v1/account/usage \
  -H "Authorization: Bearer xb_your_token"
const response = await fetch('https://www.extractbill.com/api/v1/account/usage', {
  headers: {
    'Authorization': 'Bearer xb_your_token'
  }
});
const data = await response.json();
console.log(data);
import requests

response = requests.get(
    'https://www.extractbill.com/api/v1/account/usage',
    headers={'Authorization': 'Bearer xb_your_token'}
)
data = response.json()
print(data)
$client = new \GuzzleHttp\Client();
$response = $client->get('https://www.extractbill.com/api/v1/account/usage', [
    'headers' => ['Authorization' => 'Bearer xb_your_token']
]);
$data = json_decode($response->getBody(), true);
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
    .uri(URI.create("https://www.extractbill.com/api/v1/account/usage"))
    .header("Authorization", "Bearer xb_your_token")
    .GET()
    .build();
HttpResponse<String> response = client.send(request,
    HttpResponse.BodyHandlers.ofString());
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("Authorization", "Bearer xb_your_token");
var response = await client.GetAsync("https://www.extractbill.com/api/v1/account/usage");
var data = await response.Content.ReadAsStringAsync();

Response (200 OK)

{
  "data": {
    "total_documents": 150,
    "documents_this_month": 25,
    "completed": 145,
    "failed": 5,
    "success_rate": 96.67,
    "average_confidence_score": 94.2,
    "average_processing_time_seconds": 4.8
  }
}

Response Fields

total_documents Total number of documents processed across all time
documents_this_month Documents processed in the current calendar month
success_rate Percentage of successfully processed documents (0-100)
average_confidence_score Average AI confidence score for completed documents (0-100)
average_processing_time_seconds Average time to process a document from upload to completion
GET

/account/balance

Retrieve your current token balance and purchase history. Use this endpoint to check if you have sufficient tokens before uploading documents, or to display balance information in your application.

Response (200 OK)

{
  "data": {
    "current_balance": 47,
    "total_purchased": 100,
    "total_used": 53,
    "usage_percentage": 53.00,
    "last_purchase_at": "2025-09-01T10:00:00Z"
  }
}

Response Fields

current_balance Number of tokens available for document processing
total_purchased Total tokens purchased (including free trial tokens)
usage_percentage Percentage of purchased tokens that have been used
POST

/documents

Upload a document (invoice, receipt, or bill) for AI-powered parsing. The document will be processed asynchronously - you'll receive an immediate response with a document ID that you can use to check the status and retrieve parsed data once processing is complete.

Authentication: Required Rate Limit: 100/minute

Need higher limits? Contact us

Parameters

file
required Document file - Supported formats: PDF, JPG, PNG, WEBP. Max size: 10MB
webhook_url
optional Override default webhook URL for this request. We'll POST to this URL when processing completes.
webhook_secret
optional Custom webhook secret for HMAC signature verification

Request Example

curl -X POST https://www.extractbill.com/api/v1/documents \
  -H "Authorization: Bearer xb_your_token" \
  -F "file=@invoice.pdf"
const formData = new FormData();
formData.append('file', fileInput.files[0]);

const response = await fetch('https://www.extractbill.com/api/v1/documents', {
  method: 'POST',
  headers: { 'Authorization': 'Bearer xb_your_token' },
  body: formData
});
const data = await response.json();
import requests

with open('invoice.pdf', 'rb') as f:
    files = {'file': f}
    response = requests.post(
        'https://www.extractbill.com/api/v1/documents',
        headers={'Authorization': 'Bearer xb_your_token'},
        files=files
    )
data = response.json()
$client = new \GuzzleHttp\Client();
$response = $client->post('https://www.extractbill.com/api/v1/documents', [
    'headers' => ['Authorization' => 'Bearer xb_your_token'],
    'multipart' => [
        ['name' => 'file', 'contents' => fopen('invoice.pdf', 'r')]
    ]
]);
$data = json_decode($response->getBody(), true);
var file = new File("invoice.pdf");
var request = HttpRequest.newBuilder()
    .uri(URI.create("https://www.extractbill.com/api/v1/documents"))
    .header("Authorization", "Bearer xb_your_token")
    .header("Content-Type", "multipart/form-data")
    .POST(HttpRequest.BodyPublishers.ofFile(file.toPath()))
    .build();
var response = HttpClient.newHttpClient()
    .send(request, HttpResponse.BodyHandlers.ofString());
using var client = new HttpClient();
using var content = new MultipartFormDataContent();
var fileContent = new StreamContent(File.OpenRead("invoice.pdf"));
content.Add(fileContent, "file", "invoice.pdf");
client.DefaultRequestHeaders.Add("Authorization", "Bearer xb_your_token");
var response = await client.PostAsync("https://www.extractbill.com/api/v1/documents", content);

Response (202 Accepted)

{
  "data": {
    "id": "d-a3k9f2x7m1p",
    "status": "pending",
    "original_filename": "invoice.pdf",
    "uploaded_at": "2025-10-14T14:30:00Z",
    "uploaded_via": "api"
  }
}

Possible Errors

400 Invalid file format - only PDF, JPG, PNG, WEBP are supported
402 Insufficient tokens - purchase more tokens to continue
413 File too large (max 10MB)
422 Validation error - check request parameters
429 Rate limit exceeded - wait before making more requests
GET

/documents/:id

Retrieve the current status and parsed data for a specific document. Use this endpoint to poll for completion after uploading a document. Once processing is complete, you'll receive the full parsed data in structured JSON format.

Request Example

curl https://www.extractbill.com/api/v1/documents/d-a3k9f2x7m1p \
  -H "Authorization: Bearer xb_your_token"
const documentId = 'd-a3k9f2x7m1p';
const response = await fetch(`https://www.extractbill.com/api/v1/documents/${documentId}`, {
  headers: { 'Authorization': 'Bearer xb_your_token' }
});
const data = await response.json();
import requests

document_id = 'd-a3k9f2x7m1p'
response = requests.get(
    f'https://www.extractbill.com/api/v1/documents/{document_id}',
    headers={'Authorization': 'Bearer xb_your_token'}
)
data = response.json()
$client = new \GuzzleHttp\Client();
$documentId = 'd-a3k9f2x7m1p';
$response = $client->get("https://www.extractbill.com/api/v1/documents/{$documentId}", [
    'headers' => ['Authorization' => 'Bearer xb_your_token']
]);
$data = json_decode($response->getBody(), true);
String documentId = "d-a3k9f2x7m1p";
var request = HttpRequest.newBuilder()
    .uri(URI.create("https://www.extractbill.com/api/v1/documents/" + documentId))
    .header("Authorization", "Bearer xb_your_token")
    .GET()
    .build();
var response = HttpClient.newHttpClient()
    .send(request, HttpResponse.BodyHandlers.ofString());
using var client = new HttpClient();
string documentId = "d-a3k9f2x7m1p";
client.DefaultRequestHeaders.Add("Authorization", "Bearer xb_your_token");
var response = await client.GetAsync($"https://www.extractbill.com/api/v1/documents/{documentId}");
var data = await response.Content.ReadAsStringAsync();

Response (200 OK)

{
  "data": {
    "id": "d-a3k9f2x7m1p",
    "status": "completed",
    "confidence_score": 95.5,
    "parsed_data": {
      "document": {
        "type": "invoice",
        "identifier": "INV-2025-001",
        "date": "2025-10-10",
        "due_date": "2025-11-10"
      },
      "supplier": {
        "name": "ACME Corporation",
        "address": "123 Main St, New York, NY 10001",
        "tax_id": "12-3456789"
      },
      "customer": {
        "name": "John Doe",
        "address": "456 Oak Ave, Los Angeles, CA 90001"
      },
      "totals": {
        "subtotal": 1000.00,
        "tax": 250.00,
        "total": 1250.00,
        "currency": "USD"
      },
      "items": [
        {
          "description": "Professional Services",
          "quantity": 10,
          "unit_price": 100.00,
          "amount": 1000.00
        }
      ]
    },
    "completed_at": "2025-10-14T14:30:05Z",
    "processing_time_seconds": 5
  }
}

Status Values

pending Document uploaded, waiting to be processed
processing AI is currently parsing the document
completed Parsing complete, data available in parsed_data field
failed Processing failed, check error_message field
GET

/documents

List all documents for your account with pagination and filtering options. Use this to build document management interfaces or to monitor processing status across multiple uploads.

Query Parameters

page
optional Page number (default: 1)
per_page
optional Items per page (default: 20, max: 100)
status
optional Filter by status: pending, processing, completed, failed

Request Example

curl "https://www.extractbill.com/api/v1/documents?status=completed&per_page=20" \
  -H "Authorization: Bearer xb_your_token"
const params = new URLSearchParams({
  status: 'completed',
  per_page: '20'
});
const response = await fetch(`https://www.extractbill.com/api/v1/documents?${params}`, {
  headers: { 'Authorization': 'Bearer xb_your_token' }
});
const data = await response.json();
import requests

params = {'status': 'completed', 'per_page': 20}
response = requests.get(
    'https://www.extractbill.com/api/v1/documents',
    headers={'Authorization': 'Bearer xb_your_token'},
    params=params
)
data = response.json()
$client = new \GuzzleHttp\Client();
$response = $client->get('https://www.extractbill.com/api/v1/documents', [
    'headers' => ['Authorization' => 'Bearer xb_your_token'],
    'query' => ['status' => 'completed', 'per_page' => 20]
]);
$data = json_decode($response->getBody(), true);
var request = HttpRequest.newBuilder()
    .uri(URI.create("https://www.extractbill.com/api/v1/documents?status=completed&per_page=20"))
    .header("Authorization", "Bearer xb_your_token")
    .GET()
    .build();
var response = HttpClient.newHttpClient()
    .send(request, HttpResponse.BodyHandlers.ofString());
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("Authorization", "Bearer xb_your_token");
var query = "?status=completed&per_page=20";
var response = await client.GetAsync($"https://www.extractbill.com/api/v1/documents{query}");
var data = await response.Content.ReadAsStringAsync();

Error Handling

All errors return a consistent JSON format with a descriptive error code, human-readable message, and additional details when available. This structure makes it easy to handle errors programmatically in your application.

Error Response Format

{
  "error": {
    "code": "insufficient_tokens",
    "message": "Insufficient token balance",
    "details": {
      "current_balance": 0,
      "required": 1,
      "purchase_url": "https://www.extractbill.com/settings/billing"
    }
  }
}

Common Error Codes

401
Unauthorized Invalid or missing API token. Check your Authorization header.
402
Payment Required Insufficient token balance. Purchase more tokens to continue processing documents.
403
Forbidden Access denied. You don't have permission to access this resource.
404
Not Found The requested resource doesn't exist. Check the document ID.
422
Validation Error Request validation failed. Check the details field for specific validation errors.
429
Too Many Requests Rate limit exceeded. Wait before making more requests or contact us for higher limits.
500
Internal Server Error Something went wrong on our end. Contact support if the problem persists.

More Examples

Real-world examples to help you integrate ExtractBill API into your application.

Upload Document with Custom Webhook Notification

Upload a document and receive a webhook notification when processing completes:

💡 Tip: You can set a global webhook URL for all documents in Admin Settings. Per-request webhook URLs override the global setting. Learn more in Webhooks Documentation.

cURL
curl -X POST https://www.extractbill.com/api/v1/documents \
  -H "Authorization: Bearer xb_your_token" \
  -F "file=@invoice.pdf" \
  -F "webhook_url=https://your-app.com/webhooks/extractbill" \
  -F "webhook_secret=your_webhook_secret_123"

Check Balance Before Upload

Always check your balance before uploading to avoid 402 errors:

cURL
# 1. Check balance
curl https://www.extractbill.com/api/v1/account/balance \
  -H "Authorization: Bearer xb_your_token"

# 2. If balance > 0, upload document
curl -X POST https://www.extractbill.com/api/v1/documents \
  -H "Authorization: Bearer xb_your_token" \
  -F "file=@invoice.pdf"

Poll for Document Completion

Simple polling loop to wait for document processing:

Bash Script
# Upload document
DOCUMENT_ID=$(curl -X POST https://www.extractbill.com/api/v1/documents \
  -H "Authorization: Bearer xb_your_token" \
  -F "file=@invoice.pdf" | jq -r '.data.id')

# Poll every 2 seconds until completed
while true; do
  STATUS=$(curl https://www.extractbill.com/api/v1/documents/$DOCUMENT_ID \
    -H "Authorization: Bearer xb_your_token" | jq -r '.data.status')

  if [ "$STATUS" = "completed" ] || [ "$STATUS" = "failed" ]; then
    break
  fi

  sleep 2
done

# Get final result
curl https://www.extractbill.com/api/v1/documents/$DOCUMENT_ID \
  -H "Authorization: Bearer xb_your_token"

List Only Failed Documents

Get all failed documents to investigate processing issues:

cURL
curl "https://www.extractbill.com/api/v1/documents?status=failed&per_page=50" \
  -H "Authorization: Bearer xb_your_token"

Need help integrating?

Check out our Getting Started guide for step-by-step instructions, or contact our support team if you need assistance.

We use cookies to provide and improve our service. Essential cookies are required for the site to function. Analytics cookies help us understand how you use the site. Learn more