API gratis de centrado de cartas: PSA y fotos

Centering Lab es una API REST gratuita de centrado de cartas, sin clave API. En https://centeringlab.com/api/v1 devuelve mediciones de fotos, proporciones y límites PSA, BGS, CGC o SGC basados solo en el centrado. No predice la nota final ni autentica cartas. Llámala desde tu servidor. OpenAPI.

Inicio rápido

Ejecuta curl en el terminal. Ambos ejes del anverso están presentes, pero falta el reverso: incomplete es true. PSA 10 es un límite por centrado, no una predicción de nota final.

POST /api/v1/grade

Solicitud

curl --fail-with-body 'https://centeringlab.com/api/v1/grade' \
  -H 'Content-Type: application/json' \
  --data '{"front_lr":0.55,"front_tb":"50/50","house":"PSA"}'

Respuesta HTTP 200

Extracto de una respuesta HTTP real local; se omiten campos y otras bandas. Las claves JSON y los mensajes del servidor siguen en inglés.

{
  "front": {
    "ratios": {
      "lr": 55,
      "tb": 50
    }
  },
  "combined": {
    "PSA": {
      "maximum_grade": 10,
      "source_status": "official"
    }
  },
  "normalizations": [
    {
      "field": "front_lr",
      "input": 0.55,
      "ratio": "55/45",
      "note": "0.55 read as 55/45"
    }
  ],
  "incomplete": true,
  "missing_faces": [
    "back"
  ],
  "disclaimer": "This is a ceiling from centering alone, not a prediction of the final grade."
}

Endpoints y campos

Proporciones conocidas

POST /grade requiere al menos uno de front_lr, front_tb, back_lr, back_tb. Normaliza al lado mayor. Acepta porcentajes (55), fracciones (0.55) y pares que sumen 100 (55/45) o 1 (0.55/0.45). Separadores: /, -, –, —, :, x y espacios; % opcional. El escalar 1 es ambiguo y se rechaza. Sufijos L/R o left/right solo para *_lr; T/B o top/bottom para *_tb.

house filtra PSA, BGS, CGC o SGC. normalizations declara conversiones; missing_axes, missing_faces e incomplete indican datos ausentes que pueden reducir el límite. combined combina caras; limiting_face y limiting_axis señalan la restricción. Cada límite incluye estado y fuentes.

Campos del contrato (nombres JSON exactos)

