Kostenlose Karten-Zentrierungs-API: PSA und Fotos

Centering Lab ist eine kostenlose REST-API für Kartenzentrierung ohne API-Key. Unter https://centeringlab.com/api/v1 liefert sie Fotomessungen, Verhältnisse und PSA-, BGS-, CGC- oder SGC-Obergrenzen allein anhand der Zentrierung. Sie sagt keine Endnote voraus und authentifiziert keine Karten. Rufe sie von deinem Server auf. OpenAPI.

Schnellstart

curl im Terminal ausführen. Beide Vorderseitenachsen liegen vor, die Rückseite fehlt: incomplete ist true. PSA 10 ist eine Zentrierungsobergrenze, keine Vorhersage der Endnote.

POST /api/v1/grade

Anfrage

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

HTTP-200-Antwort

Auszug aus einer echten lokalen HTTP-Antwort; weitere Felder und Stufen wurden weggelassen. JSON-Schlüssel und Servermeldungen bleiben Englisch.

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

Endpunkte und Felder

Bekannte Verhältnisse

POST /grade verlangt mindestens eines von front_lr, front_tb, back_lr, back_tb. Normalisierung auf den größeren Anteil. Prozentwerte (55), Brüche (0.55) und Paare mit Summe 100 (55/45) oder 1 (0.55/0.45) sind möglich. Trenner: /, -, –, —, :, x oder Leerzeichen; % optional. Der Einzelwert 1 ist mehrdeutig und wird abgewiesen. L/R oder left/right nur bei *_lr, T/B oder top/bottom nur bei *_tb.

house filtert PSA, BGS, CGC oder SGC. normalizations dokumentiert Bruchumrechnungen. missing_axes, missing_faces und incomplete zeigen fehlende Daten, die die Obergrenze senken können. combined kombiniert Seiten; limiting_face und limiting_axis zeigen die Begrenzung. Jede Obergrenze enthält Quellen und Evidenzstatus.

Vertragsfelder (exakte JSON-Namen)

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

Vertragsfelder (exakte JSON-Namen)

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

Fotomessung

POST /measure benötigt face: front oder back. Standard für card_size ist standard (63×88 mm), small bedeutet 59×86 mm. Genau eine Quelle: öffentliche HTTPS-image_url auf Port 443 oder image_base64 ohne data-URL-Präfix. Private Adressen, Zugangsdaten und Antworten ohne Bild werden auch bei Weiterleitungen abgewiesen. REST akzeptiert zusätzlich einen openaiFileIdRefs-Eintrag aus GPT Actions; normale Integrationen nutzen URL oder Base64. Ein Rasterbild mit einem Frame senden, vorzugsweise JPG, PNG oder WebP.

HTTP 200 kann status ok, needs-review oder failed enthalten; nur ok liefert ceilings. code, confidence, warnings, lr und tb prüfen. uncertainty_pp und sigma_pp sind Prozentpunkte, stations_major_percent enthält Stichproben. Unsicherheit verlangt ein neues Foto oder Kontrolle über adjust_url. Das Beispiel sendet absichtlich ein kleines einfarbiges Bild: NO_CARD ist ein echter Fehlschlag, keine erfolgreiche Messung.

Vertragsfelder (exakte JSON-Namen)

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

Anfrage

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

HTTP-200-Antwort

Auszug aus einer echten lokalen HTTP-Antwort; weitere Felder und Stufen wurden weggelassen. JSON-Schlüssel und Servermeldungen bleiben Englisch.

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

Foto als card.jpg speichern. Diese Antwort stammt vom Demofoto der Website: Unsicherheit führt zu needs-review, ceilings ist null. Dein Foto ergibt eigene Messwerte.

POST /api/v1/measure

Anfrage

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

HTTP-200-Antwort

Auszug aus einer echten lokalen HTTP-Antwort; weitere Felder und Stufen wurden weggelassen. JSON-Schlüssel und Servermeldungen bleiben Englisch.

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

Standards und Quellen

