Integrar una API OCR parece tan simple como «enviar una imagen y recibir texto», pero lo que decide si el proyecto llega a producción son otras cosas: el formato y el tamaño de la imagen, si la llamada es síncrona o asíncrona, cómo se definen las coordenadas y los campos, y cómo responder cuando el resultado no es estable. En la etapa de demo estos problemas no se notan; con facturas y escaneos reales salen a la superficie.

Este artículo usa como ejemplo las interfaces compatibles con OpenAI y los servicios de OCR habituales para explicar la cadena completa, desde la carga hasta la salida estructurada, con ejemplos, preprocesamiento, diagnóstico de errores y una lista de verificación para migraciones.

Nota: OpenAI, Google, Microsoft, Alibaba Cloud, Tencent Cloud, Baidu y las demás marcas mencionadas son marcas registradas de sus respectivos titulares. Este artículo tiene fines técnicos y no implica afiliación con esos proveedores; las interfaces, los modelos, los límites de imagen y las reglas de facturación se rigen por la documentación oficial de cada plataforma y por las páginas de este sitio.

1. Interfaz y autenticación

Hay dos formas habituales de integrar OCR. Primero confirma cuál estás usando:

  • Interfaz dedicada de OCR: un endpoint independiente de reconocimiento que recibe una imagen o un PDF y devuelve bloques de texto, coordenadas y confianza; algunas admiten plantillas de facturas, documentos de identidad y tablas.
  • Interfaz de conversación multimodal: se usa /v1/chat/completions, se coloca la imagen en el mensaje y se pide al modelo de visión que devuelva texto o JSON. Es adecuada para diseños complejos, escritura a mano y tareas de comprensión.

La autenticación de una interfaz compatible con OpenAI suele ser así:

POST https://ai.zhongshangqihang.com/v1/chat/completions
Authorization: Bearer sk-tu-clave
Content-Type: application/json

La URL base se escribe solo hasta /v1; no armes la ruta por tu cuenta y luego la entregues al SDK, porque es fácil terminar en un 404. Algunos proveedores de nube usan firma con access key + secret key o primero intercambian un access_token, así que no se les puede aplicar directamente el esquema Bearer; guíate por la documentación oficial.

2. Cómo enviar la imagen: URL, Base64 y carga de archivos

Hay tres formas habituales de carga; la elección depende de dónde esté la imagen y qué admita la interfaz:

Método Adecuado para Consideraciones
URL pública Imágenes ya alojadas en object storage o CDN El servidor debe poder acceder; el enlace debe tener vigencia suficiente; evita publicar imágenes sensibles
Data URL en Base64 Archivos locales, imágenes de red interna, pruebas temporales El tamaño aumenta alrededor de un tercio; un prefijo o una codificación incorrectos producen 400
Carga multipart Interfaces OCR dedicadas del proveedor Los nombres de campo, la codificación y el tamaño se rigen por la documentación

El formato de Base64 suele ser data:image/jpeg;base64,<contenido codificado>. Los errores más comunes son omitir el prefijo, incluir saltos de línea dentro de la cadena o usar codificación URL-safe sin restaurarla después.

En cuanto a formatos, JPEG, PNG y WebP son los más habituales; GIF, TIFF, BMP y PDF dependen de cada interfaz. El tamaño por imagen suele ir de unos pocos MB a 20 MB, y al superar el límite es común recibir 413 o 400. Más resolución no siempre es mejor: el servidor puede reescalar imágenes demasiado grandes. Para escaneos se suelen usar 200 a 300 DPI; para fotos de teléfono, un lado largo de 1600 a 2400 píxeles como punto de partida, y luego se valida con muestras reales.

3. Síncrono o asíncrono

La interfaz síncrona sirve para una sola imagen y escenarios interactivos: se envía la solicitud y se espera el resultado. El riesgo son los timeouts, sobre todo con imágenes grandes, PDF de varias páginas o colas del lado del servidor; la conexión puede cerrarse antes de tiempo.

La interfaz asíncrona conviene para lotes y documentos de varias páginas. El flujo suele ser:

  1. Enviar la imagen o el archivo y obtener un ID de tarea.
  2. Consultar el estado periódicamente o esperar una notificación por callback.
  3. Cuando termine, descargar o consultar el resultado del reconocimiento.
  4. Los resultados suelen tener un plazo de conservación; guárdalos en tu base de datos a tiempo.

No elijas solo por la simplicidad de la interfaz. Si una solicitud puede tardar decenas de segundos, el modo síncrono ocupa conexiones e hilos, y los reintentos amplifican la presión. El modo asíncrono debe manejar idempotencia, envíos duplicados, retroceso en el sondeo y expiración de resultados. En producción conviene crear una tabla de IDs de tarea que registre el estado y la ubicación del resultado.

4. Ejemplo de llamada con curl