input: front_lr, front_tb, back_lr, back_tb, house
output: front, front.ratios, front.ratios.lr, front.ratios.tb, front.missing_axes, front.ceilings, front.ceilings.PSA, front.ceilings.PSA.maximum_grade, front.ceilings.PSA.label, front.ceilings.PSA.rank, front.ceilings.PSA.limiting_face, front.ceilings.PSA.limiting_axis, front.ceilings.PSA.distance_to_next, front.ceilings.PSA.next_grade, front.ceilings.PSA.next_label, front.ceilings.PSA.borderline, front.ceilings.PSA.unconstrained, front.ceilings.PSA.source_status, front.ceilings.PSA.standard_status, front.ceilings.PSA.source, front.ceilings.PSA.sources, front.ceilings.PSA.verified, front.ceilings.PSA.verification, front.ceilings.BGS, front.ceilings.BGS.maximum_grade, front.ceilings.BGS.label, front.ceilings.BGS.rank, front.ceilings.BGS.limiting_face, front.ceilings.BGS.limiting_axis, front.ceilings.BGS.distance_to_next, front.ceilings.BGS.next_grade, front.ceilings.BGS.next_label, front.ceilings.BGS.borderline, front.ceilings.BGS.unconstrained, front.ceilings.BGS.source_status, front.ceilings.BGS.standard_status, front.ceilings.BGS.source, front.ceilings.BGS.sources, front.ceilings.BGS.verified, front.ceilings.BGS.verification, front.ceilings.CGC, front.ceilings.CGC.maximum_grade, front.ceilings.CGC.label, front.ceilings.CGC.rank, front.ceilings.CGC.limiting_face, front.ceilings.CGC.limiting_axis, front.ceilings.CGC.distance_to_next, front.ceilings.CGC.next_grade, front.ceilings.CGC.next_label, front.ceilings.CGC.borderline, front.ceilings.CGC.unconstrained, front.ceilings.CGC.source_status, front.ceilings.CGC.standard_status, front.ceilings.CGC.source, front.ceilings.CGC.sources, front.ceilings.CGC.verified, front.ceilings.CGC.verification, front.ceilings.SGC, front.ceilings.SGC.maximum_grade, front.ceilings.SGC.label, front.ceilings.SGC.rank, front.ceilings.SGC.limiting_face, front.ceilings.SGC.limiting_axis, front.ceilings.SGC.distance_to_next, front.ceilings.SGC.next_grade, front.ceilings.SGC.next_label, front.ceilings.SGC.borderline, front.ceilings.SGC.unconstrained, front.ceilings.SGC.source_status, front.ceilings.SGC.standard_status, front.ceilings.SGC.source, front.ceilings.SGC.sources, front.ceilings.SGC.verified, front.ceilings.SGC.verification, back, back.ratios, back.ratios.lr, back.ratios.tb, back.missing_axes, back.ceilings, back.ceilings.PSA, back.ceilings.PSA.maximum_grade, back.ceilings.PSA.label, back.ceilings.PSA.rank, back.ceilings.PSA.limiting_face, back.ceilings.PSA.limiting_axis, back.ceilings.PSA.distance_to_next, back.ceilings.PSA.next_grade, back.ceilings.PSA.next_label, back.ceilings.PSA.borderline, back.ceilings.PSA.unconstrained, back.ceilings.PSA.source_status, back.ceilings.PSA.standard_status, back.ceilings.PSA.source, back.ceilings.PSA.sources, back.ceilings.PSA.verified, back.ceilings.PSA.verification, back.ceilings.BGS, back.ceilings.BGS.maximum_grade, back.ceilings.BGS.label, back.ceilings.BGS.rank, back.ceilings.BGS.limiting_face, back.ceilings.BGS.limiting_axis, back.ceilings.BGS.distance_to_next, back.ceilings.BGS.next_grade, back.ceilings.BGS.next_label, back.ceilings.BGS.borderline, back.ceilings.BGS.unconstrained, back.ceilings.BGS.source_status, back.ceilings.BGS.standard_status, back.ceilings.BGS.source, back.ceilings.BGS.sources, back.ceilings.BGS.verified, back.ceilings.BGS.verification, back.ceilings.CGC, back.ceilings.CGC.maximum_grade, back.ceilings.CGC.label, back.ceilings.CGC.rank, back.ceilings.CGC.limiting_face, back.ceilings.CGC.limiting_axis, back.ceilings.CGC.distance_to_next, back.ceilings.CGC.next_grade, back.ceilings.CGC.next_label, back.ceilings.CGC.borderline, back.ceilings.CGC.unconstrained, back.ceilings.CGC.source_status, back.ceilings.CGC.standard_status, back.ceilings.CGC.source, back.ceilings.CGC.sources, back.ceilings.CGC.verified, back.ceilings.CGC.verification, back.ceilings.SGC, back.ceilings.SGC.maximum_grade, back.ceilings.SGC.label, back.ceilings.SGC.rank, back.ceilings.SGC.limiting_face, back.ceilings.SGC.limiting_axis, back.ceilings.SGC.distance_to_next, back.ceilings.SGC.next_grade, back.ceilings.SGC.next_label, back.ceilings.SGC.borderline, back.ceilings.SGC.unconstrained, back.ceilings.SGC.source_status, back.ceilings.SGC.standard_status, back.ceilings.SGC.source, back.ceilings.SGC.sources, back.ceilings.SGC.verified, back.ceilings.SGC.verification, combined, combined.PSA, combined.PSA.maximum_grade, combined.PSA.label, combined.PSA.rank, combined.PSA.limiting_face, combined.PSA.limiting_axis, combined.PSA.distance_to_next, combined.PSA.next_grade, combined.PSA.next_label, combined.PSA.borderline, combined.PSA.unconstrained, combined.PSA.source_status, combined.PSA.standard_status, combined.PSA.source, combined.PSA.sources, combined.PSA.verified, combined.PSA.verification, combined.BGS, combined.BGS.maximum_grade, combined.BGS.label, combined.BGS.rank, combined.BGS.limiting_face, combined.BGS.limiting_axis, combined.BGS.distance_to_next, combined.BGS.next_grade, combined.BGS.next_label, combined.BGS.borderline, combined.BGS.unconstrained, combined.BGS.source_status, combined.BGS.standard_status, combined.BGS.source, combined.BGS.sources, combined.BGS.verified, combined.BGS.verification, combined.CGC, combined.CGC.maximum_grade, combined.CGC.label, combined.CGC.rank, combined.CGC.limiting_face, combined.CGC.limiting_axis, combined.CGC.distance_to_next, combined.CGC.next_grade, combined.CGC.next_label, combined.CGC.borderline, combined.CGC.unconstrained, combined.CGC.source_status, combined.CGC.standard_status, combined.CGC.source, combined.CGC.sources, combined.CGC.verified, combined.CGC.verification, combined.SGC, combined.SGC.maximum_grade, combined.SGC.label, combined.SGC.rank, combined.SGC.limiting_face, combined.SGC.limiting_axis, combined.SGC.distance_to_next, combined.SGC.next_grade, combined.SGC.next_label, combined.SGC.borderline, combined.SGC.unconstrained, combined.SGC.source_status, combined.SGC.standard_status, combined.SGC.source, combined.SGC.sources, combined.SGC.verified, combined.SGC.verification, normalizations, normalizations[].field, normalizations[].input, normalizations[].ratio, normalizations[].note, incomplete, missing_faces, summary, disclaimer