GET /standards akzeptiert optionale Parameter house und face. official, interpreted und approximate kennzeichnen die Evidenz, keine Genauigkeitsgarantie. bands[].status gilt für einzelne Stufen; source, sources, verified und verification geben die Herkunft an. TAG ist ausgeschlossen. SGC veröffentlicht keine Rückseitentabelle: die Stufen sind leer und allein die Rückseite ergibt keine SGC-Obergrenze.

Vertragsfelder (exakte JSON-Namen)

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

Anfrage

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

HTTP-200-Antwort

Auszug aus einer echten lokalen HTTP-Antwort; weitere Felder und Stufen wurden weggelassen. JSON-Schlüssel und Servermeldungen bleiben Englisch.

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

GET /health liefert Status, Namen, Version und Disclaimer. GET /openapi.json liefert OpenAPI 3.1.0. Das Messungs-Eingabeschema ist für GPT Actions ausgelegt (URL oder Laufzeit-Dateireferenz); REST akzeptiert zusätzlich Base64 gemäß den Feldern oben.

GET /api/v1/health

Anfrage

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

HTTP-200-Antwort

Auszug aus einer echten lokalen HTTP-Antwort; weitere Felder und Stufen wurden weggelassen. JSON-Schlüssel und Servermeldungen bleiben Englisch.

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

Anfrage

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

HTTP-200-Antwort

Auszug aus einer echten lokalen HTTP-Antwort; weitere Felder und Stufen wurden weggelassen. JSON-Schlüssel und Servermeldungen bleiben Englisch.

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

JavaScript und Python

Node-fetch auf dem Server verwenden. Beide Beispiele senden dieselbe Anfrage wie der Schnellstart. Python nutzt die Standardbibliothek. HTTP-Fehler prüfen, bei Messungen auch status trotz 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

Fehler und Wiederholungen

HTTP-Fehler enthalten status, code, message und disclaimer. Die Tabelle gilt für REST, ohne MCP-Protokollfehler oder private Statistikrouten. UNSUPPORTED_ENCODING ist 415 bei komprimiertem JSON und 400 bei komprimierten Bildantworten. Detektorcodes erscheinen bei HTTP 200.

