API gratuita per la centratura carte: PSA e foto

Centering Lab è un’API REST gratuita per la centratura delle carte, senza chiave API. Su https://centeringlab.com/api/v1 restituisce misure da foto, rapporti e tetti PSA, BGS, CGC o SGC basati sulla sola centratura. Non prevede il voto finale né autentica carte. Chiamala dal tuo server. OpenAPI.

Avvio rapido

Esegui questo curl nel terminale. Il fronte ha entrambi gli assi; manca il retro, quindi incomplete è true. PSA 10 è un tetto dalla centratura fornita, non una previsione del voto finale.

POST /api/v1/grade

Richiesta

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

Risposta HTTP 200

Estratto da una risposta HTTP reale locale; campi omessi e altre fasce non sono mostrati. Chiavi JSON e messaggi del server restano in inglese.

{
  "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."
}

Endpoint e campi

Rapporti di centratura noti

POST /grade richiede almeno uno fra front_lr, front_tb, back_lr, back_tb. I valori sono normalizzati al lato maggiore. Sono ammessi una percentuale come 55, una frazione come 0.55, coppie con somma 100 (55/45) o 1 (0.55/0.45). Separatori: /, -, –, —, :, x e spazi; % facoltativo. Lo scalare 1 è ambiguo e rifiutato. Suffissi L/R o left/right solo su *_lr; T/B o top/bottom solo su *_tb.

house filtra facoltativamente PSA, BGS, CGC o SGC. normalizations dichiara le conversioni delle frazioni. missing_axes, missing_faces e incomplete segnalano dati mancanti: assi o altra faccia possono abbassare il tetto. combined combina le facce fornite; limiting_face e limiting_axis identificano il vincolo. Ogni tetto include stato e fonti.

Campi del contratto (nomi JSON esatti)

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

Campi del contratto (nomi JSON esatti)

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

Misura di una fotografia

POST /measure richiede face: front o back. card_size predefinito è standard (63×88 mm); small è 59×86 mm. Fornisci esattamente una sorgente: image_url HTTPS pubblico sulla porta 443 oppure image_base64 grezzo senza prefisso data-URL. Indirizzi privati, credenziali e risposte non immagine sono rifiutati, anche nei redirect. REST accetta anche un elemento openaiFileIdRefs fornito dal runtime GPT Actions; nelle integrazioni ordinarie usa URL o base64. JPG, PNG e WebP sono scelte pratiche; invia un raster a fotogramma singolo.

Il detector può rispondere HTTP 200 con status ok, needs-review o failed. Solo ok espone ceilings. Controlla code, confidence, warnings, lr e tb; uncertainty_pp e sigma_pp sono in punti percentuali e stations_major_percent contiene i rapporti campionati. Un risultato incerto richiede una nuova foto o verifica manuale su adjust_url. L’esempio invia deliberatamente una piccola immagine uniforme: non viene trovata alcuna carta. È un fallimento reale, non una dimostrazione di misura riuscita.

Campi del contratto (nomi JSON esatti)

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

Richiesta

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

Risposta HTTP 200

Estratto da una risposta HTTP reale locale; campi omessi e altre fasce non sono mostrati. Chiavi JSON e messaggi del server restano in inglese.

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

Per una fotografia, salva l’immagine come card.jpg ed esegui quanto segue. La risposta è stata catturata con la foto demo del sito: l’incertezza attiva needs-review e ceilings è null. La tua foto restituirà le proprie misure.

POST /api/v1/measure

Richiesta

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

Risposta HTTP 200

Estratto da una risposta HTTP reale locale; campi omessi e altre fasce non sono mostrati. Chiavi JSON e messaggi del server restano in inglese.

{
  "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/"
}

Standard pubblicati e fonti

