AI Card Grading
This page describes API of Card Grading service for images. This service provides a card grading system via AI & Machine Learning (/v2/grade
). The system will find card on an image, identified exact location, detect corners & edges. After that it computes grades for individual edges, corners and then also for grade for whole surface and centering. This is similar how it's done by companies like PSA, Beckett, CGC & others. It will also identify if it is front or back of the cards, if it is autographed and whether it is TCG or SPORT card. We provide also lightweight endpoint for identifying condition of the card (/v2/condition
). This endpoint if great for automatic submission to online marketplaces.
The API follows the general rules of Ximilar API as described in Section First steps.
This service API has several api endpoints running at URLs:
https://api.ximilar.com/card-grader/v2/grade # for extensive grade analysis with centering, corners, edges and surface
https://api.ximilar.com/card-grader/v2/condition # for fast identification of card condition like Near Mint, Moderately Played, Damaged ...
Input quality of images matters
Because the API is working with your external images that can be lower quality (blurred, low resolution) this API is suitable as a soft-grading or pre-grading of trading card games (TCG) or sports cards. Please provide images in highest resolution possible (ideally 2000px smaller side), ideally not-upscaled or post processed by smartphone and with no sleeve or slab.
BETA / TESTING
This service is in BETA version. Be aware that some changes for this endpoint are possible in the future. We are improving AI models for grading of trading cards.
Expect large number of requests?
If you need to process large number of requests per day or month and don't need to get the results immediately then just use our asynchronous requests with webhooks.
Card Grading /v2/grade
Given a one image record, this method returns position of a card, position of corners and edges. For all of these entities it will also return a grade. The individual results are stored in corners
, edges
, card
fields and the overall (averaged) results are stored in grades
field. You can get visualization of grading via _full_url_card
, _exact_url_card
fields. The _tags
field will give you info whether it is autographed, side of the card and type of the card.
Max batch in requests 2
Maximum number of images/records in one request is 2, because multiple AI models are used for analysing grades of the images and the processing of one image can take 10-20 seconds. If you send two records/images please send Front
and Back
side of the same card (Then it will be count as one record in credit terms). If so, additional field "grades"
will be at the top level of the request - it is weighted average from both sides (70 % Front and 30 % Back). Optionally, you can specify Side
in the individual record to help us identify the front and back o f the card.
Explanation of grades
All grades are in range 1-10, where 1 is the worst and 10 is the best. The final grade of card is geometric mean of all grades (surface, corners, edges and centering).
The formula is FINAL_GRADE = geomean(geomean(CORNERS), geomean(EDGES), SURFACE, CENTERING)
If you provide Front
and Back
side of the card then the final grade is 70 % weight of front and 30 % weight of back. You can provide this weight via param front_weight
<0.0, 1.0> range. Based on this grade we provide also a condition name Poor
, Fair
, Good
, Very Good
, Excellent
, Near Mint
, Mint
, Gem Mint
.
Parameters:
records
: list of photos to analyze- must contain either of
_url
or_base64
field - see section image data for details - maximum number/batch of records is 2 (front and back of the same card)
- must contain either of
front_weight
: in range 0.0 to 1.0, default is 0.7no-image-output
: (true/false) If true, no output image will be generated which will lead to faster api response
$ curl --request POST \
--url https://api.ximilar.com/card-grader/v2/grade \
--header 'Authorization: Token __API_TOKEN__' \
--header 'content-type: application/json' \
--data '{"records": [{"_url": "__IMAGE_URL"}]}'
Returns:
- HTTP error code 2XX, if the method was OK and other HTTP error code, if the method failed.
- Body of the response is a JSON object (map) with the following fields:
status
- a JSON map with a status of the method processing. It contains these subfields:code
- a numeric code of the operation status; it follows the concept of HTTP status codes (2XX, 4XX). Specific codes are described for each type of answer (or operation) (see below).text
- a text describing the status code- error_description - in case of the processing ended with error (codes 4XX), this field contains a detailed description of the error; this might include Java stack traces.
- Example of status codes that can be returned:
"status": {"code": 200, "text": "OK"}
"status": {"code": 402, "text": "aborted by error", error_description="..."}
"status": {"code": 500, "text": "unknown error", "error_description": "..."}
statistics
- a map of various statistics about the processing. The only statistic included every time isprocessing time
- time of actual processing of the query [in seconds]
records
- JSON array with the input records, each record enriched by fields
CLICK TO SHOW JSON RESULT
{
"records": [
{
"_url": "__URL_IMAGE_PATH__",
"_status": {
"code": 200,
"text": "OK",
"request_id": "cd3f6eb0-e5c1-48d6-9fa3-0a95287a2589"
},
"_id": "631b7cf6-b25b-4949-b618-b5bed803bd6f",
"_width": 1600,
"_height": 959,
"_objects": [
{
"name": "Card",
"id": "e0f155e9-2978-4524-bae2-3d7d3377c2c4",
"bound_box": [
240,
260,
746,
901
],
"prob": 0.9428542852401733
},
{
"name": "Card",
"id": "e0f155e9-2978-4524-bae2-3d7d3377c2c4",
"bound_box": [
835,
239,
1376,
892
],
"prob": 0.7435819506645203
}
],
"corners": [
{
"name": "UPPER_LEFT",
"bound_box": [
255,
261,
316,
322
],
"point": [
277,
283
],
"grade": 6.5
},
{
"name": "UPPER_RIGHT",
"bound_box": [
652,
259,
713,
320
],
"point": [
692,
281
],
"grade": 6.5
},
{
"name": "DOWN_RIGHT",
"bound_box": [
659,
831,
720,
892
],
"point": [
699,
871
],
"grade": 7.0
},
{
"name": "DOWN_LEFT",
"bound_box": [
254,
814,
315,
875
],
"point": [
276,
854
],
"grade": 7.0
}
],
"edges": [
{
"name": "UPPER",
"polygon": [
[
319,
261
],
[
649,
259
],
[
649,
323
],
[
319,
325
]
],
"grade": 7.5
},
{
"name": "RIGHT",
"polygon": [
[
649,
323
],
[
713,
323
],
[
720,
828
],
[
656,
828
]
],
"grade": 8.0
},
{
"name": "DOWN",
"polygon": [
[
318,
811
],
[
656,
828
],
[
656,
892
],
[
318,
875
]
],
"grade": 7.5
},
{
"name": "LEFT",
"polygon": [
[
255,
325
],
[
319,
325
],
[
318,
811
],
[
254,
811
]
],
"grade": 7.0
}
],
"card": [
{
"name": "CARD",
"polygon": [
[
277,
283
],
[
692,
281
],
[
699,
871
],
[
276,
854
]
],
"bound_box": [
192,
212,
794,
949
],
"_tags": {
"Category": [
{
"prob": 0.98526,
"name": "Sport",
"id": "44b6f95c-b3a5-4f5e-84d0-e6d441f19b3a"
}
],
"Damaged": [
{
"prob": 0.97285,
"name": "OK",
"id": "8d77cbf2-96de-4e45-b518-ea09cf77483a"
}
],
"Autograph": [
{
"prob": 0.99019,
"name": "No",
"id": "45ef0f10-f4c5-4065-aaab-89384369b925"
}
],
"Side": [
{
"prob": 0.90916,
"name": "Front",
"id": "3ebc6fc4-41d4-4432-99f1-5f8c50f10413"
}
]
},
"surface": {
"grade": 7.0
},
"centering": {
"left/right": "66/34",
"top/bottom": "59/41",
"bound_box": [
0.0474,
0.0474,
0.024900000000000033,
0.03369999999999995
],
"grade": 9.0
}
}
],
"versions": {
"detection": "e21f943b",
"points": "f6cac813_3",
"corners": "7516607f_1",
"edges": "b949c1b8_1",
"surface": "7ba660de_10",
"centering": "1e59a11a_1",
"final": "7516607f_1-b949c1b8_1-7ba660de_10-1e59a11a_1-e21f943b-f6cac813_3"
},
"grades": {
"corners": 7.0,
"edges": 7.5,
"surface": 7.0,
"centering": 9.0,
"final": 7.5
},
"_full_url_card": "https://s3-eu-west-1.amazonaws.com/ximilar-tmp-images/card-grading/2d4d1fc5-5909-403e-b86a-c10be0f5b73a.webp",
"_exact_url_card": "https://s3-eu-west-1.amazonaws.com/ximilar-tmp-images/card-grading/e258fc6d-39f0-4ce7-afd5-931018d91e7c.webp"
}
],
"status": {
"code": 200,
"text": "OK",
"request_id": "cd3f6eb0-e5c1-48d6-9fa3-0a95287a2589",
"proc_id": "f50ecd84-b9f9-48c1-ba03-d3555aeb8e64"
},
"statistics": {
"processing time": 6.4246416091918945
}
}
Card Condition /v2/condition
Given a one image record, this method returns position of a card (by object/bounding box). For the largest card (object) on image it will also return a Condition
. You can specify a mode
of condition (naming) that you want to return. Currently we support several modes:
ebay
:- This is default mode.
- More about naming can be found on ebay
- Conditions for TCG:
Near Mint
,Lightly Played
,Moderately Played
,Damaged
- Conditions for Sport:
Near Mint
,Excellent
,Very Good
,Poor
tcgplayer
:- More about naming can be found on tcgplayer.com
- Conditions:
Near Mint
,Lightly Played
,Moderately Played
,Heavily Played
,Damaged
ximilar
:- Conditions:
Gem Mint
,Mint
,Near Mint
,Excellent
,Very Good
,Good
,Fair
,Poor
- Conditions:
cardmarket
:- More about naming can be found on cardmarket
- Conditions:
Mint
,Near Mint
,Excellent
,Good
,Light Played
,Played
,Poor
Max batch in requests 10
Maximum number of images/records in one request is 10.
Parameters:
records
: list of photos to analyze- must contain either of
_url
or_base64
field - see section image data for details - maximum number/batch of records is 10
- must contain either of
mode
: one oftcgplayer
,ebay
orximilar
$ curl --request POST \
--url https://api.ximilar.com/card-grader/v2/condition \
--header 'Authorization: Token __API_TOKEN__' \
--header 'content-type: application/json' \
--data '{"records": [{"_url": "__IMAGE_URL"}], "mode": "tcgplayer"}'
Returns:
- HTTP error code 2XX, if the method was OK and other HTTP error code, if the method failed.
- Body of the response is a JSON object (map) with the following fields:
status
- a JSON map with a status of the method processing. It contains these subfields:code
- a numeric code of the operation status; it follows the concept of HTTP status codes (2XX, 4XX). Specific codes are described for each type of answer (or operation) (see below).text
- a text describing the status code- error_description - in case of the processing ended with error (codes 4XX), this field contains a detailed description of the error; this might include Java stack traces.
- Example of statuses that can be returned:
"status": {"code": 200, "text": "OK"}
"status": {"code": 402, "text": "aborted by error", error_description="..."}
"status": {"code": 500, "text": "unknown error", "error_description": "..."}
statistics
- a map of various statistics about the processing. The only statistic included every time isprocessing time
- time of actual processing of the query [in seconds]
records
- JSON array with the input records, each record enriched by fields_objects
withCondition
CLICK TO SHOW JSON RESULT
{
"records": [
{
"_url": "__URL_IMG__",
"_status": {
"code": 200,
"text": "OK",
"request_id": "ef16a269-8c2f-442c-84fa-ad1920c3f664"
},
"_id": "4f878b2e-5d8f-4e2a-ae1f-09e65ffdcf70",
"_width": 1600,
"_height": 1600,
"_objects": [
{
"name": "Card",
"id": "e0f155e9-2978-4524-bae2-3d7d3377c2c4",
"bound_box": [
177,
142,
1185,
1523
],
"prob": 0.9539390802383423,
"area": 0.54376875,
"Top Category": [
{
"id": "54c8ba01-545f-409d-beb9-d8dc2fecbb26",
"name": "Card",
"prob": 1
}
],
"Category": [
{
"prob": 0.98074,
"name": "Card/Trading Card Game",
"id": "089fa0cd-a399-4c6c-8420-d52a55c0ff02"
},
{
"prob": 0.01926,
"name": "Card/Sport Card",
"id": "44b6f95c-b3a5-4f5e-84d0-e6d441f19b3a"
}
],
"Condition": [
{
"value": 6.46723,
"name": "Card Condition TCG",
"id": "21cd7fde-5393-427d-bf9b-cc00920af3e7",
"label": "Lightly Played",
"mode": "tcgplayer"
}
]
}
]
}
],
"statistics": {
"processing time": 1.1068902015686035
},
"status": {
"code": 200,
"text": "OK",
"request_id": "ef16a269-8c2f-442c-84fa-ad1920c3f664",
"proc_id": "5bb21221-6008-481f-a68c-11330e479213"
}
}
Card Grading /v2/centering
Given a one image record, this method returns position of a card and centering analysis. You can get visualization of grading via _clean_url_card
, _exact_url_card
fields. The _tags
field will give you whether it is autographed, side of the card and type of the card. The centering
information is stored inside a card
field of the record.
Max batch in requests is 2
Maximum number of images/records in one request is 2.
Parameters:
records
: list of photos to analyze- must contain either of
_url
or_base64
field - see section image data for details - maximum number/batch of records is 2 (front and back of the same card)
- must contain either of
transparent
: (true/false) If your image contains card that has already analysed background / foreground (card) then set this to true. This will make processing much more precise and faster.skip-location
: (true/false) If your image contains exactly aligned card, used this option. This will skip the location which can in this case go wrong.no-image-output
: (true/false) If true, no output image will be generated which will lead to faster api response
$ curl --request POST \
--url https://api.ximilar.com/card-grader/v2/centering \
--header 'Authorization: Token __API_TOKEN__' \
--header 'content-type: application/json' \
--data '{"records": [{"_url": "__IMAGE_URL"}]}'
Returns:
- HTTP error code 2XX, if the method was OK and other HTTP error code, if the method failed.
- Body of the response is a JSON object (map) with the following fields:
status
- a JSON map with a status of the method processing. It contains these subfields:code
- a numeric code of the operation status; it follows the concept of HTTP status codes (2XX, 4XX). Specific codes are described for each type of answer (or operation) (see below).text
- a text describing the status code- error_description - in case of the processing ended with error (codes 4XX), this field contains a detailed description of the error; this might include Java stack traces.
statistics
- a map of various statistics about the processing. The only statistic included every time isprocessing time
- time of actual processing of the query [in seconds]
records
- JSON array with the input records, each record enriched by fields- record will contain
card
withcentering
inside. The centring contains several fieldsleft/right
,top/bottom
,bound_box
,pixels
,offsets
andgrade
.left/right
: ratio between left and right borders (for example "55/45")top/bottom
: ratio between top and bottom borders (for example "55/45")bound_box
: bounding box of borders in pixels ([xmin, ymin, xmax, ymax])pixels
: pixel offsets from each border (left, top, right, bottom) in pixelsoffsets
: relative offsets from each border in percentage (left, top, right, bottom)grade
: final grade
- record will contain
CLICK TO SHOW JSON RESULT
{
"records": [
{
"_url": "__URL_CARD__",
"_status": {
"code": 200,
"text": "OK",
"request_id": "cb697010-b0f7-4a47-86a9-f6bb5c3df5f4"
},
"_id": "1ea2e7de-5020-40ff-970e-d3286aa09e3c",
"_width": 4891,
"_height": 4891,
"_objects": [
{
"name": "Card",
"id": "e0f155e9-2978-4524-bae2-3d7d3377c2c4",
"bound_box": [
1033,
1097,
3722,
4096
],
"prob": 1.0
}
],
"_points": [
[
1085,
122
],
[
2567,
813
],
[
1604,
2877
],
[
122,
2186
]
],
"card": [
{
"name": "CARD",
"polygon": [
[
2118,
1219
],
[
3600,
1910
],
[
2637,
3974
],
[
1155,
3283
]
],
"bound_box": [
1033,
1097,
3722,
4096
],
"_tags": {
"Category": [
{
"prob": 0.98706,
"name": "Card/Trading Card Game",
"id": "089fa0cd-a399-4c6c-8420-d52a55c0ff02"
}
],
"Autograph": [
{
"prob": 0.98546,
"name": "Yes",
"id": "00058dcb-592a-4504-9794-2097f24100f7"
}
],
"Side": [
{
"prob": 0.98038,
"name": "Back",
"id": "3442e597-6170-4be7-932f-b2505ddd2ea6"
}
]
},
"centering": {
"left/right": "46/54",
"top/bottom": "49/51",
"bound_box": [
57,
71,
1567,
2201
],
"pixels": [
57,
71,
68,
76
],
"offsets": [
0.0351,
0.0312,
0.04200000000000004,
0.03369999999999995
],
"grade": 9.5
}
}
],
"versions": {
"detection": "e21f943b",
"points": "transparent",
"centering": "1e59a11a_1"
},
"grades": {
"centering": 9.5
},
"_exact_url_card": "__URL_CARD__",
"_clean_url_card": "__URL_CARD__"
}
],
"transparent": true,
"statistics": {
"processing time": 10.295928001403809
},
"status": {
"code": 200,
"text": "OK",
"request_id": "cb697010-b0f7-4a47-86a9-f6bb5c3df5f4",
"proc_id": "d261c5d8-a028-4e61-86cf-21337a360075"
}
}
Card Grading /v2/localize
Given one image record, the endpoint returns the corners, edges and overall position of the card in the image.
Parameters:
records
: list of images to analyze- must contain either
_url
or_base64
field - see section image data for details
- must contain either
transparent
: (true/false)- if your image contains a card whose background/foreground has already been analysed. This will make processing much more precise and faster.
$ curl --request POST \
--url https://api.ximilar.com/card-grader/v2/localize \
--header 'Authorization: Token __API_TOKEN__' \
--header 'content-type: application/json' \
--data '{"records": [{"_url": "__IMAGE_URL"}]}'
Returns:
- HTTP error code 2XX if the processing of the endpoint was OK, and other HTTP error codes if the processing of the endpoint failed.
- Body of the response is a JSON object with the following fields:
status
- status of processing of the endpoint. Contains the following subfields:code
- numeric code of the status of processing; it follows the concept of HTTP status codes (2XX, 4XX)text
- text describing the status
statistics
- various statistics about processing of the endpoint. The only statistic included every time is:processing time
- time of the actual processing of the endpoint [in seconds]
records
- array with the input records, each record with fieldscorners
,edges
andcard
:corners
- bounding boxes of the card's corners and their final point coordinatesedges
- polygons that contains the edges of the cardcard
- polygons that contains the whole card and the card's final bounding box
CLICK TO SHOW JSON RESULT
{
"records": [
{
"_url": "__URL_CARD__",
"_status": {
"code": 200,
"text": "OK",
"request_id": "3a7fa006-c72b-4bb7-9e44-213c291b8cb3"
},
"_id": "c488d900-44d9-4f5a-9d74-9c841658c029",
"_width": 801,
"_height": 1116,
"_objects": [
{
"name": "Card",
"id": "e0f155e9-2978-4524-bae2-3d7d3377c2c4",
"bound_box": [
189,
3,
784,
683
],
"prob": 0.7105523943901062
}
],
"_points": [
[
178.8824,
152.2878
],
[
776.7224,
27.997200000000003
],
[
764.5748,
686.6383999999999
],
[
213.6716,
662.1055
]
],
"corners": [
{
"name": "UPPER_LEFT",
"bound_box": [
155,
129,
219,
193
],
"point": [
178,
152
]
},
{
"name": "UPPER_RIGHT",
"bound_box": [
734,
4,
798,
68
],
"point": [
776,
27
]
},
{
"name": "DOWN_RIGHT",
"bound_box": [
722,
644,
786,
708
],
"point": [
764,
686
]
},
{
"name": "DOWN_LEFT",
"bound_box": [
190,
620,
254,
684
],
"point": [
213,
662
]
}
],
"edges": [
{
"name": "UPPER",
"polygon": [
[
222,
129
],
[
731,
4
],
[
731,
71
],
[
222,
196
]
]
},
{
"name": "RIGHT",
"polygon": [
[
731,
71
],
[
798,
71
],
[
786,
641
],
[
719,
641
]
]
},
{
"name": "DOWN",
"polygon": [
[
257,
617
],
[
719,
641
],
[
719,
708
],
[
257,
684
]
]
},
{
"name": "LEFT",
"polygon": [
[
155,
196
],
[
222,
196
],
[
257,
617
],
[
190,
617
]
]
}
],
"card": [
{
"name": "CARD",
"polygon": [
[
178,
152
],
[
776,
27
],
[
764,
686
],
[
213,
662
]
],
"bound_box": [
164,
0,
800,
707
]
}
],
"versions": {
"detection": "e21f943b",
"points": "f6cac813_4"
}
}
],
"statistics": {
"processing time": 3.519575595855713
},
"status": {
"code": 200,
"text": "OK",
"request_id": "3a7fa006-c72b-4bb7-9e44-213c291b8cb3",
"proc_id": "b441f553-4156-4e80-8220-6da1c0cabd26"
}
}
Card Grading /v2/crop_level
Given one image record of a card scan (the background must be white), return an url of the card with the background cropped out.
Parameters:
records
: list of images to analyze- must contain either
_url
or_base64
field - see section image data for details
- must contain either
use_ai
: (true/false)- indicates whether to use AI models to perform the cropping. The AI method is currently under development and using it may provide inferior results.
- default: false
$ curl --request POST \
--url https://api.ximilar.com/card-grader/v2/crop_level \
--header 'Authorization: Token __API_TOKEN__' \
--header 'content-type: application/json' \
--data '{"records": [{"_url": "__IMAGE_URL"}]}'
Returns:
- HTTP error code 2XX if the processing of the endpoint was OK, and other HTTP error codes if the processing of the endpoint failed.
- Body of the response is a JSON object with the following fields:
status
- status of processing of the endpoint. Contains the following subfields:code
- numeric code of the status of processing; it follows the concept of HTTP status codes (2XX, 4XX)text
- text describing the status
statistics
- various statistics about processing of the endpoint. The only statistic included every time is:processing time
- time of actual processing of the endpoint [in seconds]
records
- array with the input records, each record with field_img_url
that contains the url of the cropped image
CLICK TO SHOW JSON RESULT
{
"records": [
{
"_url": __URL_CARD__,
"_status": {
"code": 200,
"text": "OK",
"request_id": "372f0556-7145-495c-8feb-d91ea9e13d44"
},
"_id": "36d86202-8398-4954-bb57-24f8ea565013",
"_width": 749,
"_height": 1053,
"_img_url": __URL_CROPPED_CARD__
}
],
"statistics": {
"processing time": 0.8681676387786865
},
"status": {
"code": 200,
"text": "OK",
"request_id": "372f0556-7145-495c-8feb-d91ea9e13d44",
"proc_id": "8a5d92e1-78c5-4363-9aa0-7557a8172565"
}
}