Measure card centering
Photo to ratios, uncertainty and editable geometry. Expected needs-review results need a person to inspect the lines.
/v1/centering/measure25 credits; verified Free trial: 5 measure and 5 confirm calls, independently. Developer excluded.growth planCache-Control: private, no-store
Objects returned
No key yet? One call gets you one: POST /v1/accounts/free with an Idempotency-Key header and your email address returns a working key in the response. Keep the original Idempotency-Key and body for response replay for up to 24 hours, under the conditions in the quickstart. The trial is 800 credits over 30 days once the address is confirmed, no card; paid plans start at 29 € a month. See the quickstart and the pricing.
Measure left/right and top/bottom ratios, uncertainty and editable geometry from a photo for collection apps, marketplaces and grading tools. needs-review with ceilings: null is expected, not an HTTP error. A failed detection can also return HTTP 200 with status failed; geometry is optional. Check status and geometry before continuing. If the corners are wrong, repeat measure with the corrected corners; then let a person inspect the lines before confirm.
Growth and higher; Developer is excluded. A verified Free trial includes 5 measure and 5 confirm calls, with independent counters. Grade and standards are available during the verified trial without separate call counters. All calls still consume credits. Requires vision:read. See the two-step centering guide.
Always display: ceiling from centering alone, not a prediction of the final grade. Results are client-confirmed when a person checks the lines; never describe them as verified or certified.
Validation uses 400 INVALID_PARAMETER: service validation includes details.service_code and details.param; upload validation may carry only details.param. Busy, failed or timed-out processing returns 503 PROVIDER_UNAVAILABLE with Retry-After: 2. A deployment without centering returns 503 FEATURE_NOT_CONFIGURED. Ineligible plans, unverified Free accounts or exhausted measure/confirm trial counters return 403 PLAN_REQUIRED; follow details.next_step when present. Errors refund credits and trial reservations.
Body fields
| Name | Type | Default | Description |
|---|---|---|---|
image required | file | string | — | Photo of one face: multipart image, JSON base64 image (image_base64 alias), or raw image/* / application/octet-stream. Entire body at most 10 MiB including encoding overhead, image at most 40 MP, single frame. Prefer JPG or PNG. |
face required | enum | — | front or back. Required; use the face actually photographed. |
card_size | enum | standard | standard or small. |
corners | array | — | Optional four {x,y} points: top-left, top-right, bottom-right, bottom-left, in engine image pixels after EXIF orientation and reduction to a longest edge of 2560 px. JSON array in JSON; serialized JSON in multipart. For raw image bodies send face, card_size and corners in the query. |
Example request
curl -s "https://api.pokemontcgapi.com/v1/centering/measure" -H "X-Api-Key: $PTCG_API_KEY" -F [email protected] -F face=front -F card_size=standard > measure.jsonExample response
200 OK · Cache-Control: private, no-store
{
"data": {
"status": "needs-review",
"code": "HIGH_UNCERTAINTY",
"face": "front",
"card_size": "standard",
"lr": {
"ratio": "57/43",
"major_percent": 57.03939509255776,
"minor_percent": 42.96060490744224,
"uncertainty_pp": 3.2,
"sigma_pp": 1.4436399943093712,
"worst_station_px": 350.01602511506974,
"stations_major_percent": [
57.03939509255776,
56.98085809751625,
56.922531909006175
]
},
"tb": {
"ratio": "52/48",
"major_percent": 51.99426399246583,
"minor_percent": 48.00573600753417,
"uncertainty_pp": 2.8,
"sigma_pp": 1.2834630215822538,
"worst_station_px": 249.97486219711377,
"stations_major_percent": [
51.99426399246583,
51.97135354918483,
51.94844520547227
]
},
"confidence": 0.9214681922123064,
"warnings": [
"GET_CLOSER"
],
"ceilings": null,
"advice": "Retake a photo of the whole card, straight from above, on a dark flat background and out of its sleeve; or mark the corners and adjust the border lines in your application. For borderless fronts, measure the back.",
"elapsed_ms": 296.80170899999996,
"normalizations": [],
"image_bytes": 1093291,
"summary": "needs-review: HIGH_UNCERTAINTY. Automatic centering is not reliable enough to give grade ceilings. Retake a photo of the whole card, straight from above, on a dark flat background and out of its sleeve; or mark the corners and adjust the border lines in your application. For borderless fronts, measure the back. This is a ceiling from centering alone, not a prediction of the final grade.",
"disclaimer": "This is a ceiling from centering alone, not a prediction of the final grade.",
"geometry": {
"geometry_version": 1,
"image": {
"width": 800,
"height": 960
},
"corners": [
{
"x": 315.87424656190825,
"y": 67.82362658406812
},
{
"x": 762.5560228622654,
"y": 266.68569857507333
},
{
"x": 511.0026611084117,
"y": 904.0836010529077
},
{
"x": 10.556528389758789,
"y": 681.2867887659505
}
],
"rectified": {
"width": 1100,
"height": 1540,
"rotations": 0,
"corner_offset": 0
},
"lines": {
"cut": {
"left": {
"intercept": 49.93539826150567,
"slope": 0.00012891325544965915
},
"right": {
"intercept": 1050.1327179395462,
"slope": -0.00016133378743782848
},
"top": {
"intercept": 70.01188071447187,
"slope": 0.00006325286113347307
},
"bottom": {
"intercept": 1470.032602717461,
"slope": 0.00004132492422915254
}
},
"print": {
"left": {
"intercept": 103.94003346691179,
"slope": 0.00022914571556586211
},
"right": {
"intercept": 1009.5389888055527,
"slope": -0.0004687376878776529
},
"top": {
"intercept": 122.46437418271441,
"slope": -0.000005743348585744223
},
"bottom": {
"intercept": 1421.6408100442256,
"slope": -0.00004307293680597076
}
}
},
"card_size": "standard",
"warnings": [
"GET_CLOSER"
],
"positioning_sigma": 0.8684521246983091
}
},
"meta": {
"elapsed_ms": 0,
"engine_version": "1.0.0"
}
}Errors
Every error body carries error.code, error.message and error.request_id. Switch on the code, never on the message.
| Status | Code | When |
|---|---|---|
| 401 | MISSING_API_KEY | No X-Api-Key header and no bearer token on a route that requires one. |
| INSUFFICIENT_SCOPE | INSUFFICIENT_SCOPE | — |
| 403 | PLAN_REQUIRED | The feature is outside the plan (movers require Growth; photo recognition has 5 calls after Free email confirmation, then requires Growth), or the account subscription is canceled or suspended. Centering requires Growth or higher; Developer is excluded. After email confirmation the trial includes 5 measure and 5 confirm calls independently; grade and standards use trial credits without separate call counters. Before email confirmation, the message starts with “Confirm your email…”. Follow details.next_step when present. |
| 400 | INVALID_PARAMETER | A query parameter has the wrong type or an unsupported value — a non-integer limit, an unknown lang, an unknown region, a sort key that is not sortable. |
| PROVIDER_UNAVAILABLE | PROVIDER_UNAVAILABLE | — |
| 503 | FEATURE_NOT_CONFIGURED | The endpoint exists but its optional backend is not configured in this deployment. |
| 415 | UNSUPPORTED_MEDIA_TYPE | The Content-Type is not one this route accepts. |
| 413 | PAYLOAD_TOO_LARGE | The request body is larger than the server accepts. |
| 429 | QUOTA_EXCEEDED | Trial credits, monthly credits or the daily cap are exhausted, and details says which: daily_cap with retry_after_s is the trial daily cap, which resets at midnight UTC; renews: true with resets_at is the monthly quota of a paid plan; renews: false is the trial allowance, which never comes back. details.next_step carries the plan, prices and owner action, or email verification when it unlocks the full trial. |
| 403 | TRIAL_EXPIRED | The trial has ended: the account is on the trial and its 30 days from signup are over, and this route costs credits. details.trial_expired_at is the date, details.trial_days the length, and details.next_step the owner handoff. Zero-cost routes — /v1/me, /v1/me/usage, the key routes and /v1/billing/checkout — still answer. |
The full taxonomy, with what to do about each code, is on the errors page.