API Sathi docs
← All products

Average Bank Balance (Weekly)

ai

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

POST /gw/v1/abb-weekly-v1/SLA p95: 90000 ms

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.

X-API-Key: live_xxxxxxxxxxxx

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.

FieldTypeRequiredConstraints
pdf_base64stringrequiredBase64-encoded bank-statement PDF (digital/native, not a scan). Max 15MB.
passwordstringoptionalPDF password, if the statement is locked.
datesinteger[]optionalOptional. 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

FieldTypeRequiredConstraints
accountobjectoptionalHolder, masked account no., bank, IFSC.
basisstringoptionalAlways 'weekly' for this product.
average_balancenumberoptionalHeadline ABB: week-weighted mean of ISO-week average balances. null if no balances found.
currencystringoptional
periodobjectoptionalfrom, to, and days = number of calendar days averaged.
breakdownarrayoptionalPer-week buckets: { period: 'YYYY-Www' (ISO week), average_balance, min_balance, max_balance, days }.
metaobjectoptionalengine, transactions, had_running_balance, pages.
on_datesobjectoptionalPresent 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

CodeHTTPWhen
INVALID_INPUT422The 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_KEY401Missing, malformed, or revoked X-API-Key.
OUT_OF_SCOPE403API key is not scoped for this product.
INSUFFICIENT_BALANCE402Wallet balance is below the per-call sale price. Recharge and retry.
RATE_LIMITED429Per-key RPS or RPM limit exceeded. Back off and retry after the Retry-After header.
PRODUCT_DEPRECATED410This 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_VENDOR503No healthy vendor is currently available for this product. Transient — safe to retry after a short backoff. Not charged.
VENDOR_AUTH_FAILED502Upstream vendor rejected our credentials (our config issue). Not charged.
VENDOR_ERROR502A 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.
TIMEOUT504Upstream 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"
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}