CodeHTTPBedeutungAbhilfe
BODY_TOO_LARGE413Größenlimit überschrittenBytes oder Pixel reduzieren; Base64 zählt zum JSON-Limit.
EMPTY_IMAGE400Bild nicht dekodierbarEin unterstütztes Rasterbild senden, vorzugsweise JPG, PNG oder WebP; data-URL-Präfix entfernen.
IMAGE_DNS_FAILED400Bild-URL abgewiesen oder unerreichbarÖffentliches HTTPS-Bild auf Port 443 ohne Zugangsdaten verwenden; DNS, Weiterleitungen und Antwort prüfen.
IMAGE_DOWNLOAD_FAILED400Bild-URL abgewiesen oder unerreichbarÖffentliches HTTPS-Bild auf Port 443 ohne Zugangsdaten verwenden; DNS, Weiterleitungen und Antwort prüfen.
IMAGE_DOWNLOAD_TIMEOUT504Zeitlimit überschrittenKleineres Bild oder schnelleren Bildhost verwenden; bei Bedarf Website nutzen.
IMAGE_REDIRECT_LIMIT400Bild-URL abgewiesen oder unerreichbarÖffentliches HTTPS-Bild auf Port 443 ohne Zugangsdaten verwenden; DNS, Weiterleitungen und Antwort prüfen.
IMAGE_TOO_LARGE413Größenlimit überschrittenBytes oder Pixel reduzieren; Base64 zählt zum JSON-Limit.
IMAGE_TOO_MANY_PIXELS413Größenlimit überschrittenBytes oder Pixel reduzieren; Base64 zählt zum JSON-Limit.
INTERNAL_ERROR500Server- oder WorkerfehlerMit Wartezeit wiederholen, kleineres Foto senden oder Website nutzen.
INVALID_ARGUMENTS400Ungültige Anfrage oder VerhältnisFelder korrigieren; gültiges, unkomprimiertes JSON senden.
INVALID_BASE64400Bild nicht dekodierbarEin unterstütztes Rasterbild senden, vorzugsweise JPG, PNG oder WebP; data-URL-Präfix entfernen.
INVALID_BODY400Ungültige Anfrage oder VerhältnisFelder korrigieren; gültiges, unkomprimiertes JSON senden.
INVALID_HOST403Host oder Browser-Ursprung abgewiesencenteringlab.com vom eigenen Server aufrufen; kein Browseraufruf von einer anderen Website.
INVALID_HTTP400Ungültige Anfrage oder VerhältnisFelder korrigieren; gültiges, unkomprimiertes JSON senden.
INVALID_IMAGE400Bild nicht dekodierbarEin unterstütztes Rasterbild senden, vorzugsweise JPG, PNG oder WebP; data-URL-Präfix entfernen.
INVALID_IMAGE_URL400Bild-URL abgewiesen oder unerreichbarÖffentliches HTTPS-Bild auf Port 443 ohne Zugangsdaten verwenden; DNS, Weiterleitungen und Antwort prüfen.
INVALID_INPUT400Ungültige Anfrage oder VerhältnisFelder korrigieren; gültiges, unkomprimiertes JSON senden.
INVALID_JSON400Ungültige Anfrage oder VerhältnisFelder korrigieren; gültiges, unkomprimiertes JSON senden.
INVALID_ORIGIN403Host oder Browser-Ursprung abgewiesencenteringlab.com vom eigenen Server aufrufen; kein Browseraufruf von einer anderen Website.
INVALID_RATIO400Ungültige Anfrage oder VerhältnisFelder korrigieren; gültiges, unkomprimiertes JSON senden.
JSON_REQUIRED415Ungültige Anfrage oder VerhältnisFelder korrigieren; gültiges, unkomprimiertes JSON senden.
MEASURE_QUEUE_FULL503Dienst ausgelastetRetry-After beachten und mit steigenden Abständen wiederholen.
MEASURE_TIMEOUT504Zeitlimit überschrittenKleineres Bild oder schnelleren Bildhost verwenden; bei Bedarf Website nutzen.
MEASURE_WORKER_FAILED500Server- oder WorkerfehlerMit Wartezeit wiederholen, kleineres Foto senden oder Website nutzen.
METHOD_NOT_ALLOWED405Route oder Methode nicht verfügbarDie dokumentierten Endpunkte und Methoden verwenden.
NOT_AN_IMAGE400Bild-URL abgewiesen oder unerreichbarÖffentliches HTTPS-Bild auf Port 443 ohne Zugangsdaten verwenden; DNS, Weiterleitungen und Antwort prüfen.
NOT_FOUND404Route oder Methode nicht verfügbarDie dokumentierten Endpunkte und Methoden verwenden.
RATE_LIMIT_CAPACITY503Dienst ausgelastetRetry-After beachten und mit steigenden Abständen wiederholen.
RATE_LIMIT_DAY429Anfragequote erreichtRetry-After in Sekunden beachten; Tagesquote endet um Mitternacht UTC.
RATE_LIMIT_MINUTE429Anfragequote erreichtRetry-After in Sekunden beachten; Tagesquote endet um Mitternacht UTC.
REQUEST_CANCELLED499Anfrage abgebrochenNur bei weiterem Bedarf erneut versuchen.
REQUEST_CAPACITY503Dienst ausgelastetRetry-After beachten und mit steigenden Abständen wiederholen.
UNSAFE_IMAGE_URL400Bild-URL abgewiesen oder unerreichbarÖffentliches HTTPS-Bild auf Port 443 ohne Zugangsdaten verwenden; DNS, Weiterleitungen und Antwort prüfen.
UNSUPPORTED_ENCODING400 / 415Ungültige Anfrage oder VerhältnisFelder korrigieren; gültiges, unkomprimiertes JSON senden.
UNSUPPORTED_IMAGE400Bild nicht dekodierbarEin unterstütztes Rasterbild senden, vorzugsweise JPG, PNG oder WebP; data-URL-Präfix entfernen.

