ScreenshotNeo

BlogHow-to

How to Scrape TCGplayer Data with an API

Use TCGplayer’s approved REST API to find products and prices, authenticate safely, handle attribution, and stay within its terms.

By the ScreenshotNeo team1 October 20269 min read

Use TCGplayer’s approved REST API rather than scraping its website HTML. The current developer documentation describes REST API version v1.39.0, bearer-token authentication, catalog and pricing resources, and required attribution. TCGplayer also states that it is no longer granting new API access, so this guide applies to developers who already have an approved API Developer Key.

TCGplayer’s API documentation is the source of truth for your account’s enabled resources and current schemas. Read the developer documentation before deploying an integration.

Before you write code

Check that you are eligible

  1. Confirm that your organization already has an approved TCGplayer API Developer Key. The getting-started guide says, “We are no longer granting new API access at this time.”
  2. Confirm which API purposes and resources TCGplayer approved for your account.
  3. Keep the API key and client secret on a server or in a secret manager. Never put them in browser JavaScript, a mobile app, or a public repository.

If you do not have approved access, do not switch to HTML scraping, headless-browser scraping, proxy rotation, or a browser extension. TCGplayer’s API Terms prohibit automated collection outside the API and restrict competing, redistributive, and excessive-use patterns.

The data model: catalog first, prices second

A reliable integration separates discovery from pricing:

Task Resource family What you keep
Find categories Catalog categories Category IDs and names
Find products Catalog products or advanced search Product IDs, names, set and attributes
Find variants Product details and SKUs SKU IDs, condition, language and printing details
Read market summaries Product pricing Market, low, mid, high and buylist values where returned
Read condition-level prices SKU pricing Prices for a particular SKU and condition

Do not interpret a missing condition price as zero. A null value normally means there is no listing or price for that condition.

Authentication

The documented flow exchanges your client credentials for a bearer token. Cache the token only until its stated expiration, then request a new one. The example below uses the standard token endpoint and form fields documented by TCGplayer; confirm the exact values in your account documentation.

curl -X POST "https://api.tcgplayer.com/token" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  --data-urlencode "grant_type=client_credentials" \
  --data-urlencode "client_id=$TCGPLAYER_CLIENT_ID" \
  --data-urlencode "client_secret=$TCGPLAYER_CLIENT_SECRET"

Save the returned access_token in memory or a protected cache. Do not log it. If the response includes expires_in, schedule refresh before that interval ends.

Complete Python example

This script obtains a token, searches the catalog, and requests product pricing. Set the endpoint paths to the resources enabled for your account and check the current documentation for required query parameters.

import os
import time
import requests

BASE = "https://api.tcgplayer.com"
CLIENT_ID = os.environ["TCGPLAYER_CLIENT_ID"]
CLIENT_SECRET = os.environ["TCGPLAYER_CLIENT_SECRET"]

_session = requests.Session()
_token = None
_token_expires_at = 0

def get_token():
    global _token, _token_expires_at
    if _token and time.time() < _token_expires_at - 60:
        return _token
    response = _session.post(
        f"{BASE}/token",
        data={
            "grant_type": "client_credentials",
            "client_id": CLIENT_ID,
            "client_secret": CLIENT_SECRET,
        },
        timeout=30,
    )
    response.raise_for_status()
    payload = response.json()
    _token = payload["access_token"]
    _token_expires_at = time.time() + int(payload.get("expires_in", 3600))
    return _token

def api_get(path, params=None):
    response = _session.get(
        f"{BASE}{path}",
        headers={"Authorization": f"bearer {get_token()}"},
        params=params,
        timeout=30,
    )
    if response.status_code == 401:
        # Refresh once if a cached token expired unexpectedly.
        global _token
        _token = None
        response = _session.get(
            f"{BASE}{path}",
            headers={"Authorization": f"bearer {get_token()}"},
            params=params,
            timeout=30,
        )
    response.raise_for_status()
    return response.json()

# Replace the category and search parameters with the values you need.
categories = api_get("/catalog/categories")
print("categories:", categories)