Campos del contrato (nombres JSON exactos)

input: maximum_grade, label, rank, limiting_face, limiting_axis, distance_to_next, next_grade, next_label, borderline, unconstrained, source_status, standard_status, source, sources, verified, verification
output: maximum_grade, label, rank, limiting_face, limiting_axis, distance_to_next, next_grade, next_label, borderline, unconstrained, source_status, standard_status, source, sources, verified, verification

Medición de fotografías

POST /measure exige face: front o back. card_size predeterminado es standard (63×88 mm); small es 59×86 mm. Envía exactamente una fuente: image_url HTTPS pública en el puerto 443 o image_base64 sin prefijo data-URL. Se rechazan redes privadas, credenciales y respuestas no imagen, también en redirecciones. REST admite además un openaiFileIdRefs del runtime GPT Actions; para integraciones normales usa URL o base64. Utiliza un raster de un solo fotograma, preferiblemente JPG, PNG o WebP.

HTTP 200 puede contener status ok, needs-review o failed; solo ok tiene ceilings. Revisa code, confidence, warnings, lr y tb. uncertainty_pp y sigma_pp son puntos porcentuales; stations_major_percent contiene muestras. Si hay incertidumbre, repite la foto o revisa adjust_url. El ejemplo envía una imagen pequeña uniforme deliberadamente: NO_CARD es un fallo real, no una medición exitosa.

Campos del contrato (nombres JSON exactos)

input: face, card_size, image_url, image_base64, openaiFileIdRefs, openaiFileIdRefs[].name, openaiFileIdRefs[].id, openaiFileIdRefs[].mime_type, openaiFileIdRefs[].download_link
output: status, code, face, card_size, lr, lr.ratio, lr.major_percent, lr.minor_percent, lr.uncertainty_pp, lr.sigma_pp, lr.worst_station_px, lr.stations_major_percent, tb, tb.ratio, tb.major_percent, tb.minor_percent, tb.uncertainty_pp, tb.sigma_pp, tb.worst_station_px, tb.stations_major_percent, confidence, warnings, ceilings, ceilings.PSA, ceilings.PSA.maximum_grade, ceilings.PSA.label, ceilings.PSA.rank, ceilings.PSA.limiting_face, ceilings.PSA.limiting_axis, ceilings.PSA.distance_to_next, ceilings.PSA.next_grade, ceilings.PSA.next_label, ceilings.PSA.borderline, ceilings.PSA.unconstrained, ceilings.PSA.source_status, ceilings.PSA.standard_status, ceilings.PSA.source, ceilings.PSA.sources, ceilings.PSA.verified, ceilings.PSA.verification, ceilings.BGS, ceilings.BGS.maximum_grade, ceilings.BGS.label, ceilings.BGS.rank, ceilings.BGS.limiting_face, ceilings.BGS.limiting_axis, ceilings.BGS.distance_to_next, ceilings.BGS.next_grade, ceilings.BGS.next_label, ceilings.BGS.borderline, ceilings.BGS.unconstrained, ceilings.BGS.source_status, ceilings.BGS.standard_status, ceilings.BGS.source, ceilings.BGS.sources, ceilings.BGS.verified, ceilings.BGS.verification, ceilings.CGC, ceilings.CGC.maximum_grade, ceilings.CGC.label, ceilings.CGC.rank, ceilings.CGC.limiting_face, ceilings.CGC.limiting_axis, ceilings.CGC.distance_to_next, ceilings.CGC.next_grade, ceilings.CGC.next_label, ceilings.CGC.borderline, ceilings.CGC.unconstrained, ceilings.CGC.source_status, ceilings.CGC.standard_status, ceilings.CGC.source, ceilings.CGC.sources, ceilings.CGC.verified, ceilings.CGC.verification, ceilings.SGC, ceilings.SGC.maximum_grade, ceilings.SGC.label, ceilings.SGC.rank, ceilings.SGC.limiting_face, ceilings.SGC.limiting_axis, ceilings.SGC.distance_to_next, ceilings.SGC.next_grade, ceilings.SGC.next_label, ceilings.SGC.borderline, ceilings.SGC.unconstrained, ceilings.SGC.source_status, ceilings.SGC.standard_status, ceilings.SGC.source, ceilings.SGC.sources, ceilings.SGC.verified, ceilings.SGC.verification, adjust_url, advice, elapsed_ms, image_bytes, summary, disclaimer

