curl --request POST \
--url https://catchbackcards.com/api/partner/v1/cards/purchase \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--header 'Idempotency-Key: <idempotency-key>' \
--data '
{
"card_id": "0e94dc61-5a2f-4b1e-9c3d-7f8a1b2c3d4e",
"max_price": 1450
}
'import requests
url = "https://catchbackcards.com/api/partner/v1/cards/purchase"
payload = {
"card_id": "0e94dc61-5a2f-4b1e-9c3d-7f8a1b2c3d4e",
"max_price": 1450
}
headers = {
"Idempotency-Key": "<idempotency-key>",
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {
'Idempotency-Key': '<idempotency-key>',
Authorization: 'Bearer <token>',
'Content-Type': 'application/json'
},
body: JSON.stringify({card_id: '0e94dc61-5a2f-4b1e-9c3d-7f8a1b2c3d4e', max_price: 1450})
};
fetch('https://catchbackcards.com/api/partner/v1/cards/purchase', 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://catchbackcards.com/api/partner/v1/cards/purchase",
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([
'card_id' => '0e94dc61-5a2f-4b1e-9c3d-7f8a1b2c3d4e',
'max_price' => 1450
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json",
"Idempotency-Key: <idempotency-key>"
],
]);
$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://catchbackcards.com/api/partner/v1/cards/purchase"
payload := strings.NewReader("{\n \"card_id\": \"0e94dc61-5a2f-4b1e-9c3d-7f8a1b2c3d4e\",\n \"max_price\": 1450\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Idempotency-Key", "<idempotency-key>")
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://catchbackcards.com/api/partner/v1/cards/purchase")
.header("Idempotency-Key", "<idempotency-key>")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"card_id\": \"0e94dc61-5a2f-4b1e-9c3d-7f8a1b2c3d4e\",\n \"max_price\": 1450\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://catchbackcards.com/api/partner/v1/cards/purchase")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Idempotency-Key"] = '<idempotency-key>'
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"card_id\": \"0e94dc61-5a2f-4b1e-9c3d-7f8a1b2c3d4e\",\n \"max_price\": 1450\n}"
response = http.request(request)
puts response.read_body{
"checkout_id": "5d1c…",
"transaction_id": "a17e…",
"card_id": "0e94dc61-…",
"source": "shop",
"listing_id": null,
"price": 1450,
"fee": 1,
"total_charged": 1451,
"remaining_balance": 48549,
"item": {
"id": "0e94dc61-…",
"category": "pokemon",
"name": "Charizard ex",
"set": "Pokemon Sv3 En-Obsidian Flames",
"grading_company": "PSA",
"grading_rank": 10,
"slab_label": "2023 Obsidian Flames Charizard ex",
"language": "english",
"cert_number": "84512377",
"market_value": 1450,
"front_image": "https://…/0e94dc61-….png",
"back_image": "https://…/0e94dc61-…-back.png",
"render_image": "https://…/0e94dc61-…-render.png",
"image_url": "https://…/0e94dc61-…-render.png"
}
}{
"error": "Insufficient CatchCoins"
}{
"error": "Missing or malformed API key"
}{
"error": "<string>",
"code": "FEATURE_RESTRICTED",
"capability": "<string>"
}{
"error": "<string>",
"code": "<string>"
}{
"checkout_id": "5d1c…",
"error": "The price changed to $1500.00 since you looked. Check the new price and try again.",
"code": "PRICE_CHANGED",
"card_id": "0e94dc61-…",
"price": 1500,
"max_price": 1450
}{
"error": "<string>",
"code": "IDEMPOTENCY_KEY_REUSED"
}{
"error": "Daily card limit reached: 25 cards / $50000 per day.",
"code": "DAILY_CARD_LIMIT_REACHED",
"cards_used": 25,
"max_cards_per_day": 25,
"spend_used": 18240.5,
"max_card_spend_per_day": 50000,
"resets_at": "2026-10-29T00:00:00.000Z"
}{
"error": "We couldn't confirm whether this order was charged. It's held for review and we'll settle any charge, so check your balance before ordering again.",
"code": "PAYMENT_OUTCOME_UNKNOWN",
"needs_review": true,
"checkout_id": "5d1c…"
}{
"error": "<string>",
"code": "<string>"
}Buy a card
Scope: buy:cards (needs a linked account). Moves money.
Buys one card from the shop or a fixed-price listing: its price plus a $1 buyer fee, from your cash CatchCoins balance only. The card lands in your vault. Daily card and spend caps apply: see Rate limits.
needs_review: true in any response means the order may have been charged and we are settling it. Never place it again under a new key. Follow it with Get an order. See Idempotency.
curl --request POST \
--url https://catchbackcards.com/api/partner/v1/cards/purchase \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--header 'Idempotency-Key: <idempotency-key>' \
--data '
{
"card_id": "0e94dc61-5a2f-4b1e-9c3d-7f8a1b2c3d4e",
"max_price": 1450
}
'import requests
url = "https://catchbackcards.com/api/partner/v1/cards/purchase"
payload = {
"card_id": "0e94dc61-5a2f-4b1e-9c3d-7f8a1b2c3d4e",
"max_price": 1450
}
headers = {
"Idempotency-Key": "<idempotency-key>",
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {
'Idempotency-Key': '<idempotency-key>',
Authorization: 'Bearer <token>',
'Content-Type': 'application/json'
},
body: JSON.stringify({card_id: '0e94dc61-5a2f-4b1e-9c3d-7f8a1b2c3d4e', max_price: 1450})
};
fetch('https://catchbackcards.com/api/partner/v1/cards/purchase', 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://catchbackcards.com/api/partner/v1/cards/purchase",
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([
'card_id' => '0e94dc61-5a2f-4b1e-9c3d-7f8a1b2c3d4e',
'max_price' => 1450
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json",
"Idempotency-Key: <idempotency-key>"
],
]);
$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://catchbackcards.com/api/partner/v1/cards/purchase"
payload := strings.NewReader("{\n \"card_id\": \"0e94dc61-5a2f-4b1e-9c3d-7f8a1b2c3d4e\",\n \"max_price\": 1450\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Idempotency-Key", "<idempotency-key>")
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://catchbackcards.com/api/partner/v1/cards/purchase")
.header("Idempotency-Key", "<idempotency-key>")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"card_id\": \"0e94dc61-5a2f-4b1e-9c3d-7f8a1b2c3d4e\",\n \"max_price\": 1450\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://catchbackcards.com/api/partner/v1/cards/purchase")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Idempotency-Key"] = '<idempotency-key>'
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"card_id\": \"0e94dc61-5a2f-4b1e-9c3d-7f8a1b2c3d4e\",\n \"max_price\": 1450\n}"
response = http.request(request)
puts response.read_body{
"checkout_id": "5d1c…",
"transaction_id": "a17e…",
"card_id": "0e94dc61-…",
"source": "shop",
"listing_id": null,
"price": 1450,
"fee": 1,
"total_charged": 1451,
"remaining_balance": 48549,
"item": {
"id": "0e94dc61-…",
"category": "pokemon",
"name": "Charizard ex",
"set": "Pokemon Sv3 En-Obsidian Flames",
"grading_company": "PSA",
"grading_rank": 10,
"slab_label": "2023 Obsidian Flames Charizard ex",
"language": "english",
"cert_number": "84512377",
"market_value": 1450,
"front_image": "https://…/0e94dc61-….png",
"back_image": "https://…/0e94dc61-…-back.png",
"render_image": "https://…/0e94dc61-…-render.png",
"image_url": "https://…/0e94dc61-…-render.png"
}
}{
"error": "Insufficient CatchCoins"
}{
"error": "Missing or malformed API key"
}{
"error": "<string>",
"code": "FEATURE_RESTRICTED",
"capability": "<string>"
}{
"error": "<string>",
"code": "<string>"
}{
"checkout_id": "5d1c…",
"error": "The price changed to $1500.00 since you looked. Check the new price and try again.",
"code": "PRICE_CHANGED",
"card_id": "0e94dc61-…",
"price": 1500,
"max_price": 1450
}{
"error": "<string>",
"code": "IDEMPOTENCY_KEY_REUSED"
}{
"error": "Daily card limit reached: 25 cards / $50000 per day.",
"code": "DAILY_CARD_LIMIT_REACHED",
"cards_used": 25,
"max_cards_per_day": 25,
"spend_used": 18240.5,
"max_card_spend_per_day": 50000,
"resets_at": "2026-10-29T00:00:00.000Z"
}{
"error": "We couldn't confirm whether this order was charged. It's held for review and we'll settle any charge, so check your balance before ordering again.",
"code": "PAYMENT_OUTCOME_UNKNOWN",
"needs_review": true,
"checkout_id": "5d1c…"
}{
"error": "<string>",
"code": "<string>"
}Authorizations
Your API key, sent as a bearer token. Keep it on your server. See Authentication. A request from a location we refuse gets a 403 with Access denied, whether or not it has a valid key: where you can call from.
Headers
Your order id, 8–128 characters of A-Za-z0-9_-, unique per account across pack and card orders. Reuse it to retry the same order safely, and use a new one for every new order.
^[A-Za-z0-9_-]{8,128}$Body
Response
The card is yours. A retry of a finished order returns the original body with idempotent_replay: true, and charges nothing. If we closed the order after its response was lost, the retry returns a shorter body with reconciled: true and no fee, remaining_balance or item: see Idempotency.
Our id for the order.
The sale. It is the purchase_id that Get vault and Sell back use for this card.
shop, listing Charged for the card.
The $1 buyer fee.
The card object. Other fields appear only on the catalogs that have them.
Show child attributes
Show child attributes
The sale went through and the card is yours, but our bookkeeping needs a manual check. Don't retry under a new key.
Present and true on the shorter body we return when we closed the order after its response was lost.
Present with reconciled.

