API grátis de centralização de cartas: PSA e fotos

O Centering Lab é uma API REST gratuita de centralização de cartas, sem chave API. Em https://centeringlab.com/api/v1, retorna medições de fotos, proporções e limites PSA, BGS, CGC ou SGC baseados apenas na centralização. Não prevê a nota final nem autentica cartas. Chame pelo seu servidor. OpenAPI.

Início rápido

Execute curl no terminal. Os dois eixos da frente foram informados, mas falta o verso: incomplete é true. PSA 10 é um limite de centralização, não previsão de nota final.

POST /api/v1/grade

Requisição

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"}'

Resposta HTTP 200

Trecho de uma resposta HTTP real local; campos e faixas adicionais foram omitidos. Chaves JSON e mensagens do servidor permanecem em 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 e campos

Proporções conhecidas

POST /grade exige pelo menos um de front_lr, front_tb, back_lr, back_tb. Normaliza para o lado maior. Aceita percentual (55), fração (0.55) e pares que somam 100 (55/45) ou 1 (0.55/0.45). Separadores: /, -, –, —, :, x e espaços; % opcional. O escalar 1 é ambíguo e recusado. L/R ou left/right só em *_lr; T/B ou top/bottom só em *_tb.

house filtra PSA, BGS, CGC ou SGC. normalizations declara conversões de frações. missing_axes, missing_faces e incomplete indicam dados ausentes que podem baixar o limite. combined combina faces; limiting_face e limiting_axis identificam a restrição. Cada limite inclui fontes e status.

Campos do contrato (nomes JSON exatos)

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 do contrato (nomes JSON exatos)

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

Medição de fotografia

POST /measure exige face: front ou back. card_size padrão é standard (63×88 mm); small é 59×86 mm. Uma única fonte: image_url HTTPS pública na porta 443 ou image_base64 puro sem prefixo data-URL. Endereços privados, credenciais e respostas não imagem são recusados, inclusive em redirecionamentos. REST também aceita um openaiFileIdRefs do runtime GPT Actions; em integrações comuns use URL ou base64. Envie raster de um quadro, preferivelmente JPG, PNG ou WebP.

HTTP 200 pode trazer status ok, needs-review ou failed; apenas ok expõe ceilings. Confira code, confidence, warnings, lr e tb. uncertainty_pp e sigma_pp usam pontos percentuais; stations_major_percent contém amostras. Resultados incertos exigem nova foto ou revisão em adjust_url. O exemplo envia uma imagem pequena uniforme de propósito: NO_CARD é uma falha real, não medição bem-sucedida.

Campos do contrato (nomes JSON exatos)

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

Requisição

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

Resposta HTTP 200

Trecho de uma resposta HTTP real local; campos e faixas adicionais foram omitidos. Chaves JSON e mensagens do servidor permanecem em 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/"
}

Salve sua foto como card.jpg. Esta resposta usa a foto de demonstração do site: a incerteza gera needs-review e ceilings é null. Sua foto terá suas próprias medidas.

POST /api/v1/measure

Requisição

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

Resposta HTTP 200

Trecho de uma resposta HTTP real local; campos e faixas adicionais foram omitidos. Chaves JSON e mensagens do servidor permanecem em 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/"
}

Padrões e fontes

GET /standards aceita house e face opcionais. official, interpreted e approximate descrevem evidência, não garantias de precisão. bands[].status vale para cada faixa; source, sources, verified e verification informam a origem. TAG está excluído. SGC não publica tabela de centralização do verso: as faixas estão vazias e o verso sozinho não estabelece limite SGC.

Campos do contrato (nomes JSON exatos)

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

Requisição

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

Resposta HTTP 200

Trecho de uma resposta HTTP real local; campos e faixas adicionais foram omitidos. Chaves JSON e mensagens do servidor permanecem em 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."
}

Status e esquema

GET /health retorna status, nome, versão e disclaimer. GET /openapi.json retorna OpenAPI 3.1.0. O esquema de medição é voltado ao GPT Actions (URL ou referência de arquivo runtime); REST também aceita base64 conforme os campos acima.

GET /api/v1/health

Requisição

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

Resposta HTTP 200

Trecho de uma resposta HTTP real local; campos e faixas adicionais foram omitidos. Chaves JSON e mensagens do servidor permanecem em 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

Requisição

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

Resposta HTTP 200

