Average Bank Balance (Weekly)
aiAverage Bank Balance (ABB) computed on a WEEKLY basis from a bank-statement PDF. We reconstruct the end-of-day balance for every day, group days into ISO weeks, average each week, and return the week-weighted mean of those weekly averages — plus a per-week breakdown. Because each week counts equally (regardless of how many days it holds), the weekly ABB differs from the daily ABB, mainly at partial weeks at the start/end of the period. Single synchronous call: POST a base64 PDF, get the ABB back. Works across banks with no per-bank templates. Digital/native PDFs only (not scans). Billing: charged once per successful call; failures (422) are not charged. TIMING: parse time scales with the number of transactions, not the page count. A 3-month statement typically returns in 15-30s; a dense 12-month statement (900+ rows) measured 64s end to end. Set your client timeout to at least 120s. A short client timeout is the most common integration failure here — the call completes on our side and you are charged for it, but you never see the result. DATE-WISE ABB: pass `dates` (e.g. [5,10,15,20,25,30]) to also get the end-of-day balance on those specific days of every month — the figures banking partners assess against. Any combination of days 1-31 is supported, and the usual monthly figures are returned alongside, not instead. The response adds an `on_dates` block: `months[]` (the balance for each requested day in each month, plus that month average), an overall `average_balance` across every sampled point, and `missing[]`. A requested day that the statement does not cover — including the 29th-31st in a short month such as February — is listed in `missing` and NEVER substituted with a nearby day, so a lender never assesses against a date that was not actually measured. Date-wise sampling costs nothing extra: it is a parameter on the same call, not a separate product.
Authentication
Pass your key in the X-API-Key header. Use a test_ key against the sandbox and a live_ key in production. Send an optional Idempotency-Key header to safely retry — the same key returns the same response for 24h.
Request
Endpoint: POST https://apisathi.in/gw/v1/abb-weekly-v1/
The trailing slash is required. /v1/abb-weekly-v1/ works; /v1/abb-weekly-v1 returns 404 Not Found. This applies to every product.
| Field | Type | Required | Constraints |
|---|---|---|---|
| pdf_base64 | string | required | Base64-encoded bank-statement PDF (digital/native, not a scan). Max 15MB. |
| password | string | optional | PDF password, if the statement is locked. |
| dates | integer[] | optional | Optional. Days of the month to sample the balance on, e.g. [5, 15, 25]. Lenders differ on which dates their banking partner assesses ABB against (5/10/15, 5/15/25 and 20/25/30 are all common). Returns an `on_dates` block alongside the usual figures. |
Code snippets
curl -X POST https://apisathi.in/gw/v1/abb-weekly-v1/ \
-H "X-API-Key: $API_SATHI_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"pdf_base64":"<base64-encoded PDF>","dates":[5,15,25]}'Response
| Field | Type | Required | Constraints |
|---|---|---|---|
| account | object | optional | Holder, masked account no., bank, IFSC. |
| basis | string | optional | Always 'weekly' for this product. |
| average_balance | number | optional | Headline ABB: week-weighted mean of ISO-week average balances. null if no balances found. |
| currency | string | optional | — |
| period | object | optional | from, to, and days = number of calendar days averaged. |
| breakdown | array | optional | Per-week buckets: { period: 'YYYY-Www' (ISO week), average_balance, min_balance, max_balance, days }. |
| meta | object | optional | engine, transactions, had_running_balance, pages. |
| on_dates | object | optional | Present ONLY when `dates` was supplied: the end-of-day balance on each requested day in every month, a per-month average, an overall average, and a `missing` list of requested dates the statement does not cover (never substituted with a nearby day). |
Sample response
{
"account": {
"account_holder": "RAHUL VERMA",
"account_number": "XXXXXX4321",
"bank": "HDFC Bank",
"ifsc": "HDFC0001234"
},
"basis": "weekly",
"average_balance": 61980.25,
"currency": "INR",
"period": {
"from": "2026-04-01",
"to": "2026-06-30",
"days": 91
},
"breakdown": [
{
"period": "2026-W14",
"average_balance": 57800,
"min_balance": 12400,
"max_balance": 71000,
"days": 6
},
{
"period": "2026-W15",
"average_balance": 59100,
"min_balance": 41000,
"max_balance": 78000,
"days": 7
},
{
"period": "2026-W16",
"average_balance": 62400,
"min_balance": 46500,
"max_balance": 84000,
"days": 7
}
],
"on_dates": {
"days": [
5,
15,
25
],
"average_balance": 61980.25,
"months": [
{
"period": "2026-04",
"samples": [
{
"day": 5,
"date": "2026-04-05",
"balance": 58200
},
{
"day": 15,
"date": "2026-04-15",
"balance": 62400
},
{
"day": 25,
"date": "2026-04-25",
"balance": 64100
}
],
"average_balance": 61566.67
}
],
"missing": []
},
"meta": {
"engine": "apisathi-abb-v1",
"transactions": 16,
"had_running_balance": true,
"pages": 4
}
}Reading a 400
A schema rejection returns INVALID_REQUEST with a details array naming the exact field. Read it before guessing — it gives you the JSON path and what was wrong with it.
{
"error": {
"code": "INVALID_REQUEST",
"message": "Request body failed schema validation",
"details": [
{ "path": "/callback_url",
"message": "unexpected property 'callback_url' — this endpoint does not accept unknown fields" }
]
}
}Most endpoints reject unknown top-level fields outright, so an extra key you added for your own bookkeeping will fail the call. Enum errors list the accepted values, and a missing required field is named with its full path.
Error codes
| Code | HTTP | When |
|---|---|---|
| INVALID_INPUT | 422 | The request was rejected as invalid — either it failed OUR schema validation (malformed input; not charged), or the upstream source rejected the value you sent. NOTE: an identifier that is well-formed but simply has NO RECORD is no longer an error — it returns 200 with `verified: false` and is charged (see the result-code table below). FIX the input before retrying; retrying the same value will fail again. |
| INVALID_API_KEY | 401 | Missing, malformed, or revoked X-API-Key. |
| OUT_OF_SCOPE | 403 | API key is not scoped for this product. |
| INSUFFICIENT_BALANCE | 402 | Wallet balance is below the per-call sale price. Recharge and retry. |
| RATE_LIMITED | 429 | Per-key RPS or RPM limit exceeded. Back off and retry after the Retry-After header. |
| PRODUCT_DEPRECATED | 410 | This API has been retired and is no longer available. Stop calling it — it will not return. Check the catalog for the current equivalent. |
| ROUTER_NO_VENDOR | 503 | No healthy vendor is currently available for this product. Transient — safe to retry after a short backoff. Not charged. |
| VENDOR_AUTH_FAILED | 502 | Upstream vendor rejected our credentials (our config issue). Not charged. |
| VENDOR_ERROR | 502 | A genuine transient upstream error (the source was briefly unavailable). Safe to RETRY after a short backoff. Not charged. NOTE: this is NOT for bad input — invalid values return 422 INVALID_INPUT, not 502. |
| TIMEOUT | 504 | Upstream vendor did not respond within the SLA window. Safe to retry after a short backoff. Not charged. |
OpenAPI 3.1
Generated from this product's request/response JSON Schemas.
{
"openapi": "3.1.0",
"info": {
"title": "API Sathi — Average Bank Balance (Weekly)",
"version": "1.0.0",
"description": "Average Bank Balance (ABB) computed on a WEEKLY basis from a bank-statement PDF. We reconstruct the end-of-day balance for every day, group days into ISO weeks, average each week, and return the week-weighted mean of those weekly averages — plus a per-week breakdown. Because each week counts equally (regardless of how many days it holds), the weekly ABB differs from the daily ABB, mainly at partial weeks at the start/end of the period.\n\nSingle synchronous call: POST a base64 PDF, get the ABB back. Works across banks with no per-bank templates. Digital/native PDFs only (not scans). Billing: charged once per successful call; failures (422) are not charged.\n\nTIMING: parse time scales with the number of transactions, not the page count. A 3-month statement typically returns in 15-30s; a dense 12-month statement (900+ rows) measured 64s end to end. Set your client timeout to at least 120s. A short client timeout is the most common integration failure here — the call completes on our side and you are charged for it, but you never see the result.\n\nDATE-WISE ABB: pass `dates` (e.g. [5,10,15,20,25,30]) to also get the end-of-day balance on those specific days of every month — the figures banking partners assess against. Any combination of days 1-31 is supported, and the usual monthly figures are returned alongside, not instead. The response adds an `on_dates` block: `months[]` (the balance for each requested day in each month, plus that month average), an overall `average_balance` across every sampled point, and `missing[]`. A requested day that the statement does not cover — including the 29th-31st in a short month such as February — is listed in `missing` and NEVER substituted with a nearby day, so a lender never assesses against a date that was not actually measured. Date-wise sampling costs nothing extra: it is a parameter on the same call, not a separate product."
},
"servers": [
{
"url": "https://apisathi.in/gw/v1"
}
],
"components": {
"securitySchemes": {
"ApiKeyAuth": {
"type": "apiKey",
"in": "header",
"name": "X-API-Key",
"description": "Your live or test key, e.g. `live_xxxxxxxxxxxx`."
}
}
},
"paths": {
"/abb-weekly-v1": {
"post": {
"operationId": "abbWeeklyV1",
"tags": [
"ai"
],
"summary": "Average Bank Balance (Weekly)",
"description": "Average Bank Balance (ABB) computed on a WEEKLY basis from a bank-statement PDF. We reconstruct the end-of-day balance for every day, group days into ISO weeks, average each week, and return the week-weighted mean of those weekly averages — plus a per-week breakdown. Because each week counts equally (regardless of how many days it holds), the weekly ABB differs from the daily ABB, mainly at partial weeks at the start/end of the period.\n\nSingle synchronous call: POST a base64 PDF, get the ABB back. Works across banks with no per-bank templates. Digital/native PDFs only (not scans). Billing: charged once per successful call; failures (422) are not charged.\n\nTIMING: parse time scales with the number of transactions, not the page count. A 3-month statement typically returns in 15-30s; a dense 12-month statement (900+ rows) measured 64s end to end. Set your client timeout to at least 120s. A short client timeout is the most common integration failure here — the call completes on our side and you are charged for it, but you never see the result.\n\nDATE-WISE ABB: pass `dates` (e.g. [5,10,15,20,25,30]) to also get the end-of-day balance on those specific days of every month — the figures banking partners assess against. Any combination of days 1-31 is supported, and the usual monthly figures are returned alongside, not instead. The response adds an `on_dates` block: `months[]` (the balance for each requested day in each month, plus that month average), an overall `average_balance` across every sampled point, and `missing[]`. A requested day that the statement does not cover — including the 29th-31st in a short month such as February — is listed in `missing` and NEVER substituted with a nearby day, so a lender never assesses against a date that was not actually measured. Date-wise sampling costs nothing extra: it is a parameter on the same call, not a separate product.",
"security": [
{
"ApiKeyAuth": []
}
],
"parameters": [
{
"name": "Idempotency-Key",
"in": "header",
"required": false,
"schema": {
"type": "string"
},
"description": "Optional. Same key returns the same response for 24h."
}
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"pdf_base64"
],
"properties": {
"pdf_base64": {
"type": "string",
"description": "Base64-encoded bank-statement PDF (digital/native, not a scan). Max 15MB."
},
"password": {
"type": "string",
"description": "PDF password, if the statement is locked."
},
"dates": {
"type": "array",
"description": "Optional. Days of the month to sample the balance on, e.g. [5, 15, 25]. Lenders differ on which dates their banking partner assesses ABB against (5/10/15, 5/15/25 and 20/25/30 are all common). Returns an `on_dates` block alongside the usual figures.",
"items": {
"type": "integer",
"minimum": 1,
"maximum": 31
},
"maxItems": 31
}
},
"additionalProperties": false
},
"example": {
"pdf_base64": "<base64-encoded PDF>",
"dates": [
5,
15,
25
]
}
}
}
},
"responses": {
"200": {
"description": "Successful, normalized response.",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"account": {
"type": "object",
"description": "Holder, masked account no., bank, IFSC."
},
"basis": {
"type": "string",
"description": "Always 'weekly' for this product."
},
"average_balance": {
"type": "number",
"description": "Headline ABB: week-weighted mean of ISO-week average balances. null if no balances found."
},
"currency": {
"type": "string"
},
"period": {
"type": "object",
"description": "from, to, and days = number of calendar days averaged."
},
"breakdown": {
"type": "array",
"description": "Per-week buckets: { period: 'YYYY-Www' (ISO week), average_balance, min_balance, max_balance, days }."
},
"meta": {
"type": "object",
"description": "engine, transactions, had_running_balance, pages."
},
"on_dates": {
"type": "object",
"description": "Present ONLY when `dates` was supplied: the end-of-day balance on each requested day in every month, a per-month average, an overall average, and a `missing` list of requested dates the statement does not cover (never substituted with a nearby day)."
}
},
"additionalProperties": true
},
"example": {
"account": {
"account_holder": "RAHUL VERMA",
"account_number": "XXXXXX4321",
"bank": "HDFC Bank",
"ifsc": "HDFC0001234"
},
"basis": "weekly",
"average_balance": 61980.25,
"currency": "INR",
"period": {
"from": "2026-04-01",
"to": "2026-06-30",
"days": 91
},
"breakdown": [
{
"period": "2026-W14",
"average_balance": 57800,
"min_balance": 12400,
"max_balance": 71000,
"days": 6
},
{
"period": "2026-W15",
"average_balance": 59100,
"min_balance": 41000,
"max_balance": 78000,
"days": 7
},
{
"period": "2026-W16",
"average_balance": 62400,
"min_balance": 46500,
"max_balance": 84000,
"days": 7
}
],
"on_dates": {
"days": [
5,
15,
25
],
"average_balance": 61980.25,
"months": [
{
"period": "2026-04",
"samples": [
{
"day": 5,
"date": "2026-04-05",
"balance": 58200
},
{
"day": 15,
"date": "2026-04-15",
"balance": 62400
},
{
"day": 25,
"date": "2026-04-25",
"balance": 64100
}
],
"average_balance": 61566.67
}
],
"missing": []
},
"meta": {
"engine": "apisathi-abb-v1",
"transactions": 16,
"had_running_balance": true,
"pages": 4
}
}
}
}
},
"401": {
"description": "Missing, malformed, or revoked X-API-Key.",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"error": {
"type": "object",
"properties": {
"code": {
"type": "string"
},
"message": {
"type": "string"
},
"call_id": {
"type": "string"
}
}
}
}
}
}
}
},
"402": {
"description": "Wallet balance is below the per-call sale price. Recharge and retry.",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"error": {
"type": "object",
"properties": {
"code": {
"type": "string"
},
"message": {
"type": "string"
},
"call_id": {
"type": "string"
}
}
}
}
}
}
}
},
"403": {
"description": "API key is not scoped for this product.",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"error": {
"type": "object",
"properties": {
"code": {
"type": "string"
},
"message": {
"type": "string"
},
"call_id": {
"type": "string"
}
}
}
}
}
}
}
},
"410": {
"description": "This API has been retired and is no longer available. Stop calling it — it will not return. Check the catalog for the current equivalent.",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"error": {
"type": "object",
"properties": {
"code": {
"type": "string"
},
"message": {
"type": "string"
},
"call_id": {
"type": "string"
}
}
}
}
}
}
}
},
"422": {
"description": "The request was rejected as invalid — either it failed OUR schema validation (malformed input; not charged), or the upstream source rejected the value you sent. NOTE: an identifier that is well-formed but simply has NO RECORD is no longer an error — it returns 200 with `verified: false` and is charged (see the result-code table below). FIX the input before retrying; retrying the same value will fail again.",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"error": {
"type": "object",
"properties": {
"code": {
"type": "string"
},
"message": {
"type": "string"
},
"call_id": {
"type": "string"
}
}
}
}
}
}
}
},
"429": {
"description": "Per-key RPS or RPM limit exceeded. Back off and retry after the Retry-After header.",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"error": {
"type": "object",
"properties": {
"code": {
"type": "string"
},
"message": {
"type": "string"
},
"call_id": {
"type": "string"
}
}
}
}
}
}
}
},
"502": {
"description": "Upstream vendor rejected our credentials (our config issue). Not charged.",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"error": {
"type": "object",
"properties": {
"code": {
"type": "string"
},
"message": {
"type": "string"
},
"call_id": {
"type": "string"
}
}
}
}
}
}
}
},
"503": {
"description": "No healthy vendor is currently available for this product. Transient — safe to retry after a short backoff. Not charged.",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"error": {
"type": "object",
"properties": {
"code": {
"type": "string"
},
"message": {
"type": "string"
},
"call_id": {
"type": "string"
}
}
}
}
}
}
}
},
"504": {
"description": "Upstream vendor did not respond within the SLA window. Safe to retry after a short backoff. Not charged.",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"error": {
"type": "object",
"properties": {
"code": {
"type": "string"
},
"message": {
"type": "string"
},
"call_id": {
"type": "string"
}
}
}
}
}
}
}
}
}
}
}
}
}