WaterMeter AI

API OCR para medidores de agua

API OCR para medidores de agua. Esta página documenta la API HTTP pública expuesta por el backend del sitio web para integraciones de terceros.

Empieza con una subida real

Regístrate primero, sube tu propia foto del medidor de agua en el espacio de trabajo y crea una clave API cuando estés listo para integrar.

Resumen

  • Usa el backend del sitio web para todas las integraciones externas.
  • No llames directamente a waterMeterAi desde programas de terceros.
  • La API abierta comparte los mismos usuarios, cuotas, caché, tareas y reglas de auditoría que el portal web.
  • El backend puede apuntar a diferentes negocios de IA descendentes según la configuración de despliegue.
  • Las respuestas de tareas ahora incluyen un objeto genérico result_summary para que distintos negocios puedan mostrar distintos tipos de resultado.

Autenticación

  • Inicia sesión en el sitio web y crea una clave API en la página de claves API.
  • La clave API completa solo se muestra una vez al crearla.
  • Envía la clave en el encabezado Authorization de cada solicitud.
Authorization: Bearer wm_xxxxxxxxxxxxxxxxx

API Key Permissions and Limits

  • Each API key can have independent scopes, an IPv4/IPv6/CIDR allowlist, a total requests-per-minute limit, and a task submissions-per-minute limit.
  • A valid key without the required scope or outside its source IP allowlist returns 403.
  • Every valid-key request consumes the total request limit, including requests rejected by scope, IP, or submission policy.
  • Batch requests consume one total request unit and one submission unit per uploaded file.
  • Rate-limited responses return 429 with Retry-After and both X-RateLimit-* and X-SubmissionLimit-* headers.
  • Task creation also has a per-key concurrency limit and returns X-ConcurrencyLimit-* headers; a concurrency rejection uses 429.
  • For batch concurrency, only files that pass image validation reserve slots, and success headers report the actual active slots after task binding.
Retry-After: 30
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 59
X-RateLimit-Reset: 1785902400
X-SubmissionLimit-Limit: 10
X-SubmissionLimit-Remaining: 9
X-ConcurrencyLimit-Limit: 3
X-ConcurrencyLimit-Active: 1
X-ConcurrencyLimit-Remaining: 2

Reglas de carga

  • Tamaño máximo de archivo: 20 MB por imagen.
  • Formatos compatibles: JPEG, PNG, WEBP.
  • El backend valida el contenido real del archivo, no solo la extensión.

Aplicación del tamaño de carga

  • Las cargas por lotes aceptan hasta 8 imágenes de forma predeterminada, con un límite de 20 MB por imagen y un límite total de contenido de archivo de 20 MB.
  • Un archivo o lote que excede su límite devuelve 413 antes de la validación de la imagen o la creación de la tarea; no se crea ningún lote parcial.
  • El backend aplica el recuento de bytes recibidos reales incluso cuando falta Content-Length o se utiliza una transferencia fragmentada.

Estados de tarea

  • queued: aceptada y en espera de procesamiento por IA.
  • running: se está procesando, o ya se entregó al despachador y aún se consulta el resultado final.
  • batch_waiting_ai: todos los elementos del lote esperan a que el servicio de IA vuelva a estar en línea.
  • batch_running: el lote ya se entregó al despachador y sigue procesándose.
  • done: finalizada correctamente.
  • failed: el procesamiento falló.
  • waiting_ai: en cola hasta que el servicio de IA vuelva a estar en línea; después se reanuda automáticamente.

Fuentes de resultado

  • fresh: generado por una nueva ejecución de IA.
  • cached_exact: coincidió con la caché de la versión de IA actual.
  • cached_stale: IA fuera de línea; se devolvió el resultado en caché más reciente.
  • pending: aún no hay resultado final.
  • failed: la tarea falló.
  • Cuando una tarea aún no está terminada, revisa error_message para ver el último motivo de reintento o espera.

Cuota y facturación

  • Usa `GET /api/open/quota` antes de enviar cargas grandes si tu integración necesita evitar errores por falta de cuota.
  • Solo las ejecuciones AI `fresh` consumen cuota.
  • Los resultados `cached_exact`, `cached_stale`, `pending` y `failed` no consumen cuota.
  • Las cuentas gratuitas usan primero la cuota diaria. Las cuentas de pago usan paquetes de cuota periódica activos y luego cuota temporal o fija según la política del backend.
  • Cuando la cuota se agota, el envío de tareas devuelve `429` e incluye un mensaje de error en lugar de crear una tarea nueva.

