API gratuite de centrage des cartes : PSA et photos
Centering Lab est une API REST gratuite de centrage des cartes, sans clé API. Sur https://centeringlab.com/api/v1, elle renvoie mesures photographiques, ratios et plafonds PSA, BGS, CGC ou SGC fondés sur le centrage seul. Elle ne prédit pas la note finale et n’authentifie pas les cartes. Appelle-la depuis ton serveur. OpenAPI.
Démarrage rapide
Exécute curl dans ton terminal. Les deux axes du recto sont fournis, mais le verso manque : incomplete vaut true. PSA 10 est un plafond de centrage, pas une prédiction de note finale.
POST /api/v1/grade
Requête
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"}'Réponse HTTP 200
Extrait d’une vraie réponse HTTP locale ; des champs et bandes supplémentaires sont omis. Les clés JSON et messages du serveur restent en anglais.
{
"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 et champs
Ratios connus
POST /grade exige au moins un champ parmi front_lr, front_tb, back_lr, back_tb. Les valeurs sont normalisées au côté majoritaire. Pourcentages (55), fractions (0.55) et paires de somme 100 (55/45) ou 1 (0.55/0.45) sont admis. Séparateurs : /, -, –, —, :, x ou espaces ; % facultatif. Le scalaire 1 est ambigu et refusé. L/R ou left/right uniquement pour *_lr ; T/B ou top/bottom pour *_tb.
house filtre PSA, BGS, CGC ou SGC. normalizations signale les conversions de fractions. missing_axes, missing_faces et incomplete signalent les données manquantes pouvant abaisser le plafond. combined combine les faces ; limiting_face et limiting_axis indiquent la contrainte. Chaque plafond inclut sources et statut.
Champs du contrat (noms JSON exacts)
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, disclaimerChamps du contrat (noms JSON exacts)
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, verificationMesure photographique
POST /measure exige face : front ou back. card_size vaut par défaut standard (63×88 mm) ; small correspond à 59×86 mm. Une seule source : image_url HTTPS publique sur le port 443 ou image_base64 brut sans préfixe data-URL. Adresses privées, identifiants et réponses non-image sont refusés, redirections comprises. REST accepte aussi un élément openaiFileIdRefs fourni par le runtime GPT Actions ; utilise URL ou base64 pour les intégrations ordinaires. Envoie un raster à une seule image, de préférence JPG, PNG ou WebP.
HTTP 200 peut contenir status ok, needs-review ou failed. Seul ok expose ceilings. Vérifie code, confidence, warnings, lr et tb. uncertainty_pp et sigma_pp sont en points de pourcentage ; stations_major_percent contient les échantillons. En cas d’incertitude, reprends une photo ou contrôle adjust_url. L’exemple envoie délibérément une petite image unie : NO_CARD est un véritable échec, pas une mesure réussie.
Champs du contrat (noms JSON exacts)
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, disclaimerPOST /api/v1/measure
Requête
curl --fail-with-body 'https://centeringlab.com/api/v1/measure' \
-H 'Content-Type: application/json' \
--data '{"face":"front","card_size":"standard","image_base64":"iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAIAAABLbSncAAAACXBIWXMAAAPoAAAD6AG1e1JrAAAAD0lEQVQImWNQwAEYhpYEAGFPGAHjXOFDAAAAAElFTkSuQmCC"}'Réponse HTTP 200
Extrait d’une vraie réponse HTTP locale ; des champs et bandes supplémentaires sont omis. Les clés JSON et messages du serveur restent en anglais.
{
"status": "failed",
"code": "NO_CARD",
"face": "front",
"card_size": "standard",
"lr": null,
"tb": null,
"confidence": null,
"warnings": [],
"ceilings": null,
"adjust_url": "https://centeringlab.com/"
}Enregistre ta photo comme card.jpg. Cette réponse utilise la photo de démonstration du site : l’incertitude entraîne needs-review, donc ceilings vaut null. Ta photo donnera ses propres mesures.
POST /api/v1/measure
Requête
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.jsonRéponse HTTP 200
Extrait d’une vraie réponse HTTP locale ; des champs et bandes supplémentaires sont omis. Les clés JSON et messages du serveur restent en anglais.
{
"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/"
}Normes et sources
GET /standards accepte house et face facultatifs. official, interpreted et approximate décrivent les preuves, sans garantir la précision. bands[].status concerne chaque bande ; source, sources, verified et verification donnent la provenance. TAG est exclu. SGC ne publie pas de tableau de centrage du verso : les bandes sont vides et le verso seul ne détermine pas de plafond SGC.
Champs du contrat (noms JSON exacts)
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, disclaimerGET /api/v1/standards?house=PSA&face=front
Requête
curl --fail-with-body 'https://centeringlab.com/api/v1/standards?house=PSA&face=front'Réponse HTTP 200
Extrait d’une vraie réponse HTTP locale ; des champs et bandes supplémentaires sont omis. Les clés JSON et messages du serveur restent en anglais.
{
"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."
}État et schéma
GET /health renvoie état, nom, version et disclaimer. GET /openapi.json renvoie OpenAPI 3.1.0. Le schéma de mesure vise GPT Actions (URL ou référence de fichier runtime) ; REST accepte aussi le base64 selon les champs ci-dessus.
GET /api/v1/health
Requête
curl --fail-with-body 'https://centeringlab.com/api/v1/health'Réponse HTTP 200
Extrait d’une vraie réponse HTTP locale ; des champs et bandes supplémentaires sont omis. Les clés JSON et messages du serveur restent en anglais.
{
"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
Requête
curl --fail-with-body 'https://centeringlab.com/api/v1/openapi.json'Réponse HTTP 200
Extrait d’une vraie réponse HTTP locale ; des champs et bandes supplémentaires sont omis. Les clés JSON et messages du serveur restent en anglais.
{
"openapi": "3.1.0",
"info": {
"title": "Centering Lab API",
"version": "1.1.2"
},
"servers": [
{
"url": "https://centeringlab.com"
}
]
}JavaScript et Python
Utilise fetch de Node côté serveur. Les exemples envoient la même requête initiale. Python utilise sa bibliothèque standard. Vérifie les erreurs HTTP et, pour une mesure, status même avec 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))
raiseErreurs et nouvelles tentatives
Les erreurs HTTP contiennent status, code, message et disclaimer. Le tableau concerne REST, sans erreurs de protocole MCP ni statistiques privées. UNSUPPORTED_ENCODING vaut 415 pour du JSON compressé, 400 pour une réponse image compressée. Les codes du détecteur arrivent avec HTTP 200.
| Code | HTTP | Signification | Que faire |
|---|---|---|---|
BODY_TOO_LARGE | 413 | Taille dépassée | Réduis les octets ou pixels ; le base64 compte dans le corps JSON. |
EMPTY_IMAGE | 400 | Image non décodable | Envoie une seule image raster prise en charge, de préférence JPG, PNG ou WebP ; retire le préfixe data-URL. |
IMAGE_DNS_FAILED | 400 | URL d’image refusée ou inaccessible | Utilise une image HTTPS publique sur le port 443 sans identifiants ; vérifie DNS, redirections et réponse. |
IMAGE_DOWNLOAD_FAILED | 400 | URL d’image refusée ou inaccessible | Utilise une image HTTPS publique sur le port 443 sans identifiants ; vérifie DNS, redirections et réponse. |
IMAGE_DOWNLOAD_TIMEOUT | 504 | Délai dépassé | Utilise une image plus petite ou un hébergeur plus rapide ; sinon utilise le site. |
IMAGE_REDIRECT_LIMIT | 400 | URL d’image refusée ou inaccessible | Utilise une image HTTPS publique sur le port 443 sans identifiants ; vérifie DNS, redirections et réponse. |
IMAGE_TOO_LARGE | 413 | Taille dépassée | Réduis les octets ou pixels ; le base64 compte dans le corps JSON. |
IMAGE_TOO_MANY_PIXELS | 413 | Taille dépassée | Réduis les octets ou pixels ; le base64 compte dans le corps JSON. |
INTERNAL_ERROR | 500 | Erreur serveur ou worker | Réessaie après un délai, réduis la photo ou utilise le site. |
INVALID_ARGUMENTS | 400 | Requête ou ratio invalide | Corrige les champs ; envoie du JSON valide non compressé. |
INVALID_BASE64 | 400 | Image non décodable | Envoie une seule image raster prise en charge, de préférence JPG, PNG ou WebP ; retire le préfixe data-URL. |
INVALID_BODY | 400 | Requête ou ratio invalide | Corrige les champs ; envoie du JSON valide non compressé. |
INVALID_HOST | 403 | Hôte ou origine refusé | Appelle centeringlab.com depuis ton serveur, pas depuis le navigateur d’un autre site. |
INVALID_HTTP | 400 | Requête ou ratio invalide | Corrige les champs ; envoie du JSON valide non compressé. |
INVALID_IMAGE | 400 | Image non décodable | Envoie une seule image raster prise en charge, de préférence JPG, PNG ou WebP ; retire le préfixe data-URL. |
INVALID_IMAGE_URL | 400 | URL d’image refusée ou inaccessible | Utilise une image HTTPS publique sur le port 443 sans identifiants ; vérifie DNS, redirections et réponse. |
INVALID_INPUT | 400 | Requête ou ratio invalide | Corrige les champs ; envoie du JSON valide non compressé. |
INVALID_JSON | 400 | Requête ou ratio invalide | Corrige les champs ; envoie du JSON valide non compressé. |
INVALID_ORIGIN | 403 | Hôte ou origine refusé | Appelle centeringlab.com depuis ton serveur, pas depuis le navigateur d’un autre site. |
INVALID_RATIO | 400 | Requête ou ratio invalide | Corrige les champs ; envoie du JSON valide non compressé. |
JSON_REQUIRED | 415 | Requête ou ratio invalide | Corrige les champs ; envoie du JSON valide non compressé. |
MEASURE_QUEUE_FULL | 503 | Service saturé | Respecte Retry-After et espace les nouvelles tentatives. |
MEASURE_TIMEOUT | 504 | Délai dépassé | Utilise une image plus petite ou un hébergeur plus rapide ; sinon utilise le site. |
MEASURE_WORKER_FAILED | 500 | Erreur serveur ou worker | Réessaie après un délai, réduis la photo ou utilise le site. |
METHOD_NOT_ALLOWED | 405 | Route ou méthode indisponible | Utilise les endpoints et méthodes documentés. |
NOT_AN_IMAGE | 400 | URL d’image refusée ou inaccessible | Utilise une image HTTPS publique sur le port 443 sans identifiants ; vérifie DNS, redirections et réponse. |
NOT_FOUND | 404 | Route ou méthode indisponible | Utilise les endpoints et méthodes documentés. |
RATE_LIMIT_CAPACITY | 503 | Service saturé | Respecte Retry-After et espace les nouvelles tentatives. |
RATE_LIMIT_DAY | 429 | Quota atteint | Respecte Retry-After en secondes ; le quota quotidien repart à minuit UTC. |
RATE_LIMIT_MINUTE | 429 | Quota atteint | Respecte Retry-After en secondes ; le quota quotidien repart à minuit UTC. |
REQUEST_CANCELLED | 499 | Requête annulée | Réessaie seulement si le client en a encore besoin. |
REQUEST_CAPACITY | 503 | Service saturé | Respecte Retry-After et espace les nouvelles tentatives. |
UNSAFE_IMAGE_URL | 400 | URL d’image refusée ou inaccessible | Utilise une image HTTPS publique sur le port 443 sans identifiants ; vérifie DNS, redirections et réponse. |
UNSUPPORTED_ENCODING | 400 / 415 | Requête ou ratio invalide | Corrige les champs ; envoie du JSON valide non compressé. |
UNSUPPORTED_IMAGE | 400 | Image non décodable | Envoie une seule image raster prise en charge, de préférence JPG, PNG ou WebP ; retire le préfixe data-URL. |
Codes du détecteur : HIGH_UNCERTAINTY, NEEDS_REVIEW, LOW_CONFIDENCE, NO_MEASUREMENT, NO_CARD, CARD_TOO_SMALL, CARD_CUT_OFF, NO_PRINTED_BORDER. failed et needs-review ne donnent aucun plafond. Reprends la carte entière de dessus, sans pochette, sur une surface sombre plane, ou utilise l’éditeur manuel du site.
Limites d’utilisation
Quotas par IP, partagés dans les groupes correspondants. Grade et standards partagent un compteur à la minute ; health et OpenAPI un autre. Minutes fixes et journées UTC ; pas de quota quotidien pour les appels simples/protocole. Le base64 augmente les octets et compte dans le JSON : la limite image complète peut ne pas tenir.
| Limite | Valeur actuelle |
|---|---|
POST /measure · IP | 20 par minute; 200 par jour UTC |
POST /grade + GET /standards · IP | 120 par minute; Pas de quota quotidien dans le limiteur |
GET /health + GET /openapi.json · IP | 120 par minute; Pas de quota quotidien dans le limiteur |
Corps JSON | 12 MiB (12582912 bytes) |
Octets de l’image décodée | 15 MiB (15728640 bytes) |
Pixels décodés (toutes les images) | 40000000 |
Mesure, file d’attente comprise | 15 s |
Téléchargement de l’image | 10 s |
Mesures actives | 2 |
Mesures en attente | 8 |
Requêtes HTTP en cours | 16 |
429 et 503 de capacité incluent Retry-After en secondes. File de mesure pleine et REQUEST_CAPACITY : 5 secondes ; capacité du limiteur : 60. Espace les nouvelles tentatives. Capacité partagée, sans réservation par client. Aucun SLA de disponibilité ou de délai promis.
Navigateur, CORS et confidentialité
Un autre site reçoit 403 INVALID_ORIGIN depuis son navigateur : utilise ton backend. Aucune clé nécessaire ; en ajouter une n’ouvre pas CORS. Les images API atteignent le serveur, sont traitées uniquement en mémoire et ne sont pas enregistrées sur disque. Le site traite les photos sur l’appareil. Aucun journal par requête : seulement des comptes agrégés par endpoint/tool et code de résultat. Confidentialité et conditions.
MCP et ressources
https://centeringlab.com/mcp propose aux clients AI les trois mêmes outils et le même moteur. REST offre des appels HTTP directs. Consulte AI et MCP, la méthode de mesure et à propos.
Questions sur l’API
L’API de centrage des cartes est-elle gratuite pour les développeurs ?
Oui. Tous les endpoints sont gratuits, sans compte ni formule payante. Les limites s’appliquent ; aucune authentification ni prédiction de note finale.
Les intégrations REST nécessitent-elles une clé API ?
Non. Envoie des requêtes valides depuis ton serveur ; la restriction d’origine du navigateur reste active.
Quels quotas s’appliquent à cette API de centrage ?
Mesure : 20 par minute et 200 par jour UTC par IP. Grade et standards partagent 120 par minute par IP. Respecte Retry-After et les limites de taille et capacité.
L’API REST conserve-t-elle les photographies envoyées ?
Non. Elles atteignent le serveur mais sont traitées seulement en mémoire. Aucun journal par requête, seulement des comptes agrégés et codes. Le site traite les photos sur l’appareil.
Un autre site peut-il appeler directement depuis son navigateur ?
Non. Il reçoit 403 INVALID_ORIGIN. Appelle depuis ton backend, sans clé API.
Quelle différence entre cette API REST et MCP ?
REST utilise des endpoints HTTP ; MCP offre trois outils aux clients AI. Même moteur et mêmes normes. API/MCP traitent les images sur le serveur, le site localement.
Centering Lab · Mis à jour le