A continuación se hace un reconocimiento de imagen con una interfaz compatible con OpenAI. El nombre del modelo se rige por Precios de modelos; elige uno que admita entrada de imágenes:

export ZSQH_API_KEY="sk-tu-clave"

curl "https://ai.zhongshangqihang.com/v1/chat/completions" \
  -H "Authorization: Bearer $ZSQH_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "gpt-5",
    "messages": [
      {
        "role": "user",
        "content": [
          {"type": "text", "text": "Reconoce el texto de la imagen, salida línea por línea, sin explicaciones."},
          {"type": "image_url", "image_url": {"url": "https://example.com/invoice.jpg"}}
        ]
      }
    ]
  }'

Si recibes 401, revisa primero la key y los encabezados; si recibes 400, revisa si la URL de la imagen es accesible y si el formato está soportado; si recibes 404, comprueba si la URL base y la ruta se están concatenando dos veces.

5. Llamada en Python y salida estructurada

En un proyecto real rara vez basta con texto plano; normalmente se necesitan campos. Puedes pedir al modelo que devuelva JSON según un esquema y validarlo localmente:

import base64
import json
import os
from openai import OpenAI

client = OpenAI(
    api_key=os.environ["ZSQH_API_KEY"],
    base_url="https://ai.zhongshangqihang.com/v1",
)

with open("invoice.jpg", "rb") as f:
    image_b64 = base64.b64encode(f.read()).decode()

schema_hint = '{"invoice_no": "", "date": "", "amount": "", "seller": ""}'
resp = client.chat.completions.create(
    model=os.environ["OCR_MODEL"],  # Modelo con entrada de imagen según la página de precios
    messages=[{
        "role": "user",
        "content": [
            {"type": "text", "text": f"Reconoce la factura y genera este JSON; usa null en los campos que no encuentres: {schema_hint}"},
            {"type": "image_url", "image_url": {"url": f"data:image/jpeg;base64,{image_b64}"}},
        ],
    }],
)
data = json.loads(resp.choices[0].message.content)
print(data)

Que este código funcione no significa que esté listo para producción. La salida del modelo puede incluir bloques de código, texto explicativo o campos de más, y json.loads fallará directamente. En la práctica: prioriza el modo JSON o la salida estructurada que admita la plataforma; elimina las marcas de bloque de código antes de analizar; valida los tipos con Pydantic o jsonschema; si falla, devuelve el mensaje de error al modelo y reintenta una vez; si vuelve a fallar, deriva a revisión humana. Los importes, las fechas y los números de identificación fiscal también deben validarse de forma cruzada.

6. Coordenadas, tablas y campos estructurados

El OCR tradicional devuelve el contenido de cada bloque de texto, sus coordenadas y su confianza; los modelos multimodales generan según el prompt y sus coordenadas no siempre son fiables. Primero confirma el formato de las coordenadas: si son polígonos de cuatro puntos, [x1, y1, x2, y2] o [x, y, w, h]; si la unidad es el píxel o está normalizada de 0 a 1000; y si el origen está en la esquina superior izquierda. Si hubo rotación o recorte, las coordenadas deben convertirse.

Si la imagen se redimensionó antes del reconocimiento, las coordenadas deben mapearse proporcionalmente a la imagen original; de lo contrario, los recuadros de resaltado quedarán desplazados. En escenarios de tablas hay que definir con claridad las filas, las columnas, las celdas combinadas y las reglas para valores vacíos; para tablas complejas conviene usar una interfaz específica de reconocimiento de tablas y luego pedir al modelo que organice el resultado.

Los campos estructurados requieren un esquema definido de antemano: nombre del campo, tipo, si es obligatorio, cómo se representa un valor vacío y cómo se normalizan fechas e importes. En facturas también hay que considerar la moneda, los impuestos y la coherencia entre subtotal, impuesto y total. La confianza permite clasificar: los campos con confianza baja pasan a revisión humana en lugar de descartarse.

7. Preprocesamiento: reconocer con más estabilidad

La mayor parte del error de reconocimiento se decide antes de enviar la solicitud. Procesa en este orden:

  1. Lee la orientación EXIF y rota la imagen para evitar documentos acostados.
  2. Recorta al área del documento y elimina fondos grandes y objetos irrelevantes.
  3. Corrige pequeñas inclinaciones; si la inclinación es evidente, detecta primero los bordes.
  4. Controla la resolución: 200 a 300 DPI para escaneos y un lado largo de 1600 a 2400 píxeles como punto de partida para fotos de teléfono.
  5. En documentos descoloridos o de bajo contraste, convierte a escala de grises y aumenta el contraste con moderación.
  6. Divide los PDF de varias páginas o las capturas largas por página, dejando un pequeño solapamiento en los cortes.

Una imagen borrosa no se recupera con enfoque, y el texto de baja resolución no revela detalles al ampliarlo. El objetivo del preprocesamiento es reducir el ruido, no dejar la imagen bonita. Cada vez que ajustes parámetros, compara la precisión de campos con el mismo lote de muestras reales.

