521 lines
34 KiB
Python
521 lines
34 KiB
Python
from dataclasses import dataclass
|
|
import logging
|
|
from app.db.models import Orders, OrderProducts, CardTCGPlayer, CardManabox, APIPricing, TCGPlayerInventory
|
|
from app.services.util._requests import RequestsUtil
|
|
from app.services.util._docker import DockerUtil
|
|
from app.db.utils import db_transaction
|
|
from sqlalchemy.orm import Session
|
|
from datetime import datetime
|
|
from uuid import uuid4 as uuid
|
|
from jinja2 import Environment, FileSystemLoader
|
|
from weasyprint import HTML
|
|
import json
|
|
import time
|
|
import re
|
|
import csv
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
@dataclass
|
|
class TCGPlayerAPIConfig:
|
|
"""Configuration for TCGPlayer API"""
|
|
ORDER_BASE_URL: str = "https://order-management-api.tcgplayer.com/orders"
|
|
API_VERSION: str = "?api-version=2.0"
|
|
SELLER_KEY: str = "e576ed4c"
|
|
|
|
class TCGPlayerAPIService:
|
|
def __init__(self, db: Session):
|
|
self.db = db
|
|
self.docker_util = DockerUtil()
|
|
self.requests_util = RequestsUtil()
|
|
self.is_in_docker = self.docker_util.is_in_docker()
|
|
self.config = TCGPlayerAPIConfig()
|
|
self.cookies = self.get_cookies()
|
|
self.session = None
|
|
self.template_dir = "/app/app/assets/templates"
|
|
self.env = Environment(loader=FileSystemLoader(self.template_dir))
|
|
self.address_label_template = self.env.get_template("address_label.html")
|
|
self.return_address_png = "file:///app/app/assets/images/ccrcardsaddress.png"
|
|
|
|
def get_cookies(self) -> dict:
|
|
if self.is_in_docker:
|
|
return self.requests_util.get_tcgplayer_cookies_from_file()
|
|
else:
|
|
return self.requests_util.get_tcgplayer_browser_cookies()
|
|
|
|
def get_order(self, order_id: str) -> dict:
|
|
url = f"{self.config.ORDER_BASE_URL}/{order_id}{self.config.API_VERSION}"
|
|
response = self.requests_util.send_request(url, method='GET', cookies=self.cookies)
|
|
if response:
|
|
return response.json()
|
|
return None
|
|
|
|
def get_orders(self, size: int = 25) -> dict:
|
|
url = f"{self.config.ORDER_BASE_URL}/search{self.config.API_VERSION}"
|
|
payload = {
|
|
"searchRange": "LastThreeMonths",
|
|
"filters": {
|
|
"sellerKey": self.config.SELLER_KEY
|
|
},
|
|
"sortBy": [
|
|
{"sortingType": "orderStatus", "direction": "ascending"},
|
|
{"sortingType": "orderDate", "direction": "descending"}
|
|
],
|
|
"from": 0,
|
|
"size": size
|
|
}
|
|
response = self.requests_util.send_request(url, method='POST', cookies=self.cookies, json=payload)
|
|
if response:
|
|
return response.json()
|
|
return None
|
|
|
|
def get_product_ids_from_sku(self, sku_ids: list[str]) -> dict:
|
|
"""Get product IDs from TCGPlayer SKU IDs"""
|
|
# convert SKU IDs to integers
|
|
sku_ids = [int(sku_id) for sku_id in sku_ids]
|
|
tcg_cards = self.db.query(CardTCGPlayer).filter(CardTCGPlayer.tcgplayer_id.in_(sku_ids)).all()
|
|
return {str(card.tcgplayer_id): card.product_id for card in tcg_cards}
|
|
|
|
def save_order(self, order: dict):
|
|
# check if order exists by order number
|
|
order_number = order['orderNumber']
|
|
existing_order = self.db.query(Orders).filter(Orders.order_id == order_number).first()
|
|
if existing_order:
|
|
logger.info(f"Order {order_number} already exists in database")
|
|
return existing_order
|
|
transaction = order['transaction']
|
|
shipping = order['shippingAddress']
|
|
products = order['products']
|
|
with db_transaction(self.db):
|
|
db_order = Orders(
|
|
id = str(uuid()),
|
|
order_id=order_number,
|
|
buyer_name=order['buyerName'],
|
|
recipient_name=shipping['recipientName'],
|
|
recipient_address_one=shipping['addressOne'],
|
|
recipient_address_two=shipping['addressTwo'] if 'addressTwo' in shipping else '',
|
|
recipient_city=shipping['city'],
|
|
recipient_state=shipping['territory'],
|
|
recipient_zip=shipping['postalCode'],
|
|
recipient_country=shipping['country'],
|
|
order_date=order['createdAt'],
|
|
status=order['status'],
|
|
num_products=len(products),
|
|
num_cards=sum([product['quantity'] for product in products]),
|
|
product_amount=transaction['productAmount'],
|
|
shipping_amount=transaction['shippingAmount'],
|
|
gross_amount=transaction['grossAmount'],
|
|
fee_amount=transaction['feeAmount'],
|
|
net_amount=transaction['netAmount'],
|
|
direct_fee_amount=transaction['directFeeAmount']
|
|
)
|
|
self.db.add(db_order)
|
|
self.db.flush()
|
|
|
|
product_ids = [product['skuId'] for product in products]
|
|
sku_to_product_id_mapping = self.get_product_ids_from_sku(product_ids)
|
|
order_products = []
|
|
for product in products:
|
|
product_id = sku_to_product_id_mapping.get(product['skuId'])
|
|
if product_id:
|
|
order_products.append(
|
|
OrderProducts(
|
|
id=str(uuid()),
|
|
order_id=db_order.id,
|
|
product_id=product_id,
|
|
quantity=product['quantity'],
|
|
unit_price=product['unitPrice']
|
|
)
|
|
)
|
|
self.db.add_all(order_products)
|
|
return db_order
|
|
|
|
def process_orders_task(self):
|
|
# get last 25 orders from tcgplayer
|
|
orders = self.get_orders(size=100)
|
|
if orders:
|
|
# get list of order ids
|
|
order_ids = [order['orderNumber'] for order in orders['orders']]
|
|
# get a list of order ids that are not in the database
|
|
existing_orders = self.db.query(Orders).filter(Orders.order_id.in_(order_ids)).all()
|
|
existing_order_ids = [order.order_id for order in existing_orders]
|
|
# get a list of order ids that are not in the database
|
|
new_order_ids = [order_id for order_id in order_ids if order_id not in existing_order_ids]
|
|
# process new orders
|
|
processed_orders = []
|
|
if new_order_ids:
|
|
logger.info(f"Processing {len(new_order_ids)} new orders")
|
|
new_orders = [order for order in orders['orders'] if order['orderNumber'] in new_order_ids]
|
|
for new_order in new_orders:
|
|
order = self.get_order(new_order['orderNumber'])
|
|
self.save_order(order)
|
|
processed_orders.append(order['orderNumber'])
|
|
logger.info(f"Processed {len(processed_orders)} new orders")
|
|
return processed_orders
|
|
else:
|
|
logger.info("No new orders to process")
|
|
|
|
def get_scryfall_data(self, scryfall_id: str):
|
|
url = f"https://api.scryfall.com/cards/{scryfall_id}?format=json"
|
|
response = self.requests_util.bare_request(url, method='GET')
|
|
return response
|
|
|
|
def get_tcgplayer_pricing_data(self, tcgplayer_id: str):
|
|
if not self.session:
|
|
self.session = self.requests_util.get_session()
|
|
response = self.session.get("https://tcgplayer.com")
|
|
headers = {
|
|
'accept': 'application/json, text/plain, */*',
|
|
'accept-language': 'en-US,en;q=0.8',
|
|
'priority': 'u=1, i',
|
|
'sec-ch-ua': '"Not(A:Brand";v="99", "Brave";v="133", "Chromium";v="133"',
|
|
'sec-ch-ua-mobile': '?0',
|
|
'sec-ch-ua-platform': '"macOS"',
|
|
'sec-fetch-dest': 'empty',
|
|
'sec-fetch-mode': 'cors',
|
|
'sec-fetch-site': 'same-site',
|
|
'sec-gpc': '1',
|
|
'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0 Safari/537.36'
|
|
}
|
|
url = f"https://mp-search-api.tcgplayer.com/v2/product/{tcgplayer_id}/details?mpfev=3279"
|
|
response = self.session.get(url, headers=headers)
|
|
self.requests_util.previous_request_time = datetime.now()
|
|
return response
|
|
|
|
# pricing
|
|
def get_tcgplayer_pricing_data_for_product(self, product_id: str):
|
|
# get tcgplayer pricing data for a single card by product id
|
|
# product_id to manabox card
|
|
manabox_card = self.db.query(CardManabox).filter(CardManabox.product_id == product_id).first()
|
|
tcgplayer_card = self.db.query(CardTCGPlayer).filter(CardTCGPlayer.product_id == product_id).first()
|
|
if not manabox_card or not tcgplayer_card:
|
|
logger.warning(f"Card with product id {product_id} missing in either Manabox or TCGPlayer")
|
|
return None
|
|
mbfoil = manabox_card.foil
|
|
if str.lower(mbfoil) == 'foil':
|
|
logger.warning(f"Card with product id {product_id} is foil, skipping")
|
|
return None
|
|
# get scryfall id, tcgplayer id, and tcgplayer sku
|
|
scryfall_id = manabox_card.scryfall_id
|
|
tcgplayer_sku = tcgplayer_card.tcgplayer_id
|
|
tcgplayer_id = self.get_scryfall_data(scryfall_id).json().get('tcgplayer_id')
|
|
tcgplayer_pricing = self.get_tcgplayer_pricing_data(tcgplayer_id)
|
|
if not tcgplayer_pricing:
|
|
logger.warning(f"TCGPlayer pricing data not found for product id {product_id}")
|
|
return None
|
|
else:
|
|
logger.info(f"TCGPlayer pricing data found for product id {product_id}")
|
|
return tcgplayer_pricing.json()
|
|
|
|
def save_tcgplayer_pricing_data(self, product_id: str, pricing_data: dict):
|
|
# convert to json
|
|
pricing_data_json = json.dumps(pricing_data)
|
|
with db_transaction(self.db):
|
|
pricing_record = APIPricing(
|
|
id=str(uuid()),
|
|
product_id=product_id,
|
|
pricing_data=str(pricing_data_json)
|
|
)
|
|
self.db.add(pricing_record)
|
|
|
|
def cron_tcgplayer_api_pricing(self):
|
|
# Join both tables but retrieve both objects
|
|
results = self.db.query(TCGPlayerInventory, CardTCGPlayer).join(
|
|
CardTCGPlayer,
|
|
TCGPlayerInventory.tcgplayer_id == CardTCGPlayer.tcgplayer_id
|
|
).all()
|
|
|
|
for inventory, card in results:
|
|
# Now use card.product_id (from CardTCGPlayer)
|
|
pricing_data = self.get_tcgplayer_pricing_data_for_product(card.product_id)
|
|
if pricing_data:
|
|
self.save_tcgplayer_pricing_data(card.product_id, pricing_data)
|
|
|
|
def get_packing_slip_pdf_for_orders(self, order_ids: list[str]):
|
|
url = f"{self.config.ORDER_BASE_URL}/packing-slips/export{self.config.API_VERSION}"
|
|
payload = {
|
|
"sortingType": "byRelease",
|
|
"format": "default",
|
|
"timezoneOffset": -4,
|
|
"orderNumbers": order_ids
|
|
}
|
|
response = self.requests_util.send_request(url, method='POST', cookies=self.cookies, json=payload)
|
|
if response:
|
|
# get filename from response headers
|
|
header = response.headers.get('Content-Disposition', '')
|
|
match = re.search(r'filename="?([^";]+)"?', header)
|
|
filename = match.group(1) if match else f'packingslip{datetime.now().strftime("%Y%m%d_%H%M%S")}.pdf'
|
|
output_filename = f'/app/tmp/{filename}'
|
|
|
|
# save file to disk
|
|
with open(output_filename, 'wb') as f:
|
|
f.write(response.content)
|
|
|
|
return output_filename
|
|
|
|
def get_pull_sheet_for_orders(self, order_ids: list[str]):
|
|
url = f"{self.config.ORDER_BASE_URL}/pull-sheets/export{self.config.API_VERSION}"
|
|
payload = {
|
|
"orderNumbers": order_ids,
|
|
"timezoneOffset": -4,
|
|
}
|
|
response = self.requests_util.send_request(url, method='POST', cookies=self.cookies, json=payload)
|
|
if response:
|
|
# get filename from response headers
|
|
header = response.headers.get('Content-Disposition', '')
|
|
match = re.search(r'filename="?([^";]+)"?', header)
|
|
filename = match.group(1) if match else f'packingslip{datetime.now().strftime("%Y%m%d_%H%M%S")}.pdf'
|
|
output_filename = f'/app/tmp/{filename}'
|
|
# save file to disk
|
|
with open(output_filename, 'wb') as f:
|
|
f.write(response.content)
|
|
|
|
return output_filename
|
|
|
|
def get_address_labels_csv(self, order_ids: list[str]):
|
|
url = f"{self.config.ORDER_BASE_URL}/shipping/export{self.config.API_VERSION}"
|
|
payload = {
|
|
"orderNumbers": order_ids,
|
|
"timezoneOffset": -4
|
|
}
|
|
response = self.requests_util.send_request(url, method='POST', cookies=self.cookies, json=payload)
|
|
if response:
|
|
# get filename from response headers
|
|
header = response.headers.get('Content-Disposition', '')
|
|
match = re.search(r'filename="?([^";]+)"?', header)
|
|
filename = match.group(1) if match else f'shipping{datetime.now().strftime("%Y%m%d_%H%M%S")}.csv'
|
|
output_filename = f'/app/tmp/{filename}'
|
|
# save file to disk
|
|
with open(output_filename, 'wb') as f:
|
|
f.write(response.content)
|
|
|
|
return output_filename
|
|
|
|
def get_address_labels_pdf(self, order_ids: list[str]):
|
|
shipping_csv_filename = self.get_address_labels_csv(order_ids)
|
|
with open(shipping_csv_filename, 'r') as f:
|
|
reader = csv.DictReader(f)
|
|
orders = {}
|
|
for row in reader:
|
|
order_id = row.pop('Order #')
|
|
orders[order_id] = row
|
|
|
|
labels_html = []
|
|
|
|
for order_id in orders:
|
|
order = orders[order_id]
|
|
#if float(order['Value of Products']) >49.99:
|
|
#continue
|
|
# Extract relevant information from the order
|
|
order_info = {
|
|
"recipient_name": order['FirstName'] + ' ' + order['LastName'],
|
|
"address_line1": order['Address1'],
|
|
"address_line2": order['Address2'] if 'Address2' in order else '',
|
|
"city": order['City'],
|
|
"state": order['State'],
|
|
"zip_code": order['PostalCode'],
|
|
"return_address_path": self.return_address_png
|
|
}
|
|
|
|
# Render the label HTML using the template
|
|
labels_html.append(self.address_label_template.render(order_info))
|
|
|
|
if labels_html:
|
|
# Combine the rendered labels into one HTML string
|
|
full_html = "<html><body>" + "\n".join(labels_html) + "</body></html>"
|
|
|
|
# Generate a unique output filename with a timestamp
|
|
output_filename = f'/app/tmp/address_labels_{datetime.now().strftime("%Y%m%d_%H%M%S")}.pdf'
|
|
|
|
# Generate the PDF from the HTML string
|
|
HTML(string=full_html).write_pdf(output_filename)
|
|
|
|
return output_filename
|
|
else:
|
|
print("No orders found or no valid labels generated.")
|
|
return None
|
|
|
|
def process_open_orders(self, order_ids: list[str]=None):
|
|
# get all open orders
|
|
url = f"{self.config.ORDER_BASE_URL}/search{self.config.API_VERSION}"
|
|
"""{"searchRange":"LastThreeMonths","filters":{"sellerKey":"e576ed4c","orderStatuses":["Processing","ReadyToShip","Received","Pulling","ReadyForPickup"],"fulfillmentTypes":["Normal"]},"sortBy":[{"sortingType":"orderStatus","direction":"ascending"},{"sortingType":"orderDate","direction":"ascending"}],"from":0,"size":25}"""
|
|
payload = {
|
|
"searchRange": "LastThreeMonths",
|
|
"filters": {
|
|
"sellerKey": self.config.SELLER_KEY,
|
|
"orderStatuses": ["Processing", "ReadyToShip", "Received", "Pulling", "ReadyForPickup"],
|
|
"fulfillmentTypes": ["Normal"]
|
|
},
|
|
"sortBy": [
|
|
{"sortingType": "orderStatus", "direction": "ascending"},
|
|
{"sortingType": "orderDate", "direction": "ascending"}
|
|
],
|
|
"from": 0,
|
|
"size": 100
|
|
}
|
|
response = self.requests_util.send_request(url, method='POST', cookies=self.cookies, json=payload)
|
|
if response:
|
|
orders = response.json()
|
|
if orders and 'orders' in orders:
|
|
if order_ids is None:
|
|
order_ids = [order['orderNumber'] for order in orders['orders']]
|
|
# get packing slip pdf
|
|
packing_slip_filename = self.get_packing_slip_pdf_for_orders(order_ids)
|
|
# get pull sheet pdf
|
|
pull_sheet_filename = self.get_pull_sheet_for_orders(order_ids)
|
|
# get address labels pdf
|
|
address_labels_filename = self.get_address_labels_pdf(order_ids)
|
|
with open(packing_slip_filename, 'rb') as packing_slip_file, \
|
|
open(pull_sheet_filename, 'rb') as pull_sheet_file, \
|
|
open(address_labels_filename, 'rb') as address_labels_file:
|
|
files = [
|
|
#packing_slip_file,
|
|
# pull_sheet_file,
|
|
address_labels_file
|
|
]
|
|
# request post pdfs
|
|
for file in files:
|
|
self.requests_util.bare_request(
|
|
url="http://192.168.1.110:8000/upload",
|
|
method='POST',
|
|
files={'file': file}
|
|
)
|
|
time.sleep(10)
|
|
return order_ids
|
|
return None
|
|
|
|
|
|
|
|
|
|
|
|
# this one contains nearly everything, use it first
|
|
# what does score mean? - totally ignore score, it seems related to price and changes based on what is on the page. probably some psy op shit to get you to buy expensive stuff, not useful for us
|
|
# can i get volatility from here?
|
|
# no historical data here
|
|
"""
|
|
curl 'https://mp-search-api.tcgplayer.com/v2/product/615745/details?mpfev=3279' \
|
|
-H 'accept: application/json, text/plain, */*' \
|
|
-H 'accept-language: en-US,en;q=0.8' \
|
|
-b 'tcgpartner=PK=TRADECARDS&M=1; valid=set=true; product-display-settings=sort=price+shipping&size=10; OAuthLoginSessionId=63b1a89d-1ac2-43f7-8e79-55a9ca5e761d; __RequestVerificationToken_L2FkbWlu0=Lw1sfWh823UeJ7zRux0b1ZTI4Vg4i_dFt97a55aQpf-qBURVuwWDCJyuCxSwgLNLe9nPlfDSc1AMV5nyqhY4Q4jurxs1; spDisabledUIFeatures=orders; SellerProximity=ZipCode=&MaxSellerDistance=1000&IsActive=false; tcg-uuid=613192dc-ecf6-481a-bec1-afdee8686db7; LastSeller=e576ed4c; __RequestVerificationToken=VFv72VLK6McJVzthg8O-41p7BNkdoW2jQAlDAu-ylO39qfzCddRi2-7bWiH4qloc8Vo_ZftOAAa5OhXL3OByFHIdlwY1; TCGAuthTicket_Production=270B0566400C905C51DEAD644E3CDBD634ECBCCC796B1F77717F461EE104FCE101CFAD2A8458319330A0931018A99214D4EA5601E7551E25E2069ACA550BB71775C0A04F30724E2C4E262CB167EAC2C2EB05D15F9EA08363FC6455B94654F1F110CF079E24201C3B8CEF26762423D8CAA71DDF7B; ASP.NET_SessionId=5ycv15jf0mon3l5adodmkog5; StoreSaveForLater_PRODUCTION=SFLK=a167bf88521f4d0fbeb7497a7ed74629&Ignore=false; TCG_VisitorKey=81fe992f-9a12-4926-a417-7815c4f94edd; setting=CD=US&M=1; SearchSortSettings=M=1&ProductSortOption=MinPrice&ProductSortDesc=True&PriceSortOption=Shipping&ProductResultDisplay=grid; tcg_analytics_previousPageData=%7B%22title%22%3A%22Seller%20Feedback%22%2C%22href%22%3A%22https%3A%2F%2Fshop.tcgplayer.com%2Fsellerfeedback%2Fbe27fef9%22%7D; fileDownloadToken=1740499419709; StoreCart_PRODUCTION=CK=b4f8aff616974a12a6b2811129b81ee2&Ignore=false; tracking-preferences={%22version%22:1%2C%22destinations%22:{%22Actions%20Amplitude%22:false%2C%22AdWords%22:false%2C%22Google%20AdWords%20New%22:false%2C%22Google%20Enhanced%20Conversions%22:false%2C%22Google%20Tag%20Manager%22:false%2C%22Impact%20Partnership%20Cloud%22:false%2C%22Optimizely%22:false}%2C%22custom%22:{%22advertising%22:false%2C%22functional%22:false%2C%22marketingAndAnalytics%22:false}}; tcg-segment-session=1740595460137%257C1740595481177' \
|
|
-H 'origin: https://www.tcgplayer.com' \
|
|
-H 'priority: u=1, i' \
|
|
-H 'referer: https://www.tcgplayer.com/' \
|
|
-H 'sec-ch-ua: "Not(A:Brand";v="99", "Brave";v="133", "Chromium";v="133"' \
|
|
-H 'sec-ch-ua-mobile: ?0' \
|
|
-H 'sec-ch-ua-platform: "macOS"' \
|
|
-H 'sec-fetch-dest: empty' \
|
|
-H 'sec-fetch-mode: cors' \
|
|
-H 'sec-fetch-site: same-site' \
|
|
-H 'sec-gpc: 1' \
|
|
-H 'user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36'
|
|
"""
|
|
|
|
# get volatility also
|
|
"""
|
|
curl 'https://mpgateway.tcgplayer.com/v1/pricepoints/marketprice/skus/8395586/volatility?mpfev=3279' \
|
|
-H 'accept: application/json, text/plain, */*' \
|
|
-H 'accept-language: en-US,en;q=0.8' \
|
|
-b 'tcgpartner=PK=TRADECARDS&M=1; valid=set=true; product-display-settings=sort=price+shipping&size=10; OAuthLoginSessionId=63b1a89d-1ac2-43f7-8e79-55a9ca5e761d; __RequestVerificationToken_L2FkbWlu0=Lw1sfWh823UeJ7zRux0b1ZTI4Vg4i_dFt97a55aQpf-qBURVuwWDCJyuCxSwgLNLe9nPlfDSc1AMV5nyqhY4Q4jurxs1; spDisabledUIFeatures=orders; SellerProximity=ZipCode=&MaxSellerDistance=1000&IsActive=false; tcg-uuid=613192dc-ecf6-481a-bec1-afdee8686db7; LastSeller=e576ed4c; __RequestVerificationToken=VFv72VLK6McJVzthg8O-41p7BNkdoW2jQAlDAu-ylO39qfzCddRi2-7bWiH4qloc8Vo_ZftOAAa5OhXL3OByFHIdlwY1; TCGAuthTicket_Production=270B0566400C905C51DEAD644E3CDBD634ECBCCC796B1F77717F461EE104FCE101CFAD2A8458319330A0931018A99214D4EA5601E7551E25E2069ACA550BB71775C0A04F30724E2C4E262CB167EAC2C2EB05D15F9EA08363FC6455B94654F1F110CF079E24201C3B8CEF26762423D8CAA71DDF7B; ASP.NET_SessionId=5ycv15jf0mon3l5adodmkog5; StoreSaveForLater_PRODUCTION=SFLK=a167bf88521f4d0fbeb7497a7ed74629&Ignore=false; TCG_VisitorKey=81fe992f-9a12-4926-a417-7815c4f94edd; setting=CD=US&M=1; tcg_analytics_previousPageData=%7B%22title%22%3A%22Seller%20Feedback%22%2C%22href%22%3A%22https%3A%2F%2Fshop.tcgplayer.com%2Fsellerfeedback%2Fbe27fef9%22%7D; fileDownloadToken=1740499419709; StoreCart_PRODUCTION=CK=b4f8aff616974a12a6b2811129b81ee2&Ignore=false; tracking-preferences={%22version%22:1%2C%22destinations%22:{%22Actions%20Amplitude%22:false%2C%22AdWords%22:false%2C%22Google%20AdWords%20New%22:false%2C%22Google%20Enhanced%20Conversions%22:false%2C%22Google%20Tag%20Manager%22:false%2C%22Impact%20Partnership%20Cloud%22:false%2C%22Optimizely%22:false}%2C%22custom%22:{%22advertising%22:false%2C%22functional%22:false%2C%22marketingAndAnalytics%22:false}}; SearchCriteria=M=1&WantVerifiedSellers=False&WantDirect=False&WantSellersInCart=False; SearchSortSettings=M=1&ProductSortOption=Sales&ProductSortDesc=False&PriceSortOption=Shipping&ProductResultDisplay=grid; tcg-segment-session=1740597680921%257C1740598418227' \
|
|
-H 'origin: https://www.tcgplayer.com' \
|
|
-H 'priority: u=1, i' \
|
|
-H 'referer: https://www.tcgplayer.com/' \
|
|
-H 'sec-ch-ua: "Not(A:Brand";v="99", "Brave";v="133", "Chromium";v="133"' \
|
|
-H 'sec-ch-ua-mobile: ?0' \
|
|
-H 'sec-ch-ua-platform: "macOS"' \
|
|
-H 'sec-fetch-dest: empty' \
|
|
-H 'sec-fetch-mode: cors' \
|
|
-H 'sec-fetch-site: same-site' \
|
|
-H 'sec-gpc: 1' \
|
|
-H 'user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36'
|
|
"""
|
|
|
|
# handle historical data later
|
|
|
|
|
|
# detailed range quarter - detailed pricing info for the last quarter. seems simple
|
|
"""
|
|
|
|
"""
|
|
# listings - lots of stuff here
|
|
"""
|
|
QUANTITY OVERVIEW
|
|
|
|
curl 'https://mp-search-api.tcgplayer.com/v1/product/615745/listings?mpfev=3279' \
|
|
-H 'accept: application/json, text/plain, */*' \
|
|
-H 'accept-language: en-US,en;q=0.8' \
|
|
-H 'content-type: application/json' \
|
|
-b 'tcgpartner=PK=TRADECARDS&M=1; valid=set=true; product-display-settings=sort=price+shipping&size=10; OAuthLoginSessionId=63b1a89d-1ac2-43f7-8e79-55a9ca5e761d; __RequestVerificationToken_L2FkbWlu0=Lw1sfWh823UeJ7zRux0b1ZTI4Vg4i_dFt97a55aQpf-qBURVuwWDCJyuCxSwgLNLe9nPlfDSc1AMV5nyqhY4Q4jurxs1; spDisabledUIFeatures=orders; SellerProximity=ZipCode=&MaxSellerDistance=1000&IsActive=false; tcg-uuid=613192dc-ecf6-481a-bec1-afdee8686db7; LastSeller=e576ed4c; __RequestVerificationToken=VFv72VLK6McJVzthg8O-41p7BNkdoW2jQAlDAu-ylO39qfzCddRi2-7bWiH4qloc8Vo_ZftOAAa5OhXL3OByFHIdlwY1; TCGAuthTicket_Production=270B0566400C905C51DEAD644E3CDBD634ECBCCC796B1F77717F461EE104FCE101CFAD2A8458319330A0931018A99214D4EA5601E7551E25E2069ACA550BB71775C0A04F30724E2C4E262CB167EAC2C2EB05D15F9EA08363FC6455B94654F1F110CF079E24201C3B8CEF26762423D8CAA71DDF7B; ASP.NET_SessionId=5ycv15jf0mon3l5adodmkog5; StoreSaveForLater_PRODUCTION=SFLK=a167bf88521f4d0fbeb7497a7ed74629&Ignore=false; TCG_VisitorKey=81fe992f-9a12-4926-a417-7815c4f94edd; setting=CD=US&M=1; SearchSortSettings=M=1&ProductSortOption=MinPrice&ProductSortDesc=True&PriceSortOption=Shipping&ProductResultDisplay=grid; tcg_analytics_previousPageData=%7B%22title%22%3A%22Seller%20Feedback%22%2C%22href%22%3A%22https%3A%2F%2Fshop.tcgplayer.com%2Fsellerfeedback%2Fbe27fef9%22%7D; fileDownloadToken=1740499419709; StoreCart_PRODUCTION=CK=b4f8aff616974a12a6b2811129b81ee2&Ignore=false; tracking-preferences={%22version%22:1%2C%22destinations%22:{%22Actions%20Amplitude%22:false%2C%22AdWords%22:false%2C%22Google%20AdWords%20New%22:false%2C%22Google%20Enhanced%20Conversions%22:false%2C%22Google%20Tag%20Manager%22:false%2C%22Impact%20Partnership%20Cloud%22:false%2C%22Optimizely%22:false}%2C%22custom%22:{%22advertising%22:false%2C%22functional%22:false%2C%22marketingAndAnalytics%22:false}}; tcg-segment-session=1740595460137%257C1740595481430' \
|
|
-H 'origin: https://www.tcgplayer.com' \
|
|
-H 'priority: u=1, i' \
|
|
-H 'referer: https://www.tcgplayer.com/' \
|
|
-H 'sec-ch-ua: "Not(A:Brand";v="99", "Brave";v="133", "Chromium";v="133"' \
|
|
-H 'sec-ch-ua-mobile: ?0' \
|
|
-H 'sec-ch-ua-platform: "macOS"' \
|
|
-H 'sec-fetch-dest: empty' \
|
|
-H 'sec-fetch-mode: cors' \
|
|
-H 'sec-fetch-site: same-site' \
|
|
-H 'sec-gpc: 1' \
|
|
-H 'user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36' \
|
|
--data-raw '{"filters":{"term":{"sellerStatus":"Live","channelId":0,"language":["English"],"direct-seller":true,"directProduct":true,"listingType":"standard"},"range":{"quantity":{"gte":1},"direct-inventory":{"gte":1}},"exclude":{"channelExclusion":0,"listingType":"custom"}},"from":0,"size":1,"context":{"shippingCountry":"US","cart":{}},"sort":{"field":"price+shipping","order":"asc"}}'
|
|
|
|
|
|
AGGREGATION AND SOME SPECIFIC DATA IDK THIS MIGHT BE A GOOD ONE
|
|
|
|
curl 'https://mp-search-api.tcgplayer.com/v1/product/615745/listings?mpfev=3279' \
|
|
-H 'accept: application/json, text/plain, */*' \
|
|
-H 'accept-language: en-US,en;q=0.8' \
|
|
-H 'content-type: application/json' \
|
|
-b 'tcgpartner=PK=TRADECARDS&M=1; valid=set=true; product-display-settings=sort=price+shipping&size=10; OAuthLoginSessionId=63b1a89d-1ac2-43f7-8e79-55a9ca5e761d; __RequestVerificationToken_L2FkbWlu0=Lw1sfWh823UeJ7zRux0b1ZTI4Vg4i_dFt97a55aQpf-qBURVuwWDCJyuCxSwgLNLe9nPlfDSc1AMV5nyqhY4Q4jurxs1; spDisabledUIFeatures=orders; SellerProximity=ZipCode=&MaxSellerDistance=1000&IsActive=false; tcg-uuid=613192dc-ecf6-481a-bec1-afdee8686db7; LastSeller=e576ed4c; __RequestVerificationToken=VFv72VLK6McJVzthg8O-41p7BNkdoW2jQAlDAu-ylO39qfzCddRi2-7bWiH4qloc8Vo_ZftOAAa5OhXL3OByFHIdlwY1; TCGAuthTicket_Production=270B0566400C905C51DEAD644E3CDBD634ECBCCC796B1F77717F461EE104FCE101CFAD2A8458319330A0931018A99214D4EA5601E7551E25E2069ACA550BB71775C0A04F30724E2C4E262CB167EAC2C2EB05D15F9EA08363FC6455B94654F1F110CF079E24201C3B8CEF26762423D8CAA71DDF7B; ASP.NET_SessionId=5ycv15jf0mon3l5adodmkog5; StoreSaveForLater_PRODUCTION=SFLK=a167bf88521f4d0fbeb7497a7ed74629&Ignore=false; TCG_VisitorKey=81fe992f-9a12-4926-a417-7815c4f94edd; setting=CD=US&M=1; SearchSortSettings=M=1&ProductSortOption=MinPrice&ProductSortDesc=True&PriceSortOption=Shipping&ProductResultDisplay=grid; tcg_analytics_previousPageData=%7B%22title%22%3A%22Seller%20Feedback%22%2C%22href%22%3A%22https%3A%2F%2Fshop.tcgplayer.com%2Fsellerfeedback%2Fbe27fef9%22%7D; fileDownloadToken=1740499419709; StoreCart_PRODUCTION=CK=b4f8aff616974a12a6b2811129b81ee2&Ignore=false; tracking-preferences={%22version%22:1%2C%22destinations%22:{%22Actions%20Amplitude%22:false%2C%22AdWords%22:false%2C%22Google%20AdWords%20New%22:false%2C%22Google%20Enhanced%20Conversions%22:false%2C%22Google%20Tag%20Manager%22:false%2C%22Impact%20Partnership%20Cloud%22:false%2C%22Optimizely%22:false}%2C%22custom%22:{%22advertising%22:false%2C%22functional%22:false%2C%22marketingAndAnalytics%22:false}}; tcg-segment-session=1740595460137%257C1740595481430' \
|
|
-H 'origin: https://www.tcgplayer.com' \
|
|
-H 'priority: u=1, i' \
|
|
-H 'referer: https://www.tcgplayer.com/' \
|
|
-H 'sec-ch-ua: "Not(A:Brand";v="99", "Brave";v="133", "Chromium";v="133"' \
|
|
-H 'sec-ch-ua-mobile: ?0' \
|
|
-H 'sec-ch-ua-platform: "macOS"' \
|
|
-H 'sec-fetch-dest: empty' \
|
|
-H 'sec-fetch-mode: cors' \
|
|
-H 'sec-fetch-site: same-site' \
|
|
-H 'sec-gpc: 1' \
|
|
-H 'user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36' \
|
|
--data-raw '{"filters":{"term":{"sellerStatus":"Live","channelId":0,"language":["English"]},"range":{"quantity":{"gte":1}},"exclude":{"channelExclusion":0}},"from":0,"size":10,"sort":{"field":"price+shipping","order":"asc"},"context":{"shippingCountry":"US","cart":{}},"aggregations":["listingType"]}'
|
|
|
|
|
|
AGGREGATION OF RANDOM SHIT IDK
|
|
|
|
curl 'https://mp-search-api.tcgplayer.com/v1/product/615745/listings?mpfev=3279' \
|
|
-H 'accept: application/json, text/plain, */*' \
|
|
-H 'accept-language: en-US,en;q=0.8' \
|
|
-H 'content-type: application/json' \
|
|
-b 'tcgpartner=PK=TRADECARDS&M=1; valid=set=true; product-display-settings=sort=price+shipping&size=10; OAuthLoginSessionId=63b1a89d-1ac2-43f7-8e79-55a9ca5e761d; __RequestVerificationToken_L2FkbWlu0=Lw1sfWh823UeJ7zRux0b1ZTI4Vg4i_dFt97a55aQpf-qBURVuwWDCJyuCxSwgLNLe9nPlfDSc1AMV5nyqhY4Q4jurxs1; spDisabledUIFeatures=orders; SellerProximity=ZipCode=&MaxSellerDistance=1000&IsActive=false; tcg-uuid=613192dc-ecf6-481a-bec1-afdee8686db7; LastSeller=e576ed4c; __RequestVerificationToken=VFv72VLK6McJVzthg8O-41p7BNkdoW2jQAlDAu-ylO39qfzCddRi2-7bWiH4qloc8Vo_ZftOAAa5OhXL3OByFHIdlwY1; TCGAuthTicket_Production=270B0566400C905C51DEAD644E3CDBD634ECBCCC796B1F77717F461EE104FCE101CFAD2A8458319330A0931018A99214D4EA5601E7551E25E2069ACA550BB71775C0A04F30724E2C4E262CB167EAC2C2EB05D15F9EA08363FC6455B94654F1F110CF079E24201C3B8CEF26762423D8CAA71DDF7B; ASP.NET_SessionId=5ycv15jf0mon3l5adodmkog5; StoreSaveForLater_PRODUCTION=SFLK=a167bf88521f4d0fbeb7497a7ed74629&Ignore=false; TCG_VisitorKey=81fe992f-9a12-4926-a417-7815c4f94edd; setting=CD=US&M=1; SearchSortSettings=M=1&ProductSortOption=MinPrice&ProductSortDesc=True&PriceSortOption=Shipping&ProductResultDisplay=grid; tcg_analytics_previousPageData=%7B%22title%22%3A%22Seller%20Feedback%22%2C%22href%22%3A%22https%3A%2F%2Fshop.tcgplayer.com%2Fsellerfeedback%2Fbe27fef9%22%7D; fileDownloadToken=1740499419709; StoreCart_PRODUCTION=CK=b4f8aff616974a12a6b2811129b81ee2&Ignore=false; tracking-preferences={%22version%22:1%2C%22destinations%22:{%22Actions%20Amplitude%22:false%2C%22AdWords%22:false%2C%22Google%20AdWords%20New%22:false%2C%22Google%20Enhanced%20Conversions%22:false%2C%22Google%20Tag%20Manager%22:false%2C%22Impact%20Partnership%20Cloud%22:false%2C%22Optimizely%22:false}%2C%22custom%22:{%22advertising%22:false%2C%22functional%22:false%2C%22marketingAndAnalytics%22:false}}; tcg-segment-session=1740595460137%257C1740595481430' \
|
|
-H 'origin: https://www.tcgplayer.com' \
|
|
-H 'priority: u=1, i' \
|
|
-H 'referer: https://www.tcgplayer.com/' \
|
|
-H 'sec-ch-ua: "Not(A:Brand";v="99", "Brave";v="133", "Chromium";v="133"' \
|
|
-H 'sec-ch-ua-mobile: ?0' \
|
|
-H 'sec-ch-ua-platform: "macOS"' \
|
|
-H 'sec-fetch-dest: empty' \
|
|
-H 'sec-fetch-mode: cors' \
|
|
-H 'sec-fetch-site: same-site' \
|
|
-H 'sec-gpc: 1' \
|
|
-H 'user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36' \
|
|
--data-raw '{"filters":{"term":{"condition":["Near Mint","Lightly Played","Moderately Played","Heavily Played","Damaged"],"printing":["Foil"],"language":["English"],"sellerStatus":"Live"},"range":{"quantity":{"gte":1}},"exclude":{"channelExclusion":0}},"context":{"shippingCountry":"US","cart":{}},"aggregations":["seller-key"],"size":0}'
|
|
|
|
|
|
VOLATILITY
|
|
|
|
curl 'https://mpgateway.tcgplayer.com/v1/pricepoints/marketprice/skus/8547894/volatility?mpfev=3279' \
|
|
-H 'accept: application/json, text/plain, */*' \
|
|
-H 'accept-language: en-US,en;q=0.8' \
|
|
-b 'tcgpartner=PK=TRADECARDS&M=1; valid=set=true; product-display-settings=sort=price+shipping&size=10; OAuthLoginSessionId=63b1a89d-1ac2-43f7-8e79-55a9ca5e761d; __RequestVerificationToken_L2FkbWlu0=Lw1sfWh823UeJ7zRux0b1ZTI4Vg4i_dFt97a55aQpf-qBURVuwWDCJyuCxSwgLNLe9nPlfDSc1AMV5nyqhY4Q4jurxs1; spDisabledUIFeatures=orders; SellerProximity=ZipCode=&MaxSellerDistance=1000&IsActive=false; tcg-uuid=613192dc-ecf6-481a-bec1-afdee8686db7; LastSeller=e576ed4c; __RequestVerificationToken=VFv72VLK6McJVzthg8O-41p7BNkdoW2jQAlDAu-ylO39qfzCddRi2-7bWiH4qloc8Vo_ZftOAAa5OhXL3OByFHIdlwY1; TCGAuthTicket_Production=270B0566400C905C51DEAD644E3CDBD634ECBCCC796B1F77717F461EE104FCE101CFAD2A8458319330A0931018A99214D4EA5601E7551E25E2069ACA550BB71775C0A04F30724E2C4E262CB167EAC2C2EB05D15F9EA08363FC6455B94654F1F110CF079E24201C3B8CEF26762423D8CAA71DDF7B; ASP.NET_SessionId=5ycv15jf0mon3l5adodmkog5; StoreSaveForLater_PRODUCTION=SFLK=a167bf88521f4d0fbeb7497a7ed74629&Ignore=false; TCG_VisitorKey=81fe992f-9a12-4926-a417-7815c4f94edd; setting=CD=US&M=1; SearchSortSettings=M=1&ProductSortOption=MinPrice&ProductSortDesc=True&PriceSortOption=Shipping&ProductResultDisplay=grid; tcg_analytics_previousPageData=%7B%22title%22%3A%22Seller%20Feedback%22%2C%22href%22%3A%22https%3A%2F%2Fshop.tcgplayer.com%2Fsellerfeedback%2Fbe27fef9%22%7D; fileDownloadToken=1740499419709; StoreCart_PRODUCTION=CK=b4f8aff616974a12a6b2811129b81ee2&Ignore=false; tracking-preferences={%22version%22:1%2C%22destinations%22:{%22Actions%20Amplitude%22:false%2C%22AdWords%22:false%2C%22Google%20AdWords%20New%22:false%2C%22Google%20Enhanced%20Conversions%22:false%2C%22Google%20Tag%20Manager%22:false%2C%22Impact%20Partnership%20Cloud%22:false%2C%22Optimizely%22:false}%2C%22custom%22:{%22advertising%22:false%2C%22functional%22:false%2C%22marketingAndAnalytics%22:false}}; tcg-segment-session=1740595460137%257C1740595481430' \
|
|
-H 'origin: https://www.tcgplayer.com' \
|
|
-H 'priority: u=1, i' \
|
|
-H 'referer: https://www.tcgplayer.com/' \
|
|
-H 'sec-ch-ua: "Not(A:Brand";v="99", "Brave";v="133", "Chromium";v="133"' \
|
|
-H 'sec-ch-ua-mobile: ?0' \
|
|
-H 'sec-ch-ua-platform: "macOS"' \
|
|
-H 'sec-fetch-dest: empty' \
|
|
-H 'sec-fetch-mode: cors' \
|
|
-H 'sec-fetch-site: same-site' \
|
|
-H 'sec-gpc: 1' \
|
|
-H 'user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36'
|
|
""" |