Cards Cloud
To access the Ximilar API, first register at Ximilar App to get your API token. This API follows the general rules of Ximilar API as described in Section First steps.
Cards Cloud stores the cards you identify as sessions in your Ximilar account. A session is one batch of work — a live stream, a binder scanned in one sitting, a folder of photos — and holds one identification row per card: the matched card, its alternatives, prices, links, an image and, optionally, its grading.
Sessions are shared between Ximilar's apps. The open-source Cardstream
live-stream app and Cardloader desktop app both write to Cards Cloud, and each
session records which app made it in its client field. You can use the same API to keep your own app's results
there.
Cards Cloud does not identify cards itself: identify them first with Collectibles Recognition (and grade them with Card Grading if you like), then upload the results.
Endpoints
GET https://api.ximilar.com/cards-cloud/v2/ping/ → Check access
POST https://api.ximilar.com/cards-cloud/v2/session/ → Create a session
GET https://api.ximilar.com/cards-cloud/v2/session/ → List sessions
GET https://api.ximilar.com/cards-cloud/v2/session/__SESSION_ID__/ → Get a session
PATCH https://api.ximilar.com/cards-cloud/v2/session/__SESSION_ID__/ → Rename a session
DELETE https://api.ximilar.com/cards-cloud/v2/session/__SESSION_ID__/ → Delete a session
POST https://api.ximilar.com/cards-cloud/v2/session/__SESSION_ID__/close/ → Close a session
POST https://api.ximilar.com/cards-cloud/v2/session/__SESSION_ID__/reopen/ → Reopen a session
GET https://api.ximilar.com/cards-cloud/v2/session/__SESSION_ID__/summary/ → Session totals
POST https://api.ximilar.com/cards-cloud/v2/session/__SESSION_ID__/identifications/ → Upload identified cards
POST https://api.ximilar.com/cards-cloud/v2/session/__SESSION_ID__/images/ → Upload a card's image
GET https://api.ximilar.com/cards-cloud/v2/identification/ → List a session's cards
PATCH https://api.ximilar.com/cards-cloud/v2/identification/__ID__/ → Correct a card's match
Every endpoint takes an optional workspace query parameter (a workspace ID); without it, the default workspace of
your API token is used.
How a session is filled
- Create a session. It starts
live. - Upload identified cards in batches as you go. Each row carries an
event_idyou choose — a string that is unique within the session, for example a hash of the image file name. Cards Cloud keys rows by it, so uploading the sameevent_idagain updates that row (for example when its grading arrives later) instead of adding a duplicate. - Upload each card's image by its
event_id. The answer is the stored row, with itsid. - Close the session when the batch is finished. A closed session can be reopened to add more cards.
Re-uploading a row that Cards Cloud already has does not change its card fields. When a user picks a different
match for a card, send the correction with PATCH identification/__ID__/ instead.
To continue a session later, list its rows and skip the cards whose event_id is already there.
The session object
- Name
id- Type
- string
- Description
Unique identifier of the session.
- Name
name- Type
- string
- Max
- Maximum:255
- Description
Name of the session.
- Name
description- Type
- string
- Description
Free-text description.
- Name
game- Type
- string
- Description
What the session holds:
TCG,Sports,Comics,Slabs, or empty for a mix.
- Name
platform- Type
- string
- Description
Where the cards were identified, for example a streaming platform;
otherfor anything else.
- Name
status- Type
- string
- Description
livewhile cards are being added,closedwhen finished.
- Name
started- Type
- string
- Description
When the session started (ISO 8601).
- Name
ended- Type
- string
- Description
When it was closed (ISO 8601), or
null.
- Name
identification_count- Type
- integer
- Description
Number of cards in the session.
- Name
total_price- Type
- number
- Description
Sum of the cards' prices (USD).
- Name
client- Type
- dict
- Description
The app that made the session:
name,versionandos. Cardstream and Cardloader ("name": "cardloader") set it.
- Name
meta_data- Type
- dict
- Description
Any JSON object the client wants to keep with the session, such as its settings.
The identification object
One row per card. All text fields are optional; values longer than the limit are rejected, and one invalid row rejects the whole batch.
- Name
id- Type
- string
- Description
Unique identifier of the row (assigned by Cards Cloud).
- Name
event_id- Type
- string
- Description
Your key for the card, unique within the session.
- Name
seen- Type
- string
- Description
When the card was identified (ISO 8601).
- Name
calls- Type
- integer
- Description
How many API calls identifying the card took.
- Name
id_type- Type
- string
- Description
The kind of identification:
tcg,sport,comicsorslab.
- Name
name- Type
- string
- Max
- Maximum:255
- Description
Card name.
- Name
full_name- Type
- string
- Max
- Maximum:500
- Description
Full card name, as Collectibles Recognition returns it.
- Name
set_name- Type
- string
- Max
- Maximum:255
- Description
Set (for comics: publisher).
- Name
set_code- Type
- string
- Max
- Maximum:50
- Description
Set code.
- Name
card_number- Type
- string
- Max
- Maximum:50
- Description
Card number.
- Name
series- Type
- string
- Max
- Maximum:255
- Description
Series.
- Name
year- Type
- string
- Max
- Maximum:20
- Description
Year.
- Name
subcategory- Type
- string
- Max
- Maximum:100
- Description
Game or sport, as the taxonomy spells it (e.g.
Pokemon,Baseball).
- Name
distance- Type
- number
- Description
Distance of the best match (lower is closer).
- Name
confidence- Type
- string
- Description
high,mediumorlow.
- Name
links- Type
- dict
- Description
Marketplace links of the best match,
{site: url}(each URL up to 2048 characters).
- Name
alternatives- Type
- array
- Max
- Maximum:10
- Description
Other candidate matches, as Collectibles Recognition returns them.
- Name
price_stats- Type
- array
- Max
- Maximum:10
- Description
Price statistics of the best match, as Collectibles Recognition returns them.
- Name
price- Type
- number
- Description
Price of the card (USD).
- Name
grading- Type
- dict
- Max
- Maximum:64 KiB
- Description
The card's grading:
endpoint(the Card Grading endpoint used),grades(withfinal,condition, sub-grades) and optionally the gradingrecords.gradeis derived fromgrades.final.
- Name
grade- Type
- number
- Description
Final grade (read-only, from
grading).
- Name
image- Type
- string
- Description
URL of the card's image, once uploaded (read-only).
- Name
thumbnail- Type
- string
- Description
URL of a small version of the image (read-only).
- Name
sold- Type
- boolean
- Description
Whether the card was sold.
- Name
note- Type
- string
- Description
Free-text note.
Check access
Checks that your API token can use Cards Cloud.
Returns
HTTP error code 2XX if Cards Cloud is available to your account, and another HTTP error code if not.
Request
curl -H 'Authorization: Token __API_TOKEN__' \
https://api.ximilar.com/cards-cloud/v2/ping/
Create a session
Creates a new live session.
Attributes
- Name
name- Type
- string
- Max
- Maximum:255
- Description
Name of the session.
- Name
description- Type
- string
- Description
Free-text description.
- Name
game- Type
- string
- Description
TCG,Sports,Comics,Slabs, or empty for a mix.
- Name
platform- Type
- string
- Description
Where the cards are identified;
otherfor anything else.
- Name
client- Type
- dict
- Description
The app creating the session:
name,version,os.
- Name
meta_data- Type
- dict
- Description
Any JSON object to keep with the session.
Returns
The new session, with its id. 402 means the account doesn't have enough credits
to create a session; 403 means Cards Cloud isn't enabled for the account.
Request
curl --location 'https://api.ximilar.com/cards-cloud/v2/session/' \
--header 'Content-Type: application/json' \
--header 'Authorization: Token __API_TOKEN__' \
--data '{
"name": "Binder #3 — vintage Pokémon",
"description": "Scanned with my app",
"game": "TCG",
"platform": "other",
"client": {"name": "my-app", "version": "1.0.0", "os": "macos"},
"meta_data": {"source": "folder"}
}'
Response
{
"id": "8f3c2a10-5b7e-4c1d-9a2f-0e6d4b1c7a35",
"name": "Binder #3 — vintage Pokémon",
"description": "Scanned with my app",
"game": "TCG",
"platform": "other",
"status": "live",
"client": {"name": "my-app", "version": "1.0.0", "os": "macos"},
"meta_data": {"source": "folder"}
}
List sessions
Lists your sessions, a page at a time.
Optional attributes
- Name
page- Type
- integer
- Default
- Default:1
- Description
Page number.
- Name
search- Type
- string
- Description
Only sessions matching this text.
Returns
count (number of sessions), next (URL of the next page, or null) and results, a list of
sessions.
Request
curl -H 'Authorization: Token __API_TOKEN__' \
'https://api.ximilar.com/cards-cloud/v2/session/?page=1&search=binder'
Response
{
"count": 1,
"next": null,
"results": [
{
"id": "8f3c2a10-5b7e-4c1d-9a2f-0e6d4b1c7a35",
"name": "Binder #3 — vintage Pokémon",
"description": "Scanned with my app",
"game": "TCG",
"platform": "other",
"status": "closed",
"started": "2026-10-14T16:20:00Z",
"ended": "2026-10-14T16:41:12Z",
"identification_count": 18,
"total_price": 412.5,
"client": {"name": "my-app", "version": "1.0.0", "os": "macos"},
"meta_data": {"source": "folder"}
}
]
}
Get a session
Returns one session. Use its status to tell whether you can keep adding cards
(live) or need to reopen it first.
Returns
The session, or 404 if it doesn't exist (for example, it was deleted).
Request
curl -H 'Authorization: Token __API_TOKEN__' \
https://api.ximilar.com/cards-cloud/v2/session/__SESSION_ID__/
Rename a session
Changes a session's name.
Attributes
- Name
name- Type
- string
- Max
- Maximum:255
- Description
The new name.
Returns
The updated session.
Request
curl -XPATCH 'https://api.ximilar.com/cards-cloud/v2/session/__SESSION_ID__/' \
--header 'Content-Type: application/json' \
--header 'Authorization: Token __API_TOKEN__' \
--data '{"name": "Binder #3 — done"}'
Delete a session
Deletes a session with all its cards and images.
Deleting a session is permanent and cannot be undone.
Returns
HTTP error code 2XX, if the method was OK, and other HTTP error code, if the method failed.
Request
curl -XDELETE -H 'Authorization: Token __API_TOKEN__' \
https://api.ximilar.com/cards-cloud/v2/session/__SESSION_ID__/
Close a session
Marks a session as finished: its status becomes closed. Corrections with
PATCH identification/__ID__/ still work on a closed session.
Returns
HTTP error code 2XX, if the method was OK, and other HTTP error code, if the method failed.
Request
curl -XPOST -H 'Authorization: Token __API_TOKEN__' \
https://api.ximilar.com/cards-cloud/v2/session/__SESSION_ID__/close/
Reopen a session
Makes a closed session live again, so you can upload more cards to it.
Returns
HTTP error code 2XX, if the method was OK, and other HTTP error code, if the method failed.
Request
curl -XPOST -H 'Authorization: Token __API_TOKEN__' \
https://api.ximilar.com/cards-cloud/v2/session/__SESSION_ID__/reopen/
Session summary
Totals of a session's cards.
Returns
- Name
identifications- Type
- integer
- Description
Number of cards.
- Name
distinct_cards- Type
- integer
- Description
Number of different cards.
- Name
total_price- Type
- number
- Description
Sum of the cards' prices (USD).
- Name
paid_calls- Type
- integer
- Description
API calls spent identifying the cards.
- Name
average_grade- Type
- number
- Description
Average final grade of the graded cards, or
null.
Request
curl -H 'Authorization: Token __API_TOKEN__' \
https://api.ximilar.com/cards-cloud/v2/session/__SESSION_ID__/summary/
Response
{
"identifications": 18,
"distinct_cards": 15,
"total_price": 412.5,
"paid_calls": 36,
"average_grade": 8.5
}
Upload identified cards
Adds cards to a live session, or updates the ones whose event_id it already has. Send the rows in
batches.
A row whose event_id is already in the session keeps its card fields (name, set, number, …); use this to
add data that arrives later, such as grading. To change the matched card, use
PATCH identification/__ID__/.
Attributes
- Name
identifications- Type
- array
- Description
Rows in the shape of the identification object, each with an
event_id.id,grade,imageandthumbnailare set by Cards Cloud.
Returns
How many rows were received, created, updated, and were duplicates. If any row is invalid (for example a field over its length limit), the whole batch is rejected.
Request
curl --location 'https://api.ximilar.com/cards-cloud/v2/session/__SESSION_ID__/identifications/' \
--header 'Content-Type: application/json' \
--header 'Authorization: Token __API_TOKEN__' \
--data '{
"identifications": [
{
"event_id": "3f9a1c0e7b2d4e58",
"seen": "2026-10-14T16:21:03.120Z",
"calls": 1,
"id_type": "tcg",
"name": "Charizard ex",
"full_name": "Charizard ex Obsidian Flames (OBF) #125",
"set_name": "Obsidian Flames",
"set_code": "OBF",
"card_number": "125",
"series": "Scarlet & Violet",
"year": "2023",
"subcategory": "Pokemon",
"distance": 0.21,
"confidence": "high",
"links": {"tcgplayer.com": "https://example.com/product/125"},
"alternatives": [
{"name": "Charizard ex", "full_name": "Charizard ex Obsidian Flames (OBF) #223", "set": "Obsidian Flames",
"set_code": "OBF", "card_number": "223", "year": 2023}
],
"price_stats": [{"stats_type": "ungraded", "value": {"median": 12.5}}],
"grading": {
"endpoint": "grade",
"grades": {"corners": 8.0, "edges": 8.0, "surface": 6.0, "centering": 9.0, "final": 7.5,
"condition": "Near Mint"}
}
}
]
}'
Response
{
"received": 1,
"created": 1,
"updated": 0,
"duplicates": 0
}
Upload a card's image
Attaches an image to a card already uploaded to the session. Cardloader sends a JPEG of about 1024 px on the long edge.
Attributes
- Name
event_id- Type
- string
- Description
The
event_idof the card.
- Name
image- Type
- string
- Description
The image, Base64-encoded.
Returns
The identification row, with its id and image URL. 404 if the session
has no card with this event_id.
Request
curl --location 'https://api.ximilar.com/cards-cloud/v2/session/__SESSION_ID__/images/' \
--header 'Content-Type: application/json' \
--header 'Authorization: Token __API_TOKEN__' \
--data '{
"event_id": "3f9a1c0e7b2d4e58",
"image": "__BASE64_JPEG__"
}'
List a session's cards
Lists the cards of a session, a page at a time.
Required attributes
- Name
session- Type
- string
- Description
ID of the session.
Optional attributes
- Name
event_id- Type
- string
- Description
Only the card with this
event_id.
- Name
page- Type
- integer
- Default
- Default:1
- Description
Page number.
- Name
page_size- Type
- integer
- Max
- Maximum:500
- Description
Cards per page.
Returns
count, next (URL of the next page, or null) and results, a list of
identifications.
Request
curl -H 'Authorization: Token __API_TOKEN__' \
'https://api.ximilar.com/cards-cloud/v2/identification/?session=__SESSION_ID__&page=1&page_size=100'
Response
{
"count": 18,
"next": "https://api.ximilar.com/cards-cloud/v2/identification/?page=2&page_size=100&session=__SESSION_ID__",
"results": [
{
"id": "c41d7e22-90b3-4f6a-8d15-2b7a9e0f3c88",
"event_id": "3f9a1c0e7b2d4e58",
"seen": "2026-10-14T16:21:03.120Z",
"id_type": "tcg",
"name": "Charizard ex",
"full_name": "Charizard ex Obsidian Flames (OBF) #125",
"set_name": "Obsidian Flames",
"set_code": "OBF",
"card_number": "125",
"series": "Scarlet & Violet",
"year": "2023",
"subcategory": "Pokemon",
"distance": 0.21,
"confidence": "high",
"links": {"tcgplayer.com": "https://example.com/product/125"},
"alternatives": [],
"price_stats": [{"stats_type": "ungraded", "value": {"median": 12.5}}],
"price": 12.5,
"image": "https://example.com/cards-cloud/c41d7e22.jpg",
"thumbnail": "https://example.com/cards-cloud/c41d7e22_thumb.jpg",
"grading": {"endpoint": "grade", "grades": {"final": 7.5, "condition": "Near Mint"}},
"grade": 7.5,
"sold": false,
"note": ""
}
]
}
Correct a card's match
Replaces a card's match, for example when a user picks one of the alternatives instead of the best match.
Cards Cloud keeps it as the user's correction. Works on live and closed sessions.
Find the row's id in the answer to Upload a card's image, or with
List a session's cards filtered by event_id.
Attributes
- Name
name- Type
- string
- Max
- Maximum:255
- Description
Card name.
- Name
full_name- Type
- string
- Max
- Maximum:500
- Description
Full card name.
- Name
set_name- Type
- string
- Max
- Maximum:255
- Description
Set.
- Name
set_code- Type
- string
- Max
- Maximum:50
- Description
Set code.
- Name
card_number- Type
- string
- Max
- Maximum:50
- Description
Card number.
- Name
series- Type
- string
- Max
- Maximum:255
- Description
Series.
- Name
year- Type
- string
- Max
- Maximum:20
- Description
Year.
- Name
subcategory- Type
- string
- Max
- Maximum:100
- Description
Game or sport.
- Name
price- Type
- number
- Description
Price of the picked card (USD), or
null.
Returns
The updated identification, or 404 if the row was deleted.
Request
curl -XPATCH 'https://api.ximilar.com/cards-cloud/v2/identification/__ID__/' \
--header 'Content-Type: application/json' \
--header 'Authorization: Token __API_TOKEN__' \
--data '{
"name": "Charizard ex",
"full_name": "Charizard ex Obsidian Flames (OBF) #223",
"set_name": "Obsidian Flames",
"set_code": "OBF",
"card_number": "223",
"series": "Scarlet & Violet",
"year": "2023",
"subcategory": "Pokemon",
"price": 80.0
}'
Errors
Cards Cloud answers with the usual HTTP status codes. The ones a client should handle:
| Status | Meaning |
|---|---|
401 | Missing or invalid API token. |
402 | Not enough credits (for example, to create a session). |
403 | Cards Cloud isn't enabled for the account. |
404 | The session or row doesn't exist (it may have been deleted). |
409 | Conflict. |
429 | Too many requests — retry later. |
5xx | Server error — retry later. |