8. Errores frecuentes y diagnóstico

Error Causa habitual Qué hacer
401 Unauthorized Key incorrecta, vencida o encabezado mal escrito Revisa el formato Bearer, los espacios y la variable de entorno
403 Forbidden Sin permiso de modelo, restricción regional o permisos insuficientes de la subcuenta Revisa la autorización del modelo y la política de la cuenta
400 error de imagen Prefijo Base64 incorrecto, archivo dañado o formato no soportado Decodifica y valida la cabecera del archivo; cambia a JPEG o PNG
413 Payload Too Large Imagen o cuerpo de solicitud fuera de límite Comprime, escala o recorta, o usa carga de archivo o modo asíncrono
429 Too Many Requests Frecuencia o concurrencia excedidas Reduce la concurrencia y reintenta con retroceso exponencial
Timeout o 502 Imagen grande, documento largo o cola del servidor Usa tareas asíncronas o reduce el tamaño de la solicitud
Resultado vacío o desordenado Orientación incorrecta, baja resolución o diseño complejo Rota, mejora la nitidez y divide por columnas

Al diagnosticar, registra primero el ID de solicitud, el nombre del modelo, las dimensiones de la imagen y el tiempo de respuesta. Muchos casos de «el modelo funciona mal» en realidad son imágenes enviadas de forma incorrecta, coordenadas sin convertir o esquemas inconsistentes.

9. Migración y lista de verificación para producción

Pasar de un servicio de OCR a otro normalmente no se limita a cambiar base_url y api_key. Antes de salir a producción confirma:

  • Si la nueva interfaz es OCR dedicado o visión conversacional, y si la estructura del cuerpo de la solicitud es compatible.
  • Qué formatos de imagen admite, el tamaño por imagen, el número de páginas y los límites de resolución.
  • Si es síncrona o asíncrona, cuánto tiempo se conservan los resultados y si el callback necesita una dirección pública.
  • Si el formato, la unidad y el origen de las coordenadas coinciden con los del servicio anterior.
  • Si el esquema de campos, los idiomas admitidos y los tipos de factura cubren el negocio actual.
  • Si existe un plan de despliegue gradual para derivar una parte del tráfico y comparar la precisión.
  • Si hay una ruta de reversión, conservando durante un tiempo la key y la interfaz antiguas.

Conviene encapsular el OCR en un cliente independiente, por ejemplo recognize(image, schema) -> Result, y concentrar las diferencias entre proveedores en una capa de adaptación, para no reescribir el código de negocio al cambiar de servicio. Los modelos, los límites y los precios se rigen por Precios de modelos.

Preguntas frecuentes

¿La API OCR admite PDF?

Depende de la interfaz. Los servicios de OCR dedicados suelen admitir PDF, y los documentos de varias páginas suelen procesarse de forma asíncrona; las interfaces de conversación compatibles con OpenAI normalmente tratan la entrada como imagen, así que un PDF debe dividirse en páginas y convertirse a imagen, o usarse una interfaz de archivos. Guíate por la documentación.

¿Cuál es el tamaño máximo de imagen?

Varía entre proveedores. Lo habitual es de unos pocos MB a 20 MB por imagen, con límites adicionales de píxeles y de número de páginas. Se recomienda comprimir y escalar antes de subir para reducir timeouts y costos de transferencia.

¿Por qué el orden del resultado reconocido sale desordenado?

La causa habitual son los diseños de varias columnas, las tablas y la mezcla con imágenes. El OCR tradicional ordena por coordenadas y los modelos multimodales generan según el orden de lectura; ambos pueden equivocarse. Puedes dividir por columnas, pedir la salida por regiones o usar una interfaz con análisis de diseño.

¿Cuál es la forma más estable de obtener campos estructurados?

Define primero el esquema, pide al modelo una salida JSON y valida localmente tipos, expresiones regulares y reglas de negocio. Si la plataforma admite salida estructurada, úsala; si no, agrega un reintento con el mensaje de error y, si vuelve a fallar, deriva a revisión humana.

¿Qué hay que cambiar al migrar de un servicio de OCR a otro?

Además de la URL base y la key, revisa la estructura de la solicitud, el modo síncrono o asíncrono, los límites de imagen, el formato de coordenadas, el esquema y las reglas de limitación de tasa. Primero haz un despliegue gradual, compara la precisión de campos con muestras reales y después cambia todo el tráfico.

Próximos pasos

Si quieres validar primero una solicitud, crea una key con cuota baja desde la página de registro y prueba la carga, el reconocimiento y la salida JSON con una factura real. Para la selección definitiva, consulta Precios de modelos y compara la precisión de campos con un lote pequeño de imágenes etiquetadas. Los precios cambian; guíate por lo publicado en el sitio.

Lecturas relacionadas: