Look up prices in bulk
curl --request POST \
--url https://api.cromos.so/v1/prices/lookup \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"items": [
{
"card_variant_id": 123,
"grade": "ungraded",
"since": "2023-12-25"
}
]
}
'import requests
url = "https://api.cromos.so/v1/prices/lookup"
payload = { "items": [
{
"card_variant_id": 123,
"grade": "ungraded",
"since": "2023-12-25"
}
] }
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({items: [{card_variant_id: 123, grade: 'ungraded', since: '2023-12-25'}]})
};
fetch('https://api.cromos.so/v1/prices/lookup', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.cromos.so/v1/prices/lookup",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'items' => [
[
'card_variant_id' => 123,
'grade' => 'ungraded',
'since' => '2023-12-25'
]
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.cromos.so/v1/prices/lookup"
payload := strings.NewReader("{\n \"items\": [\n {\n \"card_variant_id\": 123,\n \"grade\": \"ungraded\",\n \"since\": \"2023-12-25\"\n }\n ]\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.cromos.so/v1/prices/lookup")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"items\": [\n {\n \"card_variant_id\": 123,\n \"grade\": \"ungraded\",\n \"since\": \"2023-12-25\"\n }\n ]\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.cromos.so/v1/prices/lookup")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"items\": [\n {\n \"card_variant_id\": 123,\n \"grade\": \"ungraded\",\n \"since\": \"2023-12-25\"\n }\n ]\n}"
response = http.request(request)
puts response.read_body{
"data": {
"items": [
{
"card_variant_id": 14,
"card_id": "base1-4",
"grade": "ungraded",
"currency": "USD",
"price": 50111,
"observed_on": "2026-08-22",
"change": {
"since": "2026-08-20",
"start_cents": 50111,
"start_as_of": "2026-08-20",
"change_cents": 0,
"change_bp": 0
}
},
{
"card_variant_id": 14,
"card_id": "base1-4",
"grade": "ungraded",
"currency": "USD",
"price": 50111,
"observed_on": "2026-08-22"
}
]
},
"error": null
}{
"data": {},
"error": {
"code": "<string>",
"message": "<string>"
}
}{
"data": {},
"error": {
"code": "<string>",
"message": "<string>"
}
}{
"data": {},
"error": {
"code": "<string>",
"message": "<string>"
}
}{
"data": {},
"error": {
"code": "<string>",
"message": "<string>"
}
}{
"data": {},
"error": {
"code": "<string>",
"message": "<string>"
}
}Prices
Look up prices in bulk
Price a whole collection in one request, with an acquisition date per card.
POST
/
v1
/
prices
/
lookup
Look up prices in bulk
curl --request POST \
--url https://api.cromos.so/v1/prices/lookup \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"items": [
{
"card_variant_id": 123,
"grade": "ungraded",
"since": "2023-12-25"
}
]
}
'import requests
url = "https://api.cromos.so/v1/prices/lookup"
payload = { "items": [
{
"card_variant_id": 123,
"grade": "ungraded",
"since": "2023-12-25"
}
] }
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({items: [{card_variant_id: 123, grade: 'ungraded', since: '2023-12-25'}]})
};
fetch('https://api.cromos.so/v1/prices/lookup', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.cromos.so/v1/prices/lookup",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'items' => [
[
'card_variant_id' => 123,
'grade' => 'ungraded',
'since' => '2023-12-25'
]
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.cromos.so/v1/prices/lookup"
payload := strings.NewReader("{\n \"items\": [\n {\n \"card_variant_id\": 123,\n \"grade\": \"ungraded\",\n \"since\": \"2023-12-25\"\n }\n ]\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.cromos.so/v1/prices/lookup")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"items\": [\n {\n \"card_variant_id\": 123,\n \"grade\": \"ungraded\",\n \"since\": \"2023-12-25\"\n }\n ]\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.cromos.so/v1/prices/lookup")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"items\": [\n {\n \"card_variant_id\": 123,\n \"grade\": \"ungraded\",\n \"since\": \"2023-12-25\"\n }\n ]\n}"
response = http.request(request)
puts response.read_body{
"data": {
"items": [
{
"card_variant_id": 14,
"card_id": "base1-4",
"grade": "ungraded",
"currency": "USD",
"price": 50111,
"observed_on": "2026-08-22",
"change": {
"since": "2026-08-20",
"start_cents": 50111,
"start_as_of": "2026-08-20",
"change_cents": 0,
"change_bp": 0
}
},
{
"card_variant_id": 14,
"card_id": "base1-4",
"grade": "ungraded",
"currency": "USD",
"price": 50111,
"observed_on": "2026-08-22"
}
]
},
"error": null
}{
"data": {},
"error": {
"code": "<string>",
"message": "<string>"
}
}{
"data": {},
"error": {
"code": "<string>",
"message": "<string>"
}
}{
"data": {},
"error": {
"code": "<string>",
"message": "<string>"
}
}{
"data": {},
"error": {
"code": "<string>",
"message": "<string>"
}
}{
"data": {},
"error": {
"code": "<string>",
"message": "<string>"
}
}Send the printings you hold and get one priced row back for each, in the order you sent them. Up
to 500 per request, so a 1,000-card collection is two calls rather than a thousand.
The last one is common for a card acquired the week its set came out, where no price exists from
that far back. It is an answer rather than a gap: show it as “no price from then” rather than as
zero movement. Two rarer cases answer What
One figure per tier, derived from completed sales rather than from what sellers are currently
asking. There is no low/high spread and no listing count to weigh it against, because there is no
shelf of active listings behind it. For how heavily a printing trades, read
Identify a printing, not a card
card_variant_id is the id on a card’s variants, from Get a card. A card id
alone is not enough: one card can carry several printings, they are priced separately, and they
move independently.
Do not ask for a condition
grade defaults to ungraded, and for a raw card that is the whole request: leave it off.
There is no condition axis. An ungraded price folds every condition into one figure, so
there is no question to put to someone about the state of a card in order to value it.
Send a grade only for a slabbed copy, matching the grade on the slab: psa10, bgs10, cgc10,
sgc10, or one of the numeric tiers. Anything outside that set is a 400 rather than a silently
empty valuation.
One date per item
since rides on the item, not on the request. Two copies of one card acquired six months apart
are two entries with two different since values, and each gets its own change. Repeat the same
card_variant_id as many times as you hold copies.
change_bp is basis points: 1293 is +12.93%. It is rounded to whole basis points and every
amount is an integer number of cents, so no float reaches you and nothing is left for your client
to round.
Reading an answer that is missing
Four absences mean four different things, and an app usually shows something different for each:| What you see | What it means |
|---|---|
card_id is null | No printing carries that card_variant_id. |
price is null, card_id is set | That printing has no price at the tier you asked for. |
change is absent | You did not send since for this item. |
change is null | You did, and no change can be worked out. |
null the same way, and in both a price does exist on
since: a start of exactly 0, which no percentage can be measured from, and a printing that
carries no current price to compare against.
Telling a held price from a stale one
start_as_of is usually earlier than the since you sent. A price is recorded only on the days
it changed, so the day you acquired a card generally has no reading of its own, and the last price
before it is the one that stood. Compare the two dates to tell a price that held steady from one
that had not been seen in a while.
What price is
One figure per tier, derived from completed sales rather than from what sellers are currently
asking. There is no low/high spread and no listing count to weigh it against, because there is no
shelf of active listings behind it. For how heavily a printing trades, read sales_volume on the
printing from Get a card: copies sold in the trailing year, which is depth rather
than money.Authorizations
Bearer authentication header of the form Bearer <token>, where <token> is your auth token.
Body
application/json
The printings to price, at most 500 per request. Send the same printing more than once to price several copies acquired on different days: each entry gets its own answer.
Required array length:
1 - 500 elementsShow child attributes
Show child attributes