products = api_get("/catalog/products", {"categoryId": 1, "limit": 10})
print("products:", products)

# Use a real product ID returned by the catalog response.
product_id = products["results"][0]["productId"]
pricing = api_get(f"/pricing/product/{product_id}")
print("pricing:", pricing)

Some accounts or API revisions use different pagination, filter, or response-envelope names. Keep the request pattern, then align the paths and fields with the v1.39.0 documentation and your enabled permissions.

Complete Node.js example

const BASE = 'https://api.tcgplayer.com';
const clientId = process.env.TCGPLAYER_CLIENT_ID;
const clientSecret = process.env.TCGPLAYER_CLIENT_SECRET;

let token;
let tokenExpiresAt = 0;

async function getToken() {
  if (token && Date.now() < tokenExpiresAt - 60_000) return token;
  const body = new URLSearchParams({
    grant_type: 'client_credentials',
    client_id: clientId,
    client_secret: clientSecret,
  });
  const res = await fetch(`${BASE}/token`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
    body,
  });
  if (!res.ok) throw new Error(`Token request failed: ${res.status} ${await res.text()}`);
  const data = await res.json();
  token = data.access_token;
  tokenExpiresAt = Date.now() + Number(data.expires_in ?? 3600) * 1000;
  return token;
}

async function apiGet(path, params = {}) {
  const url = new URL(`${BASE}${path}`);
  for (const [key, value] of Object.entries(params)) url.searchParams.set(key, String(value));
  let res = await fetch(url, { headers: { Authorization: `bearer ${await getToken()}` } });
  if (res.status === 401) {
    token = undefined;
    res = await fetch(url, { headers: { Authorization: `bearer ${await getToken()}` } });
  }
  if (!res.ok) throw new Error(`API request failed: ${res.status} ${await res.text()}`);
  return res.json();
}

const categories = await apiGet('/catalog/categories');
console.log(categories);
const products = await apiGet('/catalog/products', { categoryId: 1, limit: 10 });
console.log(products);
const productId = products.results[0].productId;
console.log(await apiGet(`/pricing/product/${productId}`));

Complete cURL workflow

TOKEN=$(curl -sS -X POST "https://api.tcgplayer.com/token" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  --data-urlencode "grant_type=client_credentials" \
  --data-urlencode "client_id=$TCGPLAYER_CLIENT_ID" \
  --data-urlencode "client_secret=$TCGPLAYER_CLIENT_SECRET" | jq -r .access_token)

curl -sS "https://api.tcgplayer.com/catalog/categories" \
  -H "Authorization: bearer $TOKEN" | jq

curl -sS "https://api.tcgplayer.com/catalog/products?categoryId=1&limit=10" \
  -H "Authorization: bearer $TOKEN" | jq

# Substitute a product ID returned by the previous response.
curl -sS "https://api.tcgplayer.com/pricing/product/PRODUCT_ID" \
  -H "Authorization: bearer $TOKEN" | jq

Choosing catalog, advanced search, product and SKU calls

Catalog discovery

Use category and product resources when you are building an index or need stable product IDs. Store the IDs returned by TCGplayer instead of matching names repeatedly.

Use advanced search when a user supplies attributes such as set, rarity, number, or other catalog filters. Treat search results as identifiers; request pricing after you have selected the product or SKU.

Product pricing

Product-level pricing is useful for market, low, mid, high, and buylist summaries. It is the right choice for dashboards that show a single headline value.

SKU pricing

SKU-level pricing is appropriate when condition, language, printing, or a specific variant matters. Preserve nulls and the timestamp returned by the API so downstream users can distinguish “no listing” from “free.”

Store authorization is a separate integration

A store access token represents a contract with a store. It can expose pricing and inventory and may include modification capabilities. Keep store credentials isolated from general catalog credentials, limit them to the smallest service that needs them, and audit every write operation.

Required attribution and permitted use

If your API-powered interface displays TCGplayer data, include the required notice:

This product uses TCGplayer data but is not endorsed or certified by TCGplayer.