GET /standards accetta query facoltative house e face. official, interpreted e approximate descrivono le prove, non garanzie di accuratezza. bands[].status riguarda la singola fascia; source, sources, verified e verification ne dichiarano la provenienza. TAG è escluso. SGC non pubblica una tabella di centratura del retro: le fasce del retro sono vuote e il solo retro non stabilisce un tetto SGC. Conserva questa distinzione nell’interfaccia.

Campi del contratto (nomi JSON esatti)

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

Richiesta

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

Risposta HTTP 200

Estratto da una risposta HTTP reale locale; campi omessi e altre fasce non sono mostrati. Chiavi JSON e messaggi del server restano in inglese.

{
  "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."
}

Stato e schema leggibile dalle macchine

GET /health restituisce stato, nome, versione e disclaimer. GET /openapi.json restituisce OpenAPI 3.1.0. Lo schema della richiesta di misura è orientato a GPT Actions (URL o riferimento file runtime); REST accetta anche base64 grezzo come descritto qui. Per integrare byte usa l’elenco esatto dei campi REST sopra.

GET /api/v1/health

Richiesta

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

Risposta HTTP 200

Estratto da una risposta HTTP reale locale; campi omessi e altre fasce non sono mostrati. Chiavi JSON e messaggi del server restano in inglese.