POST /api/v1/measure

Solicitud

curl --fail-with-body 'https://centeringlab.com/api/v1/measure' \
  -H 'Content-Type: application/json' \
  --data '{"face":"front","card_size":"standard","image_base64":"iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAIAAABLbSncAAAACXBIWXMAAAPoAAAD6AG1e1JrAAAAD0lEQVQImWNQwAEYhpYEAGFPGAHjXOFDAAAAAElFTkSuQmCC"}'

Respuesta HTTP 200

Extracto de una respuesta HTTP real local; se omiten campos y otras bandas. Las claves JSON y los mensajes del servidor siguen en inglés.

{
  "status": "failed",
  "code": "NO_CARD",
  "face": "front",
  "card_size": "standard",
  "lr": null,
  "tb": null,
  "confidence": null,
  "warnings": [],
  "ceilings": null,
  "adjust_url": "https://centeringlab.com/"
}

Guarda tu fotografía como card.jpg. Esta respuesta usa la foto demo del sitio: la incertidumbre provoca needs-review y ceilings es null. Tu imagen tendrá sus propias medidas.

POST /api/v1/measure

Solicitud

python3 - <<'PY' > measure.json
import base64, json
from pathlib import Path
print(json.dumps({
    'face': 'front', 'card_size': 'standard',
    'image_base64': base64.b64encode(Path('card.jpg').read_bytes()).decode('ascii'),
}))
PY
curl --fail-with-body 'https://centeringlab.com/api/v1/measure' \
  -H 'Content-Type: application/json' \
  --data-binary @measure.json

Respuesta HTTP 200

Extracto de una respuesta HTTP real local; se omiten campos y otras bandas. Las claves JSON y los mensajes del servidor siguen en inglés.

{
  "status": "needs-review",
  "code": "HIGH_UNCERTAINTY",
  "face": "front",
  "card_size": "standard",
  "lr": {
    "ratio": "56/44",
    "major_percent": 55.54242922739353,
    "minor_percent": 44.45757077260647,
    "uncertainty_pp": 5.1,
    "sigma_pp": 2.3393336872317274,
    "worst_station_px": 350.163778228702,
    "stations_major_percent": [
      55.54242922739353,
      54.38358480163707,
      53.25627521942576
    ]
  },
  "tb": {
    "ratio": "52/48",
    "major_percent": 52.059885599520676,
    "minor_percent": 47.940114400479324,
    "uncertainty_pp": 6.4,
    "sigma_pp": 2.9286347555409855,
    "worst_station_px": 250.03684789689908,
    "stations_major_percent": [
      52.059885599520676,
      51.766308753028554,
      51.468031630625774
    ]
  },
  "confidence": 0.8603421926529179,
  "warnings": [
    "GET_CLOSER"
  ],
  "ceilings": null,
  "adjust_url": "https://centeringlab.com/"
}

Estándares y fuentes

