Volver al blog
extraction · finO$ Team

Cómo será extraer datos de BBVA México con Python (API en early access)

Cómo se verá el flujo para convertir un estado de cuenta de BBVA México (PDF) a JSON con Python usando el API de finO$, en el roadmap. Hoy la extracción funciona desde el dashboard. Pricing $5 MXN por hoja procesada.

Nota — función en el roadmap. El API REST de finO$ que se muestra en este post (llamadas con API key, endpoints, webhooks) aún no está disponible; está en desarrollo. Este artículo describe cómo será el flujo cuando abra el early access. Hoy puedes extraer el mismo JSON desde el dashboard de finO$ (subes el PDF y descargas JSON/CSV/Excel). Solicita early access al API para usarlo de forma programática en cuanto esté listo.

TL;DR: Cuando el API esté disponible, procesar un estado de cuenta de BBVA México con Python tomará ~10 líneas. El pricing será $5 MXN por hoja procesada. Un estado típico de 8 hojas costará $40 MXN. Hoy, la misma extracción ya funciona desde el dashboard.

Por qué este tutorial

Si trabajas con datos bancarios mexicanos, eventualmente tienes que parsear PDFs de BBVA. Los estados de cuenta de BBVA México tienen un layout consistente desde 2024 (cuenta de cheques, débito, nómina, empresarial) y son una buena base para construir herramientas contables, dashboards de finanzas personales, o motores de underwriting.

Este tutorial muestra cómo se verá llamar al API de finO$ desde Python para convertir un PDF de BBVA en JSON estructurado, una vez que el API entre en early access.

Lo que necesitarás (cuando abra el early access)

  • Python 3.9+
  • Early access al API de finO$ (en el roadmap — solicítalo en getfinos.com)
  • Un PDF de tu estado de cuenta BBVA México

Setup

pip install requests

Exporta tu API key:

export FINOS_API_KEY="tu_api_key_aqui"

El código completo

import os
import time
import requests

API = "https://api.getfinos.com/v1"
KEY = os.environ["FINOS_API_KEY"]
HEADERS = {"Authorization": f"Bearer {KEY}"}

# 1. Sube el PDF
with open("bbva-statement.pdf", "rb") as f:
    upload = requests.post(
        f"{API}/upload",
        headers=HEADERS,
        files={"file": f},
        data={"bank_hint": "bbva_mx"},
    ).json()

job_id = upload["job_id"]

Eso es la mitad. Ahora consultas el resultado:

# 2. Espera a que termine (~15s típico)
while True:
    job = requests.get(f"{API}/jobs/{job_id}", headers=HEADERS).json()
    if job["status"] == "completed":
        break
    if job["status"] == "failed":
        raise Exception(job["error"])
    time.sleep(2)

# 3. Obtén las transacciones
result = requests.get(f"{API}/extractions/{job['extraction_id']}", headers=HEADERS).json()

for tx in result["transactions"]:
    print(f"{tx['date']} | {tx['description'][:40]:40} | {tx['amount']:>10}")

Qué obtienes

El resultado es un JSON con:

{
  "bank": "BBVA México",
  "account_clabe": "012180001234567890",
  "account_type": "cheques",
  "period": { "start": "2026-04-01", "end": "2026-04-30" },
  "opening_balance": 18450.32,
  "closing_balance": 22107.18,
  "transactions": [
    {
      "date": "2026-04-03",
      "posting_date": "2026-04-03",
      "description": "TRANSFERENCIA SPEI RECIBIDA - CLIENTE ACME",
      "reference": "ABCD12345",
      "amount": 15000.00,
      "type": "credit",
      "category": "income.client_payment",
      "balance_after": 33450.32,
      "spei": {
        "tracking_key": "1234567890",
        "source_clabe": "012180009876543210"
      }
    }
  ]
}

Pricing

El pricing es $5 MXN por hoja procesada (mismo precio en el dashboard hoy y en el API cuando abra). Un estado de cuenta de BBVA México típicamente tiene 8 hojas, así que cada extracción cuesta $40 MXN. No hay setup fee, no hay mínimo mensual, no hay compromiso anual.

Si procesas 30 estados al mes (caso típico de un contador con 30 clientes):

  • 30 estados × 8 hojas = 240 hojas
  • 240 × $5 MXN = $1,200 MXN/mes

Es menos del costo de una hora de un asistente contable junior.

Manejo de errores

ERROR_CODES = {
    401: "API key inválida o expirada",
    403: "Sin permiso para este recurso",
    413: "PDF excede límite de 50MB",
    422: "PDF no pudo ser parseado (¿está corrupto o con contraseña?)",
    429: "Rate limit excedido (100 req/min)",
    500: "Error del servidor — reintenta con backoff",
}

response = requests.post(f"{API}/upload", ...)
if not response.ok:
    raise Exception(f"{response.status_code}: {ERROR_CODES.get(response.status_code, 'unknown')}")

Siguiente paso

Si quieres procesar batch de cientos de estados, configura webhooks para no hacer polling:

upload = requests.post(
    f"{API}/upload",
    headers=HEADERS,
    files={"file": f},
    data={
        "bank_hint": "bbva_mx",
        "webhook_url": "https://tu-server.com/finos-callback",
    },
).json()

finO$ te avisa con un POST cuando el job termina. Idempotencia firmada con HMAC-SHA256.

Recursos

Solicitar early access al API