Caché y vigencia del resultado

  • El backend almacena resultados correctos por hash de imagen y versión de AI, incluido el espacio de nombres del negocio descendente.
  • `cached_exact` significa que la misma imagen ya tiene un resultado correcto para la versión AI actual, por lo que no se necesitó una nueva ejecución.
  • `cached_stale` significa que el servicio AI está sin conexión y el backend devolvió el último resultado histórico disponible.
  • Cuando `is_latest_ai_version` es false, guarda el resultado como dato histórico utilizable pero revisable.
  • No asumas que toda tarea `done` consumió cuota; revisa `result_source`.

Task completion webhooks

  • Send signed task completion events to your HTTPS endpoint with automatic retries.
  • This API key is inactive. Existing records remain available, but new, enabled, test, and secret-rotation operations are blocked.
X-WaterMeter-Webhook-Id: whev_new_event
X-WaterMeter-Webhook-Replay-Of: whev_original_event
replay_of_event_id: whev_original_event
blocked endpoint management: HTTP 409
canceled delivery status: canceled

GET/api/open/quota

Lea la cuota actual y el uso restante.

Ejemplo de solicitud
curl -X GET "https://watermeterai.com/api/open/quota" \
  -H "Authorization: Bearer wm_xxxxxxxxxxxxxxxxx"
Ejemplo de respuesta
{
  "ok": true,
  "email": "[email protected]",
  "quota": {
    "account_type": "free",
    "daily_limit": 20,
    "used": 3,
    "remaining": 17,
    "day_tag": "2026-06-01",
    "is_unlimited": false,
    "monthly_remaining": 0,
    "monthly_bonus_remaining": 0,
    "fixed_remaining": 0,
    "nearest_expire_at": null,
    "quota_summary_text": "Free daily quota: 3/20 used today. Remaining: 17."
  }
}

GET/api/open/ai-status

Lea el estado del servicio público para el envío de tareas y el respaldo de resultados almacenados en caché.

Ejemplo de solicitud
curl -X GET "https://watermeterai.com/api/open/ai-status" \
  -H "Authorization: Bearer wm_xxxxxxxxxxxxxxxxx"
Ejemplo de respuesta
{
  "ok": true,
  "is_online": true,
  "busy": false,
  "active_jobs": 0,
  "public_status": "online",
  "public_status_label": "online",
  "public_status_reason": "ready",
  "accepts_new_tasks": true,
  "can_return_cached_result": true,
  "queue_state": "idle",
  "queue_count": 0,
  "current_ai_version": "water_v2:wm-ai-v2-2026-06-01",
  "last_checked_at": "2026-06-01T06:15:30Z"
}

POST/api/open/tasks

Sube una imagen y crea una sola tarea.

Ejemplo de solicitud
curl -X POST "https://watermeterai.com/api/open/tasks" \
  -H "Authorization: Bearer wm_xxxxxxxxxxxxxxxxx" \
  -F "file=@D:\data\meter_001.jpg"
Ejemplo de respuesta
{
  "ok": true,
  "task_id": "8e6c4d1f8f8348c0bc53e2e39d2dc111",
  "status": "queued",
  "result_source": "pending",
  "visibility": "private",
  "ai_online": true,
  "is_latest_ai_version": false,
  "final_reading": "-",
  "result_summary": {
    "resultKind": "unknown",
    "primaryLabel": "Result",
    "primaryValue": "-",
    "secondaryLabel": "Detail",
    "secondaryValue": "-",
    "summaryText": "No result is available yet."
  },
  "error_message": "",
  "created_at": "2026-06-01T06:16:10Z"
}

GET/api/open/tasks/{task_id}

Leer el estado de una tarea y la lectura final.

Ejemplo de solicitud
curl -X GET "https://watermeterai.com/api/open/tasks/task_a" \
  -H "Authorization: Bearer wm_xxxxxxxxxxxxxxxxx"
Ejemplo de respuesta
{
  "ok": true,
  "task_id": "8e6c4d1f8f8348c0bc53e2e39d2dc111",
  "status": "done",
  "result_source": "fresh",
  "ai_online": true,
  "is_latest_ai_version": true,
  "final_reading": "123.45",
  "meter_type": "pointer",
  "success": "yes",
  "result_summary": {
    "resultKind": "water_meter",
    "primaryLabel": "Final Reading",
    "primaryValue": "123.45",
    "secondaryLabel": "Meter Type",
    "secondaryValue": "pointer",
    "summaryText": "Water meter recognition finished."
  },
  "error_message": "",
  "created_at": "2026-06-01T06:16:10Z"
}

GET/api/open/tasks

Lea las tareas recientes propiedad del usuario clave actual API.

Ejemplo de solicitud
curl -X GET "https://watermeterai.com/api/open/tasks?limit=20" \
  -H "Authorization: Bearer wm_xxxxxxxxxxxxxxxxx"