GET /standards admite house y face opcionales. official, interpreted y approximate son estados de evidencia, no garantías de precisión. bands[].status corresponde a cada banda; source, sources, verified y verification explican la procedencia. TAG está excluido. SGC no publica tabla de centrado del reverso: sus bandas están vacías y el reverso solo no establece un límite SGC.

Campos del contrato (nombres JSON exactos)

input: house, face
output: standards, standards[].house, standards[].face, standards[].bands, standards[].bands[].value, standards[].bands[].label, standards[].bands[].rank, standards[].bands[].loose, standards[].bands[].tight, standards[].bands[].status, standards[].source, standards[].sources, standards[].status, standards[].verified, standards[].verification, standards[].note, rounding_rule, tag_note, summary, disclaimer

GET /api/v1/standards?house=PSA&face=front

Solicitud

curl --fail-with-body 'https://centeringlab.com/api/v1/standards?house=PSA&face=front'

Respuesta HTTP 200

Extracto de una respuesta HTTP real local; se omiten campos y otras bandas. Las claves JSON y los mensajes del servidor siguen en inglés.

{
  "standards": [
    {
      "house": "PSA",
      "face": "front",
      "bands": [
        {
          "value": 10,
          "label": "GEM MT",
          "rank": 100,
          "loose": 55,
          "status": "official"
        }
      ],
      "status": "official",
      "source": "https://www.psacard.com/gradingstandards",
      "note": "Published percentage thresholds. Only the listed centering bands are used; no missing grades are invented."
    }
  ],
  "tag_note": "TAG is excluded because its centering assessment is proprietary and is not represented by these public percentage bands."
}

Estado y esquema

GET /health devuelve estado, nombre, versión y disclaimer. GET /openapi.json devuelve OpenAPI 3.1.0. Su esquema de medición está orientado a GPT Actions (URL o referencia runtime); REST también acepta base64 según los campos anteriores.

GET /api/v1/health

Solicitud

curl --fail-with-body 'https://centeringlab.com/api/v1/health'

Respuesta HTTP 200

Extracto de una respuesta HTTP real local; se omiten campos y otras bandas. Las claves JSON y los mensajes del servidor siguen en inglés.

{
  "status": "ok",
  "name": "centering-lab",
  "version": "1.1.2",
  "disclaimer": "This is a ceiling from centering alone, not a prediction of the final grade."
}

GET /api/v1/openapi.json

Solicitud

curl --fail-with-body 'https://centeringlab.com/api/v1/openapi.json'

Respuesta HTTP 200

Extracto de una respuesta HTTP real local; se omiten campos y otras bandas. Las claves JSON y los mensajes del servidor siguen en inglés.

{
  "openapi": "3.1.0",
  "info": {
    "title": "Centering Lab API",
    "version": "1.1.2"
  },
  "servers": [
    {
      "url": "https://centeringlab.com"
    }
  ]
}

JavaScript y Python

Usa fetch de Node desde el servidor. Ambos ejemplos envían la solicitud inicial. Python utiliza la biblioteca estándar. Comprueba errores HTTP y, en mediciones, también status con HTTP 200.

JavaScript · Node

const response = await fetch('https://centeringlab.com/api/v1/grade', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({"front_lr":0.55,"front_tb":"50/50","house":"PSA"}),
});
const result = await response.json();
if (!response.ok) throw new Error(result.code + ': ' + result.message);
console.log(result);

Python · urllib

import json
from urllib.request import Request, urlopen
from urllib.error import HTTPError

request = Request(
    'https://centeringlab.com/api/v1/grade',
    data=json.dumps({"front_lr":0.55,"front_tb":"50/50","house":"PSA"}).encode('utf-8'),
    headers={'Content-Type': 'application/json'},
    method='POST',
)
try:
    with urlopen(request, timeout=20) as response:
        print(json.load(response))
except HTTPError as error:
    print(error.code, json.load(error))
    raise

Errores y reintentos

Los errores HTTP contienen status, code, message y disclaimer. La tabla cubre REST, sin errores de protocolo MCP ni estadísticas privadas. UNSUPPORTED_ENCODING es 415 para JSON comprimido y 400 para imágenes comprimidas. Los códigos del detector llegan con HTTP 200.