Trecho de uma resposta HTTP real local; campos e faixas adicionais foram omitidos. Chaves JSON e mensagens do servidor permanecem em inglês.

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

JavaScript e Python

Use fetch de Node no servidor. Os exemplos enviam a requisição inicial. Python usa a biblioteca padrão. Confira erros HTTP e, para medir, também status mesmo com 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

Erros e novas tentativas

Erros HTTP têm status, code, message e disclaimer. A tabela cobre REST, sem erros de protocolo MCP nem estatísticas privadas. UNSUPPORTED_ENCODING é 415 para JSON comprimido e 400 para resposta de imagem comprimida. Códigos do detector chegam com HTTP 200.

CódigoHTTPSignificadoO que fazer
BODY_TOO_LARGE413Tamanho excedidoReduza bytes ou pixels; base64 também conta no corpo JSON.
EMPTY_IMAGE400Imagem não decodificávelEnvie um raster compatível, de preferência JPG, PNG ou WebP; remova o prefixo data-URL do base64.
IMAGE_DNS_FAILED400URL recusada ou inacessívelUse imagem HTTPS pública na porta 443 sem credenciais; confira DNS, redirecionamentos e resposta.
IMAGE_DOWNLOAD_FAILED400URL recusada ou inacessívelUse imagem HTTPS pública na porta 443 sem credenciais; confira DNS, redirecionamentos e resposta.
IMAGE_DOWNLOAD_TIMEOUT504Tempo excedidoUse imagem menor ou host mais rápido; se necessário, use o site.
IMAGE_REDIRECT_LIMIT400URL recusada ou inacessívelUse imagem HTTPS pública na porta 443 sem credenciais; confira DNS, redirecionamentos e resposta.
IMAGE_TOO_LARGE413Tamanho excedidoReduza bytes ou pixels; base64 também conta no corpo JSON.
IMAGE_TOO_MANY_PIXELS413Tamanho excedidoReduza bytes ou pixels; base64 também conta no corpo JSON.
INTERNAL_ERROR500Falha do servidor ou workerAguarde, reduza a foto ou use o site.
INVALID_ARGUMENTS400Requisição ou proporção inválidaCorrija os campos; envie JSON válido sem compressão.
INVALID_BASE64400Imagem não decodificávelEnvie um raster compatível, de preferência JPG, PNG ou WebP; remova o prefixo data-URL do base64.
INVALID_BODY400Requisição ou proporção inválidaCorrija os campos; envie JSON válido sem compressão.
INVALID_HOST403Host ou origem recusadosChame centeringlab.com do seu servidor, não do navegador de outro site.
INVALID_HTTP400Requisição ou proporção inválidaCorrija os campos; envie JSON válido sem compressão.
INVALID_IMAGE400Imagem não decodificávelEnvie um raster compatível, de preferência JPG, PNG ou WebP; remova o prefixo data-URL do base64.
INVALID_IMAGE_URL400URL recusada ou inacessívelUse imagem HTTPS pública na porta 443 sem credenciais; confira DNS, redirecionamentos e resposta.
INVALID_INPUT400Requisição ou proporção inválidaCorrija os campos; envie JSON válido sem compressão.
INVALID_JSON400Requisição ou proporção inválidaCorrija os campos; envie JSON válido sem compressão.
INVALID_ORIGIN403Host ou origem recusadosChame centeringlab.com do seu servidor, não do navegador de outro site.
INVALID_RATIO400Requisição ou proporção inválidaCorrija os campos; envie JSON válido sem compressão.
JSON_REQUIRED415Requisição ou proporção inválidaCorrija os campos; envie JSON válido sem compressão.
MEASURE_QUEUE_FULL503Serviço lotadoRespeite Retry-After e aumente o intervalo entre tentativas.
MEASURE_TIMEOUT504Tempo excedidoUse imagem menor ou host mais rápido; se necessário, use o site.
MEASURE_WORKER_FAILED500Falha do servidor ou workerAguarde, reduza a foto ou use o site.
METHOD_NOT_ALLOWED405Rota ou método indisponívelUse os endpoints e métodos documentados.
NOT_AN_IMAGE400URL recusada ou inacessívelUse imagem HTTPS pública na porta 443 sem credenciais; confira DNS, redirecionamentos e resposta.
NOT_FOUND404Rota ou método indisponívelUse os endpoints e métodos documentados.
RATE_LIMIT_CAPACITY503Serviço lotadoRespeite Retry-After e aumente o intervalo entre tentativas.
RATE_LIMIT_DAY429Cota atingidaRespeite Retry-After em segundos; a cota diária reinicia à meia-noite UTC.
RATE_LIMIT_MINUTE429Cota atingidaRespeite Retry-After em segundos; a cota diária reinicia à meia-noite UTC.
REQUEST_CANCELLED499Requisição canceladaTente de novo apenas se ainda precisar do resultado.
REQUEST_CAPACITY503Serviço lotadoRespeite Retry-After e aumente o intervalo entre tentativas.
UNSAFE_IMAGE_URL400URL recusada ou inacessívelUse imagem HTTPS pública na porta 443 sem credenciais; confira DNS, redirecionamentos e resposta.
UNSUPPORTED_ENCODING400 / 415Requisição ou proporção inválidaCorrija os campos; envie JSON válido sem compressão.
UNSUPPORTED_IMAGE400Imagem não decodificávelEnvie um raster compatível, de preferência JPG, PNG ou WebP; remova o prefixo data-URL do base64.

