Utility Bill AI
The Utility Bill AI service accepts a customer's electricity bill as a PDF or image and returns a structured JSON object containing customer details, billing information, meter readings, and up to 12 months of historical usage — all extracted by Google Gemini with no manual data entry.
Processing pipeline
Supported input
| Format | Notes |
|---|---|
| Multi-page PDFs are treated as a single document. All pages are analysed together. | |
| PNG / JPG / JPEG | Multiple image files are treated as one document. OCR noise (routing codes, asterisks) is filtered automatically. |
400 error.Extracted data
A successful extraction returns a JSON object with three top-level sections:
customerInformation
| Field | Type | Description |
|---|---|---|
customerName | string | Extracted from the payment stub. OCR noise and routing codes are filtered out. |
accountNumber | string | Utility account number. |
serviceNumber | string | Service reference number, if explicitly labelled on the bill. |
serviceAddress | Address | Address where electricity is provided (line1, line2, city, state, zip, country). |
billingInformation
| Field | Type | Description |
|---|---|---|
utilityProviderName | string | Name of the utility company. |
invoiceDate | string | Bill issue date in YYYY-MM-DD format. |
billingPeriodStart / End | string | Start and end dates of the billing period in YYYY-MM-DD format. |
amountDue | string | Total amount due. Negative values indicate credits. Currency symbols are stripped. |
pastDue | string | Previous balance remaining after last payment. |
dueDate | string | Payment due date in YYYY-MM-DD format. |
billingAddress | Address | Address the bill is mailed to, extracted from the payment stub. |
electricityUsage
| Field | Type | Description |
|---|---|---|
meters | array | Per-meter readings — each includes meterNumber, usageKwh, servicePeriodStart, servicePeriodEnd. |
electricRate | string | Customer's rate plan or class (e.g. "Residential Regular R-1"). |
electricityTier | string | Tier information, if explicitly marked on the bill. |
supplyRatePerKwh | string Required | Supply or generation rate per kWh. The service retries extraction up to 3 times if this field is missing. |
marketState | string | Two-letter state abbreviation of the service address (e.g. MA, RI). |
usageByMonth | array | Up to 12 months of historical usage. Each entry: month, year, value, unit. Missing months are extrapolated using seasonal patterns from available data. |
Retry logic
Gemini is called up to 3 times per request. After each attempt the service checks whether usageByMonth contains at least 12 months. If the count is sufficient, processing stops early. Retries also stop if the result does not change between attempts.
API endpoints
| Method & path | Mode | Use when |
|---|---|---|
POST /process-bill | Synchronous | Files uploaded directly in the request body. Returns the full result immediately after processing. |
POST /process-utility-bill | Asynchronous | Files already stored in GCS (uploaded separately). Triggers background processing — poll for status. |
POST /poll-utility-bill-status | Polling | Returns the current status of an async job. Statuses: IN_PROGRESS, COMPLETED, FAILED. |
GET /health | — | Health check. Returns {'status': 'ok'}. |
Request — POST /process-bill
Multipart form data:
POST /v1/utility-bill-service/process-bill
Authorization: Bearer <id_token>
Content-Type: multipart/form-data
files: <file> # PDF, PNG, JPG, or JPEG
transactionId: <uuid>
transactionGroupId: <uuid>
externalId: <user_email> # optional, defaults to token emailResponse
{
"apiVersion": "1.0",
"transactionId": "...",
"transactionGroupId": "...",
"id": "...",
"data": {
"items": [
{
"customerInformation": { ... },
"billingInformation": { ... },
"electricityUsage": {
"meters": [...],
"electricRate": "Residential Regular R-1",
"supplyRatePerKwh": "0.18213",
"marketState": "MA",
"usageByMonth": [
{ "month": "January", "year": "2025", "value": "980", "unit": "kWh" },
...
]
}
}
]
},
"fileStatus": [
{ "filename": "bill.pdf", "status": "success" }
]
}Task status (async mode)
When using POST /process-utility-bill, poll for completion using the transactionId:
POST /v1/utility-bill-service/poll-utility-bill-status
{ "transactionId": "<uuid>" }
# Response
{
"status": "IN_PROGRESS",
"percentage": "50%",
"message": "Utility bill extracted successfully..."
}Percentage milestones: 0% initiated → 10% validated → 20% processing → 50% extracted → 100% complete or failed.
Error cases
| Status | Code | Cause |
|---|---|---|
400 | Invalid file type | Uploaded file is not a supported format or is not an electricity bill. |
400 | Incomplete bill | Customer name or service address could not be extracted after all retries. |
400 | Subscription inactive | The organisation's utility-bill service subscription has ended or is not active. |
500 | Processing failed | Gemini returned an unparseable response after all retries. |