Detektorcodes: HIGH_UNCERTAINTY, NEEDS_REVIEW, LOW_CONFIDENCE, NO_MEASUREMENT, NO_CARD, CARD_TOO_SMALL, CARD_CUT_OFF, NO_PRINTED_BORDER. failed und needs-review haben keine Grading-Obergrenzen. Ganze Karte senkrecht von oben, ohne Hülle, auf dunkler ebener Fläche fotografieren oder den manuellen Website-Editor nutzen.

Nutzungsgrenzen

Quoten gelten pro IP und teilen sich die jeweiligen Engine-/Protokollgruppen. Grade und standards teilen einen Minuten-Zähler; health und OpenAPI einen anderen. Feste Minutenfenster und UTC-Tage; kein Tageslimit für einfache/Protokollaufrufe. Base64 vergrößert die Daten und zählt zum JSON-Limit: die volle Bildgröße passt eventuell nicht hinein.

LimitAktueller Wert
POST /measure · IP20 pro Minute; 200 pro UTC-Tag
POST /grade + GET /standards · IP120 pro Minute; Kein Tageslimit im Limiter
GET /health + GET /openapi.json · IP120 pro Minute; Kein Tageslimit im Limiter
JSON-Anfragetext12 MiB (12582912 bytes)
Dekodierte Bildbytes15 MiB (15728640 bytes)
Dekodierte Pixel (alle Frames)40000000
Messung einschließlich Warteschlange15 s
Bilddownload10 s
Aktive Messungen2
Messungen in Warteschlange8
Gleichzeitige HTTP-Anfragen16

429 und Kapazitäts-503 enthalten Retry-After in Sekunden. Volle Messwarteschlange und REQUEST_CAPACITY: 5 Sekunden; Limiterkapazität: 60. Mit steigenden Wartezeiten wiederholen. Gemeinsame Kapazität ohne Reservierung pro Client. Keine Verfügbarkeits- oder Latenz-SLA zugesagt.

Browser, CORS und Datenschutz

Browseraufrufe von anderen Websites erhalten 403 INVALID_ORIGIN: das eigene Backend nutzen. Kein API-Key nötig; ein Key öffnet CORS nicht. API-Fotos erreichen den Server und werden nur im Speicher verarbeitet, ohne Speicherung auf Datenträgern. Website-Fotos bleiben auf dem Gerät. Keine Anfrageprotokolle: nur aggregierte Zählwerte nach Endpoint/Tool und Ergebniscode. Datenschutz und Nutzungsbedingungen.

MCP und weitere Informationen

https://centeringlab.com/mcp stellt AI-Clients dieselben drei Tools und dieselbe Engine bereit. REST bietet direkte HTTP-Aufrufe. Siehe AI und MCP, Messmethode und über Centering Lab.

Fragen zur API

Ist die Kartenzentrierungs-API für Entwickler kostenlos?

Ja. Alle Endpunkte sind kostenlos, ohne Konto oder Bezahlplan. Nutzungslimits gelten; keine Authentifizierung oder Endnotenvorhersage.

Brauchen REST-Integrationen einen API-Key?

Nein. Gültige Anfragen vom Server senden; die Browser-Ursprungsprüfung bleibt bestehen.

Welche Quoten hat diese Zentrierungs-API?

Messung: 20 pro Minute und 200 pro UTC-Tag je IP. Grade und standards teilen 120 pro Minute je IP. Retry-After sowie Größen- und Kapazitätsgrenzen beachten.

Speichert die REST-API hochgeladene Fotos?

Nein. Fotos erreichen den Server, werden aber nur im Speicher verarbeitet. Keine Anfrageprotokolle, nur aggregierte Zählwerte und Ergebniscodes. Website-Fotos bleiben auf dem Gerät.

Kann eine andere Website direkt aus ihrem Browser aufrufen?

Nein. Sie erhält 403 INVALID_ORIGIN. Vom eigenen Backend aufrufen; kein API-Key nötig.

Wie unterscheidet sich diese REST-API von MCP?

REST nutzt HTTP-Endpunkte; MCP bietet AI-Clients drei Tools. Beide teilen Engine und Standards. API/MCP-Fotos erreichen den Server; die Website verarbeitet sie lokal.