Códigos do detector: HIGH_UNCERTAINTY, NEEDS_REVIEW, LOW_CONFIDENCE, NO_MEASUREMENT, NO_CARD, CARD_TOO_SMALL, CARD_CUT_OFF, NO_PRINTED_BORDER. failed e needs-review não têm limites de graduação. Fotografe a carta inteira de cima, sem sleeve, em fundo escuro plano, ou use o editor manual do site.

Limites de uso

Cotas por IP e compartilhadas com os grupos correspondentes. Grade e standards dividem um contador por minuto; health e OpenAPI outro. Janelas de minuto fixas e dias UTC; sem cota diária para chamadas simples/protocolo. Base64 expande os bytes e conta no corpo JSON: o limite completo da imagem pode não caber.

LimiteValor atual
POST /measure · IP20 por minuto; 200 por dia UTC
POST /grade + GET /standards · IP120 por minuto; Sem cota diária no limitador
GET /health + GET /openapi.json · IP120 por minuto; Sem cota diária no limitador
Corpo JSON12 MiB (12582912 bytes)
Bytes da imagem decodificada15 MiB (15728640 bytes)
Pixels decodificados (todos os quadros)40000000
Medição, incluindo fila15 s
Download da imagem10 s
Medições ativas2
Medições na fila8
Requisições HTTP em andamento16

429 e 503 de capacidade incluem Retry-After em segundos. Fila cheia e REQUEST_CAPACITY: 5 segundos; capacidade do limitador: 60. Respeite o cabeçalho com intervalos crescentes. Capacidade compartilhada, sem reserva por cliente e sem SLA de disponibilidade ou latência.

Navegador, CORS e privacidade

O navegador de outro site recebe 403 INVALID_ORIGIN: use seu backend. Não precisa de chave, e adicioná-la não abre CORS. Fotos API chegam ao servidor, são processadas apenas em memória e não são salvas em disco. O site processa no dispositivo. Sem logs por requisição: apenas contagens agregadas por endpoint/tool e código de resultado. Privacidade e termos.

MCP e outras informações

https://centeringlab.com/mcp oferece aos clientes AI as mesmas três ferramentas e motor. REST oferece chamadas HTTP diretas. Leia AI e MCP, como funciona e sobre o Centering Lab.

Perguntas sobre a API

A API de centralização de cartas é gratuita para desenvolvedores?

Sim. Todos os endpoints são gratuitos, sem conta nem plano pago. Limites se aplicam; não autentica cartas nem prevê a nota final.

Integrações REST precisam de chave API?

Não. Envie requisições válidas pelo seu servidor; a restrição de origem do navegador continua ativa.

Quais são as cotas desta API de centralização?

Medição: 20 por minuto e 200 por dia UTC por IP. Grade e standards dividem 120 por minuto por IP. Respeite Retry-After e limites de tamanho e capacidade.

A API REST salva fotografias enviadas?

Não. Chegam ao servidor, mas são processadas apenas em memória. Sem logs individuais; somente contagens agregadas e códigos. O site processa fotos no dispositivo.

Outro site pode chamar diretamente pelo navegador?

Não. Recebe 403 INVALID_ORIGIN. Chame pelo seu backend, sem chave API.

Qual a diferença entre esta API REST e MCP?

REST usa endpoints HTTP; MCP oferece três ferramentas aos clientes AI. Compartilham motor e padrões. API/MCP processam no servidor; o site processa localmente.