{
  "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

Richiesta

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

Risposta HTTP 200

Estratto da una risposta HTTP reale locale; campi omessi e altre fasce non sono mostrati. Chiavi JSON e messaggi del server restano in inglese.

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

JavaScript e Python

Usa fetch lato server in Node, non uno script nel browser di un altro sito. Gli esempi inviano la stessa richiesta dell’avvio rapido. Python usa la libreria standard e non richiede pacchetti aggiuntivi. Verifica gli errori HTTP prima di usare i tetti; per le foto controlla anche 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

Errori e nuovi tentativi

Gli errori HTTP contengono status, code, message e disclaimer. La tabella copre trasporto e servizio REST; errori di protocollo MCP e statistiche private sono fuori da questo contratto. UNSUPPORTED_ENCODING è 415 per JSON compresso, 400 per risposte immagine compresse. I codici del detector arrivano con HTTP 200 e sono elencati a parte.

CodiceHTTPSignificatoCosa fare
BODY_TOO_LARGE413Dimensione eccessivaRiduci byte o pixel; il base64 conta anche nel limite del corpo JSON.
EMPTY_IMAGE400Immagine non decodificabileInvia un solo raster supportato, preferibilmente JPG, PNG o WebP; togli il prefisso data-URL dal base64.
IMAGE_DNS_FAILED400URL immagine rifiutato o irraggiungibileUsa un’immagine HTTPS pubblica sulla porta 443 senza credenziali; verifica DNS, redirect e risposta.
IMAGE_DOWNLOAD_FAILED400URL immagine rifiutato o irraggiungibileUsa un’immagine HTTPS pubblica sulla porta 443 senza credenziali; verifica DNS, redirect e risposta.
IMAGE_DOWNLOAD_TIMEOUT504Tempo massimo superatoProva un’immagine più piccola o un host più rapido; se serve usa il sito.
IMAGE_REDIRECT_LIMIT400URL immagine rifiutato o irraggiungibileUsa un’immagine HTTPS pubblica sulla porta 443 senza credenziali; verifica DNS, redirect e risposta.
IMAGE_TOO_LARGE413Dimensione eccessivaRiduci byte o pixel; il base64 conta anche nel limite del corpo JSON.
IMAGE_TOO_MANY_PIXELS413Dimensione eccessivaRiduci byte o pixel; il base64 conta anche nel limite del corpo JSON.
INTERNAL_ERROR500Errore del server o workerRiprova con attese crescenti, riduci la foto oppure usa il sito.
INVALID_ARGUMENTS400Richiesta o rapporto non validoCorreggi i campi indicati; invia JSON valido e non compresso.
INVALID_BASE64400Immagine non decodificabileInvia un solo raster supportato, preferibilmente JPG, PNG o WebP; togli il prefisso data-URL dal base64.
INVALID_BODY400Richiesta o rapporto non validoCorreggi i campi indicati; invia JSON valido e non compresso.
INVALID_HOST403Host o origine browser rifiutatiChiama centeringlab.com dal tuo server; evita il browser di un altro sito.
INVALID_HTTP400Richiesta o rapporto non validoCorreggi i campi indicati; invia JSON valido e non compresso.
INVALID_IMAGE400Immagine non decodificabileInvia un solo raster supportato, preferibilmente JPG, PNG o WebP; togli il prefisso data-URL dal base64.
INVALID_IMAGE_URL400URL immagine rifiutato o irraggiungibileUsa un’immagine HTTPS pubblica sulla porta 443 senza credenziali; verifica DNS, redirect e risposta.
INVALID_INPUT400Richiesta o rapporto non validoCorreggi i campi indicati; invia JSON valido e non compresso.
INVALID_JSON400Richiesta o rapporto non validoCorreggi i campi indicati; invia JSON valido e non compresso.
INVALID_ORIGIN403Host o origine browser rifiutatiChiama centeringlab.com dal tuo server; evita il browser di un altro sito.
INVALID_RATIO400Richiesta o rapporto non validoCorreggi i campi indicati; invia JSON valido e non compresso.
JSON_REQUIRED415Richiesta o rapporto non validoCorreggi i campi indicati; invia JSON valido e non compresso.
MEASURE_QUEUE_FULL503Servizio saturoAttendi Retry-After e riprova con attese crescenti.
MEASURE_TIMEOUT504Tempo massimo superatoProva un’immagine più piccola o un host più rapido; se serve usa il sito.
MEASURE_WORKER_FAILED500Errore del server o workerRiprova con attese crescenti, riduci la foto oppure usa il sito.
METHOD_NOT_ALLOWED405Rotta o metodo non disponibileUsa endpoint e metodo elencati qui.
NOT_AN_IMAGE400URL immagine rifiutato o irraggiungibileUsa un’immagine HTTPS pubblica sulla porta 443 senza credenziali; verifica DNS, redirect e risposta.
NOT_FOUND404Rotta o metodo non disponibileUsa endpoint e metodo elencati qui.
RATE_LIMIT_CAPACITY503Servizio saturoAttendi Retry-After e riprova con attese crescenti.
RATE_LIMIT_DAY429Quota di richieste raggiuntaAttendi Retry-After (secondi); la quota giornaliera si azzera a mezzanotte UTC.
RATE_LIMIT_MINUTE429Quota di richieste raggiuntaAttendi Retry-After (secondi); la quota giornaliera si azzera a mezzanotte UTC.
REQUEST_CANCELLED499Richiesta annullataRiprova solo se il client ha ancora bisogno del risultato.
REQUEST_CAPACITY503Servizio saturoAttendi Retry-After e riprova con attese crescenti.
UNSAFE_IMAGE_URL400URL immagine rifiutato o irraggiungibileUsa un’immagine HTTPS pubblica sulla porta 443 senza credenziali; verifica DNS, redirect e risposta.
UNSUPPORTED_ENCODING400 / 415Richiesta o rapporto non validoCorreggi i campi indicati; invia JSON valido e non compresso.
UNSUPPORTED_IMAGE400Immagine non decodificabileInvia un solo raster supportato, preferibilmente JPG, PNG o WebP; togli il prefisso data-URL dal base64.

Esiti del detector: HIGH_UNCERTAINTY, NEEDS_REVIEW, LOW_CONFIDENCE, NO_MEASUREMENT, NO_CARD, CARD_TOO_SMALL, CARD_CUT_OFF, NO_PRINTED_BORDER. Un esito failed o needs-review non ha tetti di grading. Rifai una foto della carta intera dall’alto, senza sleeve, su fondo scuro e piano, oppure usa l’editor manuale del sito. Non presentare un rapporto incerto come voto finale.

Limiti di utilizzo

Le quote sono per IP e condivise con i rispettivi bucket del motore/protocollo. Grade e standards condividono un bucket al minuto; health e OpenAPI un altro. Le finestre sono minuti fissi e giorni UTC. Il limiter attuale non ha quote giornaliere per richieste semplici/protocollo. Il base64 espande i byte e conta nel corpo JSON: l’intero limite dell’immagine potrebbe non entrare in una richiesta base64.

LimiteValore attuale
POST /measure · IP20 al minuto; 200 al giorno UTC
POST /grade + GET /standards · IP120 al minuto; Nessuna quota giornaliera nel limiter
GET /health + GET /openapi.json · IP120 al minuto; Nessuna quota giornaliera nel limiter
Corpo JSON12 MiB (12582912 bytes)
Byte dell’immagine decodificata15 MiB (15728640 bytes)
Pixel decodificati (tutti i fotogrammi)40000000
Misura, coda inclusa15 s
Download immagine10 s
Misure attive2
Misure in coda8
Richieste HTTP in corso16

Le risposte 429 e 503 per capacità includono Retry-After in secondi. Coda misura piena e REQUEST_CAPACITY usano 5 secondi; capacità del limiter 60 secondi. Rispetta l’header con attese crescenti. La capacità è condivisa dal servizio, non riservata al client. Nessuna disponibilità, tempo di risposta o SLA promessi.

Browser, CORS e privacy

Una chiamata dal browser di un altro sito oggi riceve 403 INVALID_ORIGIN: usa il tuo backend. Non serve una chiave API e aggiungerne una non sblocca le chiamate browser cross-origin. Non costruire un’integrazione solo client contando su CORS permissivo.

Le immagini API arrivano al server, sono elaborate solo in memoria e non sono salvate su disco. Il sito elabora invece le foto sul dispositivo. Nessun log per richiesta: solo conteggi aggregati per endpoint/tool e codice d’esito. Privacy e termini descrivono la politica esistente.

MCP e approfondimenti

I client AI possono usare https://centeringlab.com/mcp: gli stessi tre strumenti e lo stesso motore di centratura. MCP presenta gli strumenti agli assistenti; REST offre chiamate HTTP dirette per la tua applicazione. Leggi AI e configurazione MCP, come funziona la misura e chi siamo.

Domande sull’API

L’API di centratura carte è gratuita per sviluppatori?

Sì. Tutti gli endpoint API di Centering Lab sono gratuiti, senza account o piani a pagamento. Si applicano limiti d’uso; il risultato è un tetto di centratura, non autenticazione o previsione del voto finale.

Serve una chiave per le integrazioni REST?

No. Invia richieste valide dal tuo server agli endpoint documentati; resta applicato il vincolo attuale sulle origini browser.

Quali quote ha l’API di centratura carte?

La misura foto ammette 20 chiamate al minuto e 200 al giorno UTC per IP. Grade e standards condividono 120 chiamate al minuto per IP. Rispetta Retry-After e i limiti di corpo, immagine, pixel e capacità indicati sopra.

L’API REST salva le fotografie inviate?

No. Le foto arrivano al server ma sono elaborate solo in memoria. Nessun log per richiesta; solo conteggi aggregati di utilizzo e codici d’esito. Le foto del sito sono elaborate sul dispositivo.

Un altro sito può chiamare questa API direttamente dal browser?

No. Il controllo attuale restituisce 403 INVALID_ORIGIN per il browser di un altro sito. Chiama l’API dal backend, senza bisogno di chiave API.

Come si differenzia questa API REST dall’endpoint MCP?

REST usa endpoint HTTP diretti; MCP espone tre strumenti ai client AI. Entrambi usano lo stesso motore e gli stessi standard. Le immagini API e MCP arrivano al server; il sito le elabora localmente.