Identify TCGplayer as the pricing source and link to the relevant TCGplayer product page or search result. Review the API Terms & Conditions for the approved purpose of your integration.

  • Do not crawl, scrape, or automate collection from the TCGplayer website outside the API.
  • Do not build a competing service or redistribute TCG Content for commercial or competitive purposes without permission.
  • Do not combine TCGplayer pricing with third-party pricing when the terms prohibit that use.
  • Do not send excessive or abusive request volume.

Production checklist

  • Cache bearer tokens until shortly before expiration.
  • Cache catalog IDs and unchanged product metadata; refresh prices on a schedule appropriate to your approved use.
  • Use bounded retries with exponential backoff for transient 5xx and network failures. Do not retry authentication or validation errors indefinitely.
  • Apply a concurrency limit and a queue so a user spike cannot become an API flood.
  • Record request IDs, status codes, latency, endpoint, and your own correlation ID. Never log client secrets or bearer tokens.
  • Rotate secrets and revoke credentials when a team member or deployment environment changes.
  • Validate numeric fields and preserve null pricing values.
  • Store the source timestamp and your retrieval timestamp so users know how fresh a value is.

Troubleshooting

Symptom Likely cause Fix
401 Unauthorized Expired token, malformed bearer header, or wrong credentials Request a fresh token, send Authorization: bearer TOKEN, and verify the client credentials are from the approved application.
403 Forbidden Your approved purpose or resource does not include the call Check account permissions and contact TCGplayer through the documented support path. Do not work around the restriction.
404 Not Found Wrong resource path or an ID from the wrong resource type Confirm the v1.39.0 path and use a product ID for product pricing or a SKU ID for SKU pricing.
429 or throttling Too many concurrent or repeated requests Reduce concurrency, add backoff and caching, and follow the limits associated with your account.
Empty search results Wrong category, filter, page, or spelling Start with broad catalog discovery, inspect the response envelope, then add one filter at a time.
Price is null No listing exists for that condition or SKU Display “no listing” or equivalent; never coerce null to zero.
Works locally but fails in production Secrets missing, clock skew, proxy changes, or blocked outbound traffic Check environment variables, synchronized system time, outbound access, and redacted request logs.

Performance, reliability and cost

The largest avoidable cost is unnecessary API traffic. Resolve a product once, retain its ID, batch work through your queue, and refresh only the prices your product actually displays. Token caching removes an authentication request from every data request. Separate catalog refresh jobs from user-facing price reads so a slow catalog operation cannot block the application.

For reliability, make reads idempotent, use timeouts, retry only transient failures, and expose a stale-data indicator when your cache is serving an older successful response. Your financial model should include your own hosting, storage, queue, and monitoring costs; the research dossier does not establish a public TCGplayer API price list.

Or skip the browser setup

ScreenshotNeo is a screenshot API, not a substitute for TCGplayer’s approved data API. Use it when you need a clean visual capture of a permitted page, report, or internal dashboard after your data pipeline has produced it. One GET request returns PNG, JPEG, WebP, or PDF, and the ScreenshotNeo docs list the options.

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

ScreenshotNeo removes cookie banners, newsletter popups, and chat widgets before the shot. Bot checks, blank pages, and failed loads are never billed, and response headers identify the page verdict and billing result. Its MCP server lets AI agents take screenshots, and 1,000 screenshots a month are free with no card; paid plans start at $5 for 3,000. Create a free account at ScreenshotNeo.

FAQ

Can I scrape TCGplayer HTML if I only make a few requests?

No. The terms prohibit automated collection outside the API regardless of whether the volume is small. Use an approved API integration.

Can I get a new API key today?

The current getting-started guide says TCGplayer is no longer granting new API access. Existing approved users should verify their credentials and permissions.

Should I use product pricing or SKU pricing?

Use product pricing for market summaries and SKU pricing when condition or another specific variant changes the value.

How should I show missing prices?

Preserve null and label it as unavailable or no listing. Zero is a real value and should not represent missing data.

What attribution belongs in my app?

Include “This product uses TCGplayer data but is not endorsed or certified by TCGplayer,” identify TCGplayer as the source, and link to the relevant product or search page.