CódigoHTTPSignificadoQué hacer
BODY_TOO_LARGE413Tamaño excesivoReduce bytes o píxeles; el base64 también cuenta en el cuerpo JSON.
EMPTY_IMAGE400No se puede decodificar la imagenEnvía una imagen raster compatible, preferiblemente JPG, PNG o WebP; elimina el prefijo data-URL del base64.
IMAGE_DNS_FAILED400URL rechazada o inaccesibleUsa una imagen HTTPS pública en el puerto 443 sin credenciales; revisa DNS, redirecciones y respuesta.
IMAGE_DOWNLOAD_FAILED400URL rechazada o inaccesibleUsa una imagen HTTPS pública en el puerto 443 sin credenciales; revisa DNS, redirecciones y respuesta.
IMAGE_DOWNLOAD_TIMEOUT504Tiempo agotadoUsa una imagen menor o un host más rápido; también puedes usar el sitio.
IMAGE_REDIRECT_LIMIT400URL rechazada o inaccesibleUsa una imagen HTTPS pública en el puerto 443 sin credenciales; revisa DNS, redirecciones y respuesta.
IMAGE_TOO_LARGE413Tamaño excesivoReduce bytes o píxeles; el base64 también cuenta en el cuerpo JSON.
IMAGE_TOO_MANY_PIXELS413Tamaño excesivoReduce bytes o píxeles; el base64 también cuenta en el cuerpo JSON.
INTERNAL_ERROR500Fallo de servidor o workerReintenta con espera, reduce la foto o usa el sitio.
INVALID_ARGUMENTS400Solicitud o proporción no válidaCorrige los campos; envía JSON válido sin comprimir.
INVALID_BASE64400No se puede decodificar la imagenEnvía una imagen raster compatible, preferiblemente JPG, PNG o WebP; elimina el prefijo data-URL del base64.
INVALID_BODY400Solicitud o proporción no válidaCorrige los campos; envía JSON válido sin comprimir.
INVALID_HOST403Host u origen rechazadoLlama a centeringlab.com desde tu servidor, no desde el navegador de otra web.
INVALID_HTTP400Solicitud o proporción no válidaCorrige los campos; envía JSON válido sin comprimir.
INVALID_IMAGE400No se puede decodificar la imagenEnvía una imagen raster compatible, preferiblemente JPG, PNG o WebP; elimina el prefijo data-URL del base64.
INVALID_IMAGE_URL400URL rechazada o inaccesibleUsa una imagen HTTPS pública en el puerto 443 sin credenciales; revisa DNS, redirecciones y respuesta.
INVALID_INPUT400Solicitud o proporción no válidaCorrige los campos; envía JSON válido sin comprimir.
INVALID_JSON400Solicitud o proporción no válidaCorrige los campos; envía JSON válido sin comprimir.
INVALID_ORIGIN403Host u origen rechazadoLlama a centeringlab.com desde tu servidor, no desde el navegador de otra web.
INVALID_RATIO400Solicitud o proporción no válidaCorrige los campos; envía JSON válido sin comprimir.
JSON_REQUIRED415Solicitud o proporción no válidaCorrige los campos; envía JSON válido sin comprimir.
MEASURE_QUEUE_FULL503Servicio saturadoRespeta Retry-After y reintenta con esperas crecientes.
MEASURE_TIMEOUT504Tiempo agotadoUsa una imagen menor o un host más rápido; también puedes usar el sitio.
MEASURE_WORKER_FAILED500Fallo de servidor o workerReintenta con espera, reduce la foto o usa el sitio.
METHOD_NOT_ALLOWED405Ruta o método no disponibleUsa los endpoints y métodos documentados.
NOT_AN_IMAGE400URL rechazada o inaccesibleUsa una imagen HTTPS pública en el puerto 443 sin credenciales; revisa DNS, redirecciones y respuesta.
NOT_FOUND404Ruta o método no disponibleUsa los endpoints y métodos documentados.
RATE_LIMIT_CAPACITY503Servicio saturadoRespeta Retry-After y reintenta con esperas crecientes.
RATE_LIMIT_DAY429Cuota alcanzadaEspera Retry-After (segundos); la cuota diaria se reinicia a medianoche UTC.
RATE_LIMIT_MINUTE429Cuota alcanzadaEspera Retry-After (segundos); la cuota diaria se reinicia a medianoche UTC.
REQUEST_CANCELLED499Solicitud canceladaReintenta si el cliente aún necesita el resultado.
REQUEST_CAPACITY503Servicio saturadoRespeta Retry-After y reintenta con esperas crecientes.
UNSAFE_IMAGE_URL400URL rechazada o inaccesibleUsa una imagen HTTPS pública en el puerto 443 sin credenciales; revisa DNS, redirecciones y respuesta.
UNSUPPORTED_ENCODING400 / 415Solicitud o proporción no válidaCorrige los campos; envía JSON válido sin comprimir.
UNSUPPORTED_IMAGE400No se puede decodificar la imagenEnvía una imagen raster compatible, preferiblemente JPG, PNG o WebP; elimina el prefijo data-URL del base64.