Ejemplo de respuesta
{
  "ok": true,
  "items": [
    {
      "task_id": "8e6c4d1f8f8348c0bc53e2e39d2dc111",
      "status": "done",
      "result_source": "cached_exact",
      "final_reading": "123.45",
      "meter_type": "pointer",
      "success": "yes",
      "result_summary": {},
      "ai_online": true,
      "is_latest_ai_version": true,
      "created_at": "2026-06-01T06:16:10Z",
      "error_message": ""
    }
  ]
}

POST/api/open/batches

Cargue varias imágenes en una sola solicitud.

Ejemplo de solicitud
curl -X POST "https://watermeterai.com/api/open/batches" \
  -H "Authorization: Bearer wm_xxxxxxxxxxxxxxxxx" \
  -F "files=@D:\data\meter_001.jpg" \
  -F "files=@D:\data\meter_002.jpg"
Ejemplo de respuesta
{
  "ok": true,
  "batch_id": "5a14f4e5f6d34fdabce00e62c0dd0001",
  "status": "batch_running",
  "total_count": 2,
  "done_count": 0,
  "failed_count": 0,
  "pending_count": 2,
  "items": [
    {
      "index": 0,
      "file_name": "meter_001.jpg",
      "task_id": "task_a",
      "status": "queued",
      "result_source": "pending",
      "final_reading": "-",
      "failure_kind": "",
      "error_message": ""
    },
    {
      "index": 1,
      "file_name": "meter_002.jpg",
      "task_id": "task_b",
      "status": "queued",
      "result_source": "pending",
      "final_reading": "-",
      "failure_kind": "",
      "error_message": ""
    }
  ]
}

GET/api/open/batches/{batch_id}

Lea el progreso del lote y los estados de las tareas por archivo.

Ejemplo de solicitud
curl -X GET "https://watermeterai.com/api/open/batches/5a14f4e5f6d34fdabce00e62c0dd0001" \
  -H "Authorization: Bearer wm_xxxxxxxxxxxxxxxxx"
Ejemplo de respuesta
{
  "ok": true,
  "batch_id": "5a14f4e5f6d34fdabce00e62c0dd0001",
  "status": "batch_done",
  "total_count": 2,
  "done_count": 2,
  "failed_count": 0,
  "pending_count": 0,
  "items": [
    {
      "index": 0,
      "file_name": "meter_001.jpg",
      "task_id": "task_a",
      "status": "done",
      "result_source": "fresh",
      "final_reading": "123.45",
      "failure_kind": "",
      "error_message": ""
    },
    {
      "index": 1,
      "file_name": "meter_002.jpg",
      "task_id": "task_b",
      "status": "done",
      "result_source": "cached_exact",
      "final_reading": "456.78",
      "failure_kind": "",
      "error_message": ""
    }
  ]
}

Flujo recomendado

  • Consulta `GET /api/open/ai-status` antes de enviar cargas grandes.
  • Guarda `task_id` o `batch_id` en tu propio sistema inmediatamente después del envío.
  • Trata `queued`, `running`, `waiting_ai`, `batch_waiting_ai` y `batch_running` como estados no finales y sigue consultando.
  • Usa solicitudes por lote cuando la ruta de red al backend tenga alta latencia y el negocio desplegado admita modo por lote.
  • Las solicitudes por lote actualmente pasan por `webBackend -> dispatchCenter -> waterMeterAi`.
  • `batch_waiting_ai` significa que todos los elementos siguen esperando al servicio de IA, no que se estén ejecutando activamente.
  • Cuando el negocio desplegado sea `CAD`, usa `POST /api/open/tasks` por ahora y considera que la carga por lote no está disponible.

Notas de error

  • 400: solicitud no válida o la empresa implementada no admite la carga por lotes.
  • 401: clave API faltante, no válida, caducada o deshabilitada.
  • 403: the user account is not active, the key lacks the required scope, or the source IP is outside the key allowlist.
  • 404: tarea o lote no encontrado o no propiedad del usuario actual.
  • 413: archivo demasiado grande.
  • 415: tipo de imagen no admitido o contenido de imagen no válido.
  • 429: quota exhausted or the API key request, submission, or concurrency limit was exceeded.
Carga por lotes 400 no disponible
{
  "detail": "batch upload is not supported for business: cax"
}
401 clave API faltante o no válida
{
  "ok": false,
  "error": "unauthorized",
  "message": "Missing or invalid API key."
}
Archivo 413 demasiado grande
{
  "ok": false,
  "error": "file_too_large",
  "message": "Maximum file size is 20MB."
}
Cupo 429 agotado
{
  "ok": false,
  "error": "quota_exhausted",
  "message": "Quota exhausted.",
  "quota": {
    "remaining": 0,
    "quota_summary_text": "Free daily quota: 20/20 used today. Remaining: 0."
  }
}

¿Listo para probar el flujo?

Usa el espacio de trabajo web para la primera foto y pasa a las claves API cuando el formato del resultado encaje con tu sistema.