Códigos del detector: HIGH_UNCERTAINTY, NEEDS_REVIEW, LOW_CONFIDENCE, NO_MEASUREMENT, NO_CARD, CARD_TOO_SMALL, CARD_CUT_OFF, NO_PRINTED_BORDER. failed y needs-review no tienen límites de grading. Fotografía la carta completa desde arriba, sin funda, sobre fondo oscuro plano, o usa el editor manual del sitio.

Límites de uso

Cuotas por IP, compartidas con los buckets correspondientes. Grade y standards comparten un minuto; health y OpenAPI otro. Ventanas de minuto fijas y días UTC; sin cuota diaria para llamadas simples/protocolo. Base64 aumenta los bytes y cuenta en el cuerpo JSON: puede impedir usar todo el límite de imagen.

LímiteValor actual
POST /measure · IP20 por minuto; 200 por día UTC
POST /grade + GET /standards · IP120 por minuto; Sin cuota diaria en el limitador
GET /health + GET /openapi.json · IP120 por minuto; Sin cuota diaria en el limitador
Cuerpo JSON12 MiB (12582912 bytes)
Bytes de imagen decodificada15 MiB (15728640 bytes)
Píxeles decodificados (todos los fotogramas)40000000
Medición, incluida la cola15 s
Descarga de imagen10 s
Mediciones activas2
Mediciones en cola8
Solicitudes HTTP en curso16

Los 429 y 503 de capacidad incluyen Retry-After en segundos. Cola llena y REQUEST_CAPACITY: 5 segundos; capacidad del limitador: 60. Respeta el encabezado con esperas crecientes. Capacidad compartida, sin reserva por cliente ni SLA de disponibilidad o latencia.

Navegador, CORS y privacidad

El navegador de otro sitio recibe 403 INVALID_ORIGIN: usa tu backend. No necesitas clave y añadirla no habilita CORS. Las fotos API llegan al servidor, se procesan solo en memoria y no se guardan en disco; el sitio las procesa localmente. No hay logs por solicitud: solo conteos agregados por endpoint/tool y código de resultado. Consulta privacidad y términos.

MCP y más información

https://centeringlab.com/mcp ofrece las mismas tres herramientas y motor a clientes AI. REST proporciona llamadas HTTP para tu aplicación. Lee AI y MCP, cómo funciona y quiénes somos.

Preguntas sobre la API

¿Es gratuita la API de centrado de cartas para desarrolladores?

Sí. Todos los endpoints son gratuitos, sin cuenta ni planes de pago. Se aplican límites; no autentica cartas ni predice la nota final.

¿Necesitan clave API las integraciones REST?

No. Envía solicitudes válidas desde tu servidor; sigue vigente la restricción de origen del navegador.

¿Cuáles son las cuotas de esta API de centrado?

Medición: 20 por minuto y 200 por día UTC por IP. Grade y standards comparten 120 por minuto por IP. Respeta Retry-After y los límites de tamaño y capacidad.

¿Guarda la API REST las fotografías enviadas?

No. Llegan al servidor y se procesan solo en memoria. Sin logs por solicitud; solo conteos agregados y códigos. El sitio procesa fotos en el dispositivo.

¿Puede otra web llamar directamente desde su navegador?

No. Recibe 403 INVALID_ORIGIN. Llama desde tu backend; no requiere clave API.

¿Qué diferencia hay entre esta API REST y MCP?

REST usa endpoints HTTP; MCP ofrece tres herramientas a clientes AI. Comparten motor y estándares. API y MCP procesan fotos en el servidor; el sitio lo hace localmente.