├── __init__.py ├── models ├── __init__.py ├── .DS_Store ├── __pycache__ │ ├── me.cpython-311.pyc │ ├── item.cpython-311.pyc │ ├── auction.cpython-311.pyc │ ├── listing.cpython-311.pyc │ ├── seller.cpython-311.pyc │ ├── __init__.cpython-311.pyc │ ├── reference.cpython-311.pyc │ ├── stickers.cpython-311.pyc │ ├── buy_orders.cpython-311.pyc │ └── statistics.cpython-311.pyc ├── stall.py ├── buy_orders.py ├── reference.py ├── statistics.py ├── stickers.py ├── auction.py ├── seller.py ├── listing.py ├── item.py └── me.py ├── .gitignore ├── .idea ├── .gitignore ├── vcs.xml ├── inspectionProfiles │ └── profiles_settings.xml ├── misc.xml ├── modules.xml └── csfloat_api.iml ├── README.md └── csfloat_client.py /__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /models/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /testttt.py 2 | __pycache__/ 3 | *.pyc 4 | .DS_Store 5 | -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | -------------------------------------------------------------------------------- /models/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rushifakami/csfloat_api/HEAD/models/.DS_Store -------------------------------------------------------------------------------- /models/__pycache__/me.cpython-311.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rushifakami/csfloat_api/HEAD/models/__pycache__/me.cpython-311.pyc -------------------------------------------------------------------------------- /models/__pycache__/item.cpython-311.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rushifakami/csfloat_api/HEAD/models/__pycache__/item.cpython-311.pyc -------------------------------------------------------------------------------- /models/__pycache__/auction.cpython-311.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rushifakami/csfloat_api/HEAD/models/__pycache__/auction.cpython-311.pyc -------------------------------------------------------------------------------- /models/__pycache__/listing.cpython-311.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rushifakami/csfloat_api/HEAD/models/__pycache__/listing.cpython-311.pyc -------------------------------------------------------------------------------- /models/__pycache__/seller.cpython-311.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rushifakami/csfloat_api/HEAD/models/__pycache__/seller.cpython-311.pyc -------------------------------------------------------------------------------- /models/__pycache__/__init__.cpython-311.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rushifakami/csfloat_api/HEAD/models/__pycache__/__init__.cpython-311.pyc -------------------------------------------------------------------------------- /models/__pycache__/reference.cpython-311.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rushifakami/csfloat_api/HEAD/models/__pycache__/reference.cpython-311.pyc -------------------------------------------------------------------------------- /models/__pycache__/stickers.cpython-311.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rushifakami/csfloat_api/HEAD/models/__pycache__/stickers.cpython-311.pyc -------------------------------------------------------------------------------- /models/__pycache__/buy_orders.cpython-311.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rushifakami/csfloat_api/HEAD/models/__pycache__/buy_orders.cpython-311.pyc -------------------------------------------------------------------------------- /models/__pycache__/statistics.cpython-311.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rushifakami/csfloat_api/HEAD/models/__pycache__/statistics.cpython-311.pyc -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/csfloat_api.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /models/stall.py: -------------------------------------------------------------------------------- 1 | from typing import List, Dict, Any, Optional 2 | from .listing import Listing 3 | 4 | 5 | class Stall: 6 | def __init__(self, *, data: Dict[str, Any]) -> None: 7 | listings_data = data.get('data') 8 | if listings_data is None: 9 | self._listings = [] 10 | else: 11 | self._listings = [Listing(data=item) for item in listings_data] 12 | 13 | @property 14 | def listings(self) -> List[Listing]: 15 | return self._listings 16 | -------------------------------------------------------------------------------- /models/buy_orders.py: -------------------------------------------------------------------------------- 1 | from typing import Optional, Dict, Any 2 | 3 | class BuyOrders: 4 | __slots__ = ( 5 | "_id", 6 | "_created_at", 7 | "_expression", 8 | "_qty", 9 | "_price" 10 | ) 11 | 12 | def __init__(self, *, data: Dict[str, Any]): 13 | self._id = data.get("id") 14 | self._created_at = data.get("created_at") 15 | self._expression = data.get("expression") 16 | self._qty = data.get("qty") 17 | self._price = data.get("price") 18 | 19 | @property 20 | def id(self) -> Optional[str]: 21 | return self._id 22 | 23 | @property 24 | def created_at(self) -> Optional[str]: 25 | return self._created_at 26 | 27 | @property 28 | def expression(self) -> Optional[str]: 29 | return self._expression 30 | 31 | @property 32 | def qty(self) -> Optional[int]: 33 | return self._qty 34 | 35 | @property 36 | def price(self) -> Optional[int]: 37 | return self._price -------------------------------------------------------------------------------- /models/reference.py: -------------------------------------------------------------------------------- 1 | from typing import Dict, Any, Optional 2 | 3 | 4 | class Reference: 5 | __slots__ = ( 6 | "_base_price", 7 | "_float_factor", 8 | "_predicted_price", 9 | "_quantity", 10 | "_last_updated" 11 | ) 12 | 13 | def __init__(self, *, data: Dict[str, Any]): 14 | self._base_price = data.get("base_price") 15 | self._float_factor = data.get("float_factor") 16 | self._predicted_price = data.get("predicted_price") 17 | self._quantity = data.get("quantity") 18 | self._last_updated = data.get("last_updated") 19 | 20 | @property 21 | def base_price(self) -> Optional[float]: 22 | return self._base_price 23 | 24 | @property 25 | def float_factor(self) -> Optional[float]: 26 | return self._float_factor 27 | 28 | @property 29 | def predicted_price(self) -> Optional[float]: 30 | return self._predicted_price 31 | 32 | @property 33 | def quantity(self) -> Optional[int]: 34 | return self._quantity 35 | 36 | @property 37 | def last_updated(self) -> Optional[str]: 38 | return self._last_updated 39 | -------------------------------------------------------------------------------- /models/statistics.py: -------------------------------------------------------------------------------- 1 | from typing import Dict, Any, Optional 2 | 3 | 4 | class Statistics: 5 | __slots__ = ( 6 | "_median_trade_time", 7 | "_total_avoided_trades", 8 | "_total_failed_trades", 9 | "_total_trades", 10 | "_total_verified_trades" 11 | ) 12 | 13 | def __init__(self, *, data: Dict[str, Any]): 14 | self._median_trade_time = data.get("median_trade_time") 15 | self._total_avoided_trades = data.get("total_avoided_trades") 16 | self._total_failed_trades = data.get("total_failed_trades") 17 | self._total_trades = data.get("total_trades") 18 | self._total_verified_trades = data.get("total_verified_trades") 19 | 20 | @property 21 | def median_trade_time(self) -> Optional[float]: 22 | return self._median_trade_time 23 | 24 | @property 25 | def total_avoided_trades(self) -> Optional[int]: 26 | return self._total_avoided_trades 27 | 28 | @property 29 | def total_failed_trades(self) -> Optional[int]: 30 | return self._total_failed_trades 31 | 32 | @property 33 | def total_trades(self) -> Optional[int]: 34 | return self._total_trades 35 | 36 | @property 37 | def total_verified_trades(self) -> Optional[int]: 38 | return self._total_verified_trades 39 | -------------------------------------------------------------------------------- /models/stickers.py: -------------------------------------------------------------------------------- 1 | from typing import Dict, Any, Optional 2 | 3 | 4 | class StickerReference: 5 | __slots__ = ( 6 | "_price", 7 | "_quantity", 8 | "_updated_at" 9 | ) 10 | 11 | def __init__(self, *, data: Dict[str, Any]): 12 | self._price = data.get("price", "") 13 | self._quantity = data.get("quantity") 14 | self._updated_at = data.get("updated_at") 15 | 16 | @property 17 | def price(self) -> Optional[float]: 18 | return self._price 19 | 20 | @property 21 | def quantity(self) -> Optional[int]: 22 | return self._quantity 23 | 24 | @property 25 | def updated_at(self) -> Optional[str]: 26 | return self._updated_at 27 | 28 | 29 | class Sticker: 30 | __slots__ = ( 31 | "_stickerId", 32 | "_slot", 33 | "_wear", 34 | "_offset_x", 35 | "_offset_y", 36 | "_icon_url", 37 | "_name", 38 | "_reference" 39 | ) 40 | 41 | def __init__(self, *, data: Dict[str, Any]): 42 | self._stickerId = data.get("stickerId") 43 | self._slot = data.get("slot") 44 | self._wear = data.get("wear") 45 | self._offset_x = data.get("offset_x") 46 | self._offset_y = data.get("offset_y") 47 | self._icon_url = data.get("icon_url") 48 | self._name = data.get("name") 49 | self._reference = data.get("reference") 50 | 51 | @property 52 | def stickerId(self) -> Optional[int]: 53 | return self._stickerId 54 | 55 | @property 56 | def slot(self) -> Optional[int]: 57 | return self._slot 58 | 59 | @property 60 | def wear(self) -> Optional[float]: 61 | return self._wear 62 | 63 | @property 64 | def icon_url(self) -> Optional[str]: 65 | return self._icon_url 66 | 67 | @property 68 | def name(self) -> Optional[str]: 69 | return self._name 70 | 71 | @property 72 | def reference(self) -> Optional[StickerReference]: 73 | if self._reference is not None: 74 | return StickerReference(data=self._reference) 75 | -------------------------------------------------------------------------------- /models/auction.py: -------------------------------------------------------------------------------- 1 | from typing import Dict, Any, Optional 2 | 3 | 4 | class TopBid: 5 | __slots__ = ( 6 | "_id", 7 | "_created_at", 8 | "_price", 9 | "_contract_id", 10 | "_state", 11 | "_active", 12 | "_obfuscated_buyer_id" 13 | ) 14 | 15 | def __init__(self, *, data: Dict[str, Any]): 16 | self._id = data.get("id") 17 | self._created_at = data.get("created_at") 18 | self._price = data.get("price") 19 | self._contract_id = data.get("contract_id") 20 | self._state = data.get("state") 21 | self._active = data.get("active") 22 | self._obfuscated_buyer_id = data.get("obfuscated_buyer_id") 23 | 24 | @property 25 | def id(self) -> Optional[int]: 26 | return self._id 27 | 28 | @property 29 | def created_at(self) -> Optional[str]: 30 | return self._created_at 31 | 32 | @property 33 | def price(self) -> Optional[int]: 34 | return self._price 35 | 36 | @property 37 | def contract_id(self) -> Optional[str]: 38 | return self._contract_id 39 | 40 | @property 41 | def state(self) -> Optional[str]: 42 | return self._state 43 | 44 | @property 45 | def obfuscated_buyer_id(self) -> Optional[str]: 46 | return self._obfuscated_buyer_id 47 | 48 | 49 | class AuctionDetails: 50 | __slots__ = ( 51 | "_reserve_price", 52 | "_top_bid", 53 | "_expires_at", 54 | "_min_next_bid", 55 | ) 56 | 57 | def __init__(self, *, data: Dict[str, Any]): 58 | self._reserve_price = data.get("reserve_price") 59 | self._top_bid = data.get("top_bid") 60 | self._expires_at = data.get("expires_at") 61 | self._min_next_bid = data.get("min_next_bid") 62 | 63 | @property 64 | def reserve_price(self) -> Optional[float]: 65 | return self._reserve_price 66 | 67 | @property 68 | def top_bid(self) -> Optional[TopBid]: 69 | return TopBid(data=self._top_bid) 70 | 71 | @property 72 | def expires_at(self) -> Optional[str]: 73 | return self._expires_at 74 | 75 | @property 76 | def min_next_bid(self) -> Optional[float]: 77 | return self._min_next_bid 78 | -------------------------------------------------------------------------------- /models/seller.py: -------------------------------------------------------------------------------- 1 | from typing import Dict, Any, Optional 2 | from .statistics import Statistics 3 | 4 | 5 | class Seller: 6 | __slots__ = ( 7 | "_avatar", 8 | "_away", 9 | "_flags", 10 | "_has_valid_steam_api_key", 11 | "_obfuscated_id", 12 | "_online", 13 | "_stall_public", 14 | "_statistics", 15 | "_steam_id", 16 | "_username", 17 | "_verification_mode" 18 | ) 19 | 20 | def __init__(self, *, data: Dict[str, Any]): 21 | self._avatar = data.get("avatar") 22 | self._away = data.get("away") 23 | self._flags = data.get("flags") 24 | self._has_valid_steam_api_key = data.get("has_valid_steam_api_key") 25 | self._obfuscated_id = data.get("obfuscated_id") 26 | self._online = data.get("online") 27 | self._stall_public = data.get("stall_public") 28 | self._statistics = data.get("statistics") 29 | self._steam_id = data.get("steam_id") 30 | self._username = data.get("username") 31 | self._verification_mode = data.get("verification_mode") 32 | 33 | @property 34 | def avatar(self) -> Optional[str]: 35 | return self._avatar 36 | 37 | @property 38 | def away(self) -> Optional[bool]: 39 | return self._away 40 | 41 | @property 42 | def flags(self) -> Optional[int]: 43 | return self._flags 44 | 45 | @property 46 | def has_valid_steam_api_key(self) -> Optional[bool]: 47 | return self._has_valid_steam_api_key 48 | 49 | @property 50 | def obfuscated_id(self) -> Optional[int]: 51 | return self._obfuscated_id 52 | 53 | @property 54 | def online(self) -> Optional[bool]: 55 | return self._online 56 | 57 | @property 58 | def stall_public(self) -> Optional[bool]: 59 | return self._stall_public 60 | 61 | @property 62 | def statistics(self) -> Optional[Statistics]: 63 | return Statistics(data=self._statistics) 64 | 65 | @property 66 | def steam_id(self) -> Optional[int]: 67 | return self._steam_id 68 | 69 | @property 70 | def username(self) -> Optional[str]: 71 | return self._username 72 | 73 | @property 74 | def verification_mode(self) -> Optional[str]: 75 | return self._verification_mode 76 | -------------------------------------------------------------------------------- /models/listing.py: -------------------------------------------------------------------------------- 1 | from typing import Dict, Any, Optional 2 | from .seller import Seller 3 | from .reference import Reference 4 | from .item import Item 5 | from .auction import AuctionDetails 6 | 7 | 8 | class Listing: 9 | __slots__ = ( 10 | "_id", 11 | "_created_at", 12 | "_type", 13 | "_price", 14 | "_description", 15 | "_state", 16 | "_seller", 17 | "_reference", 18 | "_item", 19 | "_is_seller", 20 | "_min_offer_price", 21 | "_max_offer_discount", 22 | "_is_watchlisted", 23 | "_watchers", 24 | "_auction_details", 25 | "_sold_at" 26 | ) 27 | 28 | def __init__(self, *, data: Dict[str, Any]) -> None: 29 | self._id = data.get("id") 30 | self._created_at = data.get("created_at") 31 | self._type = data.get("type") 32 | self._price = data.get("price") 33 | self._description = data.get("description") 34 | self._state = data.get("state") 35 | self._seller = data.get("seller") 36 | self._reference = data.get("reference") 37 | self._item = data.get("item") 38 | self._is_seller = data.get("is_seller") 39 | self._min_offer_price = data.get("min_offer_price") 40 | self._max_offer_discount = data.get("max_offer_discount") 41 | self._is_watchlisted = data.get("is_watchlisted") 42 | self._watchers = data.get("watchers") 43 | self._auction_details = data.get("auction_details") 44 | self._sold_at = data.get("sold_at") 45 | 46 | @property 47 | def id(self) -> Optional[int]: 48 | return self._id 49 | 50 | @property 51 | def created_at(self) -> Optional[str]: 52 | return self._created_at 53 | 54 | @property 55 | def type(self) -> Optional[str]: 56 | return self._type 57 | 58 | @property 59 | def price(self) -> Optional[float]: 60 | return self._price 61 | 62 | @property 63 | def description(self) -> Optional[str]: 64 | return self._description 65 | 66 | @property 67 | def state(self) -> Optional[str]: 68 | return self._state 69 | 70 | @property 71 | def seller(self) -> Optional[Seller]: 72 | return Seller(data=self._seller) 73 | 74 | @property 75 | def reference(self) -> Optional[Reference]: 76 | return Reference(data=self._reference) 77 | 78 | @property 79 | def item(self) -> Optional[Item]: 80 | return Item(data=self._item) 81 | 82 | @property 83 | def is_seller(self) -> Optional[bool]: 84 | return self._is_seller 85 | 86 | @property 87 | def min_offer_price(self) -> Optional[float]: 88 | return self._min_offer_price 89 | 90 | @property 91 | def max_offer_discount(self) -> Optional[float]: 92 | return self._max_offer_discount 93 | 94 | @property 95 | def is_watchlisted(self) -> Optional[bool]: 96 | return self._is_watchlisted 97 | 98 | @property 99 | def watchers(self) -> Optional[int]: 100 | return self._watchers 101 | 102 | @property 103 | def auction_details(self) -> Optional[AuctionDetails]: 104 | return AuctionDetails(data=self._auction_details) 105 | 106 | @property 107 | def sold_at(self) -> Optional[str]: 108 | return self._sold_at 109 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CSFloat Async API Client (Unofficial) 2 | 3 | An **unofficial**, **asynchronous** Python library for interacting with the [CSFloat](https://csfloat.com) API. This library provides full support for programmatic access to listings, buy orders, user data, exchange rates, and more. All prices returned by the server are in **cents**. 4 | 5 | ## Key Features 6 | 7 | * **Asynchronous design**: Built on `aiohttp` for non-blocking I/O. 8 | * **Fetch listings**: Retrieve detailed listings with filters (price in cents, float, rarity, etc.). 9 | * **Buy orders**: Get and manage buy orders for specific items. 10 | * **User information**: Access your own profile, trades, and stall data. 11 | * **Listing management**: Create, delete, and modify listings and buy orders. 12 | * **Proxy support**: Optional SOCKS4/5 and HTTP(S) proxy support. 13 | * **Error handling**: Clear exceptions with descriptive messages. 14 | 15 | ## Installation 16 | 17 | Install via pip: 18 | 19 | ```bash 20 | pip install csfloat_api 21 | ``` 22 | 23 | ## Quick Start 24 | 25 | ```python 26 | import asyncio 27 | from csfloat_api.csfloat_client import Client 28 | 29 | async def main(): 30 | async with Client(api_key="YOUR_API_KEY") as client: 31 | # Fetch up to 50 listings priced between $1.00 and $10.00 (i.e., 100–1000 cents) 32 | listings = await client.get_all_listings(min_price=100, max_price=1000) 33 | for listing in listings["listings"]: 34 | print(f"ID: {listing.id}, Price: {listing.price} cents, Float: {listing.item.float_value}") 35 | 36 | # Create a buy order for an item 37 | buy_order = await client.create_buy_order( 38 | market_hash_name="AK-47 | Redline (Field-Tested)", 39 | max_price=5000, # 5000 cents = $50.00 40 | quantity=1 41 | ) 42 | print(buy_order) 43 | 44 | asyncio.run(main()) 45 | ``` 46 | 47 | ## Session Management 48 | 49 | The `Client` class manages an underlying HTTP session using `aiohttp.ClientSession`. To ensure that resources are properly released, it is important to close the session when you are done using the client. 50 | 51 | ### Using `async with` 52 | 53 | The recommended way to use the `Client` is with an `async with` statement, which automatically closes the session when the block is exited: 54 | 55 | ```python 56 | async with Client(api_key="YOUR_API_KEY") as client: 57 | # Use the client... 58 | ``` 59 | 60 | ### Manual Session Closing 61 | 62 | If you cannot use `async with`, you can manually close the session using the `close` method: 63 | 64 | ```python 65 | client = Client(api_key="YOUR_API_KEY") 66 | try: 67 | # Use the client... 68 | finally: 69 | await client.close() 70 | ``` 71 | 72 | Failing to close the session may result in warnings about unclosed client sessions and potential resource leaks. 73 | 74 | ## Core Methods 75 | 76 | * `get_exchange_rates()` – Retrieve current exchange rates. 77 | * `get_all_listings(...)` – List items with optional filters (prices in cents). 78 | * `get_specific_listing(listing_id)` – Get detailed info for a specific listing. 79 | * `get_buy_orders(listing_id)` – Retrieve buy orders for a listing. 80 | * `get_my_buy_orders(...)` – List your own buy orders. 81 | * `get_me()` – Fetch authenticated user profile. 82 | * `get_stall(user_id)` – Get a user's stall (listed items). 83 | * `create_listing(asset_id, price, ...)` – Create a new listing (price in cents). 84 | * `create_buy_order(market_hash_name, max_price, quantity)` – Place a buy order. 85 | * `make_offer(listing_id, price)` – Make an offer on a listing. 86 | * `buy_now(total_price, listing_id)` – Instantly buy one or more listings. 87 | * `delete_buy_order(id)` – Cancel an existing buy order. 88 | 89 | For a full list of methods and parameters, refer to the library's source code. 90 | 91 | ## Proxy Support 92 | 93 | Optionally provide a proxy URL in the constructor: 94 | 95 | ```python 96 | client = Client( 97 | api_key="YOUR_API_KEY", 98 | proxy="socks5://user:pass@host:port" 99 | ) 100 | ``` 101 | 102 | ## Error Handling 103 | 104 | The client raises exceptions for HTTP errors with clear messages: 105 | 106 | * **401** Unauthorized – Invalid API key. 107 | * **404** Not Found – Resource missing. 108 | * **429** Too Many Requests – Rate limit exceeded. 109 | * **500** Internal Server Error – Server-side issue. 110 | 111 | ## Contributing 112 | 113 | Contributions are welcome! Please submit issues and pull requests on the [GitHub repository](https://github.com/Rushifakami/csfloat_api). 114 | 115 | ## License 116 | 117 | This project is licensed under the [MIT License](https://opensource.org/licenses/MIT). -------------------------------------------------------------------------------- /models/item.py: -------------------------------------------------------------------------------- 1 | from typing import Dict, Any, List, Optional 2 | from .stickers import Sticker 3 | 4 | 5 | class Item: 6 | __slots__ = ( 7 | "_asset_id", 8 | "_def_index", 9 | "_paint_index", 10 | "_paint_seed", 11 | "_float_value", 12 | "_icon_url", 13 | "_d_param", 14 | "_is_stattrak", 15 | "_is_souvenir", 16 | "_rarity", 17 | "_quality", 18 | "_market_hash_name", 19 | "_low_rank", 20 | "_stickers", 21 | "_tradable", 22 | "_inspect_link", 23 | "_has_screenshot", 24 | "_cs2_screenshot_id", 25 | "_cs2_screenshot_at", 26 | "_is_commodity", 27 | "_type", 28 | "_rarity_name", 29 | "_type_name", 30 | "_item_name", 31 | "_wear_name", 32 | "_description", 33 | "_collection", 34 | "_badges", 35 | "_serialized_inspect", 36 | "_gs_sig" 37 | ) 38 | 39 | def __init__(self, *, data: Dict[str, Any]): 40 | self._asset_id = data.get("asset_id") 41 | self._def_index = data.get("def_index") 42 | self._paint_index = data.get("paint_index") 43 | self._paint_seed = data.get("paint_seed") 44 | self._float_value = data.get("float_value") 45 | self._icon_url = data.get("icon_url") 46 | self._d_param = data.get("d_param") 47 | self._is_stattrak = data.get("is_stattrak") 48 | self._is_souvenir = data.get("is_souvenir") 49 | self._rarity = data.get("rarity") 50 | self._quality = data.get("quality") 51 | self._market_hash_name = data.get("market_hash_name") 52 | self._low_rank = data.get("low_rank") 53 | self._stickers = data.get("stickers") 54 | self._tradable = data.get("tradable") 55 | self._inspect_link = data.get("inspect_link") 56 | self._has_screenshot = data.get("has_screenshot") 57 | self._cs2_screenshot_id = data.get("cs2_screenshot_id") 58 | self._cs2_screenshot_at = data.get("cs2_screenshot_at") 59 | self._is_commodity = data.get("is_commodity") 60 | self._type = data.get("type") 61 | self._rarity_name = data.get("rarity_name") 62 | self._type_name = data.get("rarity_name") 63 | self._item_name = data.get("item_name") 64 | self._wear_name = data.get("wear_name") 65 | self._description = data.get("description") 66 | self._collection = data.get("collection") 67 | self._badges = data.get("badges") 68 | self._serialized_inspect = data.get("serialized_inspect") 69 | self._gs_sig = data.get("gs_sig") 70 | 71 | @property 72 | def asset_id(self) -> Optional[int]: 73 | return self._asset_id 74 | 75 | @property 76 | def def_index(self) -> Optional[int]: 77 | return self._def_index 78 | 79 | @property 80 | def paint_index(self) -> Optional[float]: 81 | return self._paint_index 82 | 83 | @property 84 | def paint_seed(self) -> Optional[float]: 85 | return self._paint_seed 86 | 87 | @property 88 | def float_value(self) -> Optional[float]: 89 | return self._float_value 90 | 91 | @property 92 | def icon_url(self) -> Optional[str]: 93 | return self._icon_url 94 | 95 | @property 96 | def d_param(self) -> Optional[int]: 97 | return self._d_param 98 | 99 | @property 100 | def is_stattrak(self) -> Optional[bool]: 101 | return self._is_stattrak 102 | 103 | @property 104 | def is_souvenir(self) -> Optional[bool]: 105 | return self._is_souvenir 106 | 107 | @property 108 | def rarity(self) -> Optional[str]: 109 | return self._rarity 110 | 111 | @property 112 | def quality(self) -> Optional[int]: 113 | return self._quality 114 | 115 | @property 116 | def market_hash_name(self) -> Optional[str]: 117 | return self._market_hash_name 118 | 119 | @property 120 | def low_rank(self) -> Optional[int]: 121 | return self._low_rank 122 | 123 | @property 124 | def stickers(self) -> Optional[List[Sticker]]: 125 | if self._stickers is not None: 126 | return [Sticker(data=sticker) for sticker in self._stickers] 127 | 128 | @property 129 | def tradable(self) -> Optional[bool]: 130 | return self._tradable 131 | 132 | @property 133 | def inspect_link(self) -> Optional[str]: 134 | return self._inspect_link 135 | 136 | @property 137 | def has_screenshot(self) -> Optional[bool]: 138 | return self._has_screenshot 139 | 140 | @property 141 | def cs2_screenshot_id(self) -> Optional[int]: 142 | return self._cs2_screenshot_id 143 | 144 | @property 145 | def cs2_screenshot_at(self) -> Optional[str]: 146 | return self._cs2_screenshot_at 147 | 148 | @property 149 | def is_commodity(self) -> Optional[bool]: 150 | return self._is_commodity 151 | 152 | @property 153 | def type(self) -> Optional[str]: 154 | return self._type 155 | 156 | @property 157 | def rarity_name(self) -> Optional[str]: 158 | return self._rarity_name 159 | 160 | @property 161 | def type_name(self) -> Optional[str]: 162 | return self._type_name 163 | 164 | @property 165 | def item_name(self) -> Optional[str]: 166 | return self._item_name 167 | 168 | @property 169 | def wear_name(self) -> Optional[str]: 170 | return self._wear_name 171 | 172 | @property 173 | def description(self) -> Optional[str]: 174 | return self._description 175 | 176 | @property 177 | def collection(self) -> Optional[str]: 178 | return self._collection 179 | 180 | @property 181 | def badges(self) -> Optional[list]: 182 | return self._badges 183 | 184 | @property 185 | def serialized_inspect(self) -> Optional[str]: 186 | return self._serialized_inspect 187 | 188 | @property 189 | def gs_sig(self) -> Optional[str]: 190 | return self._gs_sig 191 | -------------------------------------------------------------------------------- /models/me.py: -------------------------------------------------------------------------------- 1 | from typing import Optional, Dict, Any 2 | 3 | 4 | class Statistics: 5 | __slots__ = ( 6 | "_total_sales", 7 | "_total_purchases", 8 | "_median_trade_time", 9 | "_total_avoided_trades", 10 | "_total_failed_trades", 11 | "_total_verified_trades", 12 | "_total_trades" 13 | ) 14 | 15 | def __init__(self, *, data: Dict[str, Any]): 16 | self._total_sales = data.get("total_sales") 17 | self._total_purchases = data.get("total_purchases") 18 | self._median_trade_time = data.get("median_trade_time") 19 | self._total_avoided_trades = data.get("total_avoided_trades") 20 | self._total_failed_trades = data.get("total_failed_trades") 21 | self._total_verified_trades = data.get("total_verified_trades") 22 | self._total_trades = data.get("total_trades") 23 | 24 | @property 25 | def total_sales(self) -> Optional[int]: 26 | return self._total_sales 27 | 28 | @property 29 | def total_purchases(self) -> Optional[int]: 30 | return self._total_purchases 31 | 32 | @property 33 | def median_trade_time(self) -> Optional[int]: 34 | return self._median_trade_time 35 | 36 | @property 37 | def total_avoided_trades(self) -> Optional[int]: 38 | return self._total_avoided_trades 39 | 40 | @property 41 | def total_failed_trades(self) -> Optional[int]: 42 | return self._total_failed_trades 43 | 44 | @property 45 | def total_verified_trades(self) -> Optional[int]: 46 | return self._total_verified_trades 47 | 48 | @property 49 | def total_trades(self) -> Optional[int]: 50 | return self._total_trades 51 | 52 | 53 | class Preferences: 54 | __slots__ = ( 55 | "_offers_enabled", 56 | "_max_offer_discount" 57 | ) 58 | 59 | def __init__(self, *, data: Dict[str, Any]): 60 | self._offers_enabled = data.get("offers_enabled") 61 | self._max_offer_discount = data.get("max_offer_discount") 62 | 63 | @property 64 | def offers_enabled(self) -> Optional[bool]: 65 | return self._offers_enabled 66 | 67 | @property 68 | def max_offer_discount(self) -> Optional[int]: 69 | return self._max_offer_discount 70 | 71 | 72 | class FirebaseMessaging: 73 | __slots__ = ( 74 | "_platform", 75 | "_last_updated" 76 | ) 77 | 78 | def __init__(self, *, data: Dict[str, Any]): 79 | self._platform = data.get("platform") 80 | self._last_updated = data.get("last_updated") 81 | 82 | @property 83 | def platform(self) -> Optional[int]: 84 | return self._platform 85 | 86 | @property 87 | def last_updated(self) -> Optional[str]: 88 | return self._last_updated 89 | 90 | 91 | class User: 92 | __slots__ = ( 93 | "_steam_id", 94 | "_username", 95 | "_flags", 96 | "_avatar", 97 | "_background_url", 98 | "_email", 99 | "_balance", 100 | "_pending_balance", 101 | "_stall_public", 102 | "_away", 103 | "_trade_token", 104 | "_payment_accounts", 105 | "_api_key", 106 | "_statistics", 107 | "_preferences", 108 | "_know_your_customer", 109 | "_extension_setup_at", 110 | "_firebase_messaging", 111 | "_stripe_connect", 112 | "_has_valid_steam_api_key", 113 | "_obfuscated_id", 114 | "_online", 115 | "_fee", 116 | "_withdraw_fee", 117 | "_subscriptions", 118 | "_has_2fa" 119 | ) 120 | 121 | def __init__(self, *, data: Dict[str, Any]): 122 | self._steam_id = data.get("steam_id") 123 | self._username = data.get("username") 124 | self._flags = data.get("flags") 125 | self._avatar = data.get("avatar") 126 | self._background_url = data.get("background_url") 127 | self._email = data.get("email") 128 | self._balance = data.get("balance") 129 | self._pending_balance = data.get("pending_balance") 130 | self._stall_public = data.get("stall_public") 131 | self._away = data.get("away") 132 | self._trade_token = data.get("trade_token") 133 | self._payment_accounts = data.get("payment_accounts") 134 | self._api_key = data.get("api_key", "") 135 | self._statistics = data.get("statistics") 136 | self._preferences = data.get("preferences") 137 | self._know_your_customer = data.get("know_your_customer") 138 | self._extension_setup_at = data.get("extension_setup_at") 139 | self._firebase_messaging = data.get("firebase_messaging") 140 | self._stripe_connect = data.get("stripe_connect") 141 | self._has_valid_steam_api_key = data.get("has_valid_steam_api_key") 142 | self._obfuscated_id = data.get("obfuscated_id") 143 | self._online = data.get("online") 144 | self._fee = data.get("fee") 145 | self._withdraw_fee = data.get("withdraw_fee") 146 | self._subscriptions = data.get("subscriptions") 147 | self._has_2fa = data.get("has_2fa") 148 | 149 | @property 150 | def steam_id(self) -> Optional[str]: 151 | return self._steam_id 152 | 153 | @property 154 | def username(self) -> Optional[str]: 155 | return self._username 156 | 157 | @property 158 | def flags(self) -> Optional[int]: 159 | return self._flags 160 | 161 | @property 162 | def avatar(self) -> Optional[str]: 163 | return self._avatar 164 | 165 | @property 166 | def background_url(self) -> Optional[str]: 167 | return self._background_url 168 | 169 | @property 170 | def email(self) -> Optional[str]: 171 | return self._email 172 | 173 | @property 174 | def balance(self) -> Optional[int]: 175 | return self._balance 176 | 177 | @property 178 | def pending_balance(self) -> Optional[int]: 179 | return self._pending_balance 180 | 181 | @property 182 | def stall_public(self) -> Optional[bool]: 183 | return self._stall_public 184 | 185 | @property 186 | def away(self) -> Optional[bool]: 187 | return self._away 188 | 189 | @property 190 | def trade_token(self) -> Optional[str]: 191 | return self._trade_token 192 | 193 | @property 194 | def payment_accounts(self) -> Optional[dict]: 195 | return self._payment_accounts 196 | 197 | @property 198 | def api_key(self) -> Optional[str]: 199 | return self._api_key 200 | 201 | @property 202 | def statistics(self) -> Optional[Statistics]: 203 | return Statistics(data=self._statistics) 204 | 205 | @property 206 | def preferences(self) -> Optional[Preferences]: 207 | return Preferences(data=self._preferences) 208 | 209 | @property 210 | def know_your_customer(self) -> Optional[str]: 211 | return self._know_your_customer 212 | 213 | @property 214 | def extension_setup_at(self) -> Optional[str]: 215 | return self._extension_setup_at 216 | 217 | @property 218 | def firebase_messaging(self) -> Optional[FirebaseMessaging]: 219 | return FirebaseMessaging(data=self._firebase_messaging) 220 | 221 | @property 222 | def stripe_connect(self) -> Optional[set]: 223 | return self._stripe_connect 224 | 225 | @property 226 | def has_valid_steam_api_key(self) -> Optional[bool]: 227 | return self._has_valid_steam_api_key 228 | 229 | @property 230 | def obfuscated_id(self) -> Optional[str]: 231 | return self._obfuscated_id 232 | 233 | @property 234 | def online(self) -> Optional[bool]: 235 | return self._online 236 | 237 | @property 238 | def fee(self) -> Optional[float]: 239 | return self._fee 240 | 241 | @property 242 | def withdraw_fee(self) -> Optional[float]: 243 | return self._withdraw_fee 244 | 245 | @property 246 | def subscriptions(self) -> Optional[list]: 247 | return self._subscriptions 248 | 249 | @property 250 | def has_2fa(self) -> Optional[bool]: 251 | return self._has_2fa 252 | 253 | 254 | class Me: 255 | __slots__ = ( 256 | "_user", 257 | "_pending_offers", 258 | "_actionable_trades" 259 | ) 260 | 261 | def __init__(self, *, data: Dict[str, Any]): 262 | self._user = data.get("user") 263 | self._pending_offers = data.get("pending_offers") 264 | self._actionable_trades = data.get("actionable_trades") 265 | 266 | @property 267 | def user(self) -> Optional[User]: 268 | return User(data=self._user) 269 | 270 | @property 271 | def pending_offers(self) -> Optional[int]: 272 | return self._pending_offers 273 | 274 | @property 275 | def actionable_trades(self) -> Optional[int]: 276 | return self._actionable_trades 277 | -------------------------------------------------------------------------------- /csfloat_client.py: -------------------------------------------------------------------------------- 1 | import aiohttp 2 | import re 3 | from aiohttp_socks.connector import ProxyConnector 4 | from typing import Iterable, Union, Optional, Dict, List 5 | from .models.listing import Listing 6 | from .models.buy_orders import BuyOrders 7 | from .models.me import Me 8 | from .models.stall import Stall 9 | 10 | __all__ = "Client" 11 | 12 | _API_URL = 'https://csfloat.com/api/v1' 13 | 14 | 15 | class Client: 16 | _SUPPORTED_METHODS = ('GET', 'POST', 'DELETE', 'PATCH') 17 | ERROR_MESSAGES = { 18 | 401: 'Unauthorized -- Your API key is wrong.', 19 | 403: 'Forbidden -- The requested resource is hidden for administrators only.', 20 | 404: 'Not Found -- The specified resource could not be found.', 21 | 405: 'Method Not Allowed -- You tried to access a resource with an invalid method.', 22 | 406: 'Not Acceptable -- You requested a format that isn\'t json.', 23 | 410: 'Gone -- The requested resource has been removed from our servers.', 24 | 418: 'I\'m a teapot.', 25 | 429: 'Too Many Requests -- You\'re requesting too many resources! Slow down!', 26 | 500: 'Internal Server Error -- We had a problem with our server. Try again later.', 27 | 503: 'Service Unavailable -- We\'re temporarily offline for maintenance. Please try again later.', 28 | } 29 | 30 | __slots__ = ( 31 | "API_KEY", 32 | "proxy", 33 | "_headers", 34 | "_connector", 35 | "_session" 36 | ) 37 | 38 | def __init__(self, api_key: str, proxy: str = None) -> None: 39 | self.API_KEY = api_key 40 | self.proxy = proxy 41 | self._validate_proxy() 42 | self._headers = { 43 | 'Authorization': self.API_KEY 44 | } 45 | self._connector = ProxyConnector.from_url(self.proxy, ttl_dns_cache=300) if self.proxy else aiohttp.TCPConnector( 46 | resolver=aiohttp.resolver.AsyncResolver(), 47 | limit_per_host=50 48 | ) 49 | self._session = aiohttp.ClientSession(connector=self._connector, headers=self._headers) 50 | 51 | async def __aenter__(self): 52 | return self 53 | 54 | async def __aexit__(self, exc_type, exc, tb): 55 | await self._session.close() 56 | 57 | async def close(self): 58 | await self._session.close() 59 | 60 | def _validate_proxy(self) -> None: 61 | """Validates the proxy URL format. 62 | 63 | Raises: 64 | ValueError: If the proxy URL format is invalid or the port is out of range. 65 | """ 66 | if not self.proxy: 67 | return # Proxy is not set, which is acceptable 68 | # Regular expression to check the format: socks5://user:pass@host:port, etc. 69 | pattern = r'^(socks5|socks4|http|https)://(\w+:\w+@)?[\w.-]+:\d+$' 70 | if not re.match(pattern, self.proxy): 71 | raise ValueError( 72 | f"Invalid proxy URL format: {self.proxy}. Expected format like 'socks5://user:pass@host:port'") 73 | # Check the port 74 | port = self.proxy.split(':')[-1] 75 | if not port.isdigit() or not (1 <= int(port) <= 65535): 76 | raise ValueError(f"Invalid port in proxy URL: {port}") 77 | 78 | async def _request(self, method: str, parameters: str, json_data=None) -> Optional[dict]: 79 | if method not in self._SUPPORTED_METHODS: 80 | raise ValueError('Unsupported HTTP method.') 81 | 82 | url = f'{_API_URL}{parameters}' 83 | 84 | async with self._session.request(method=method, url=url, ssl=False, json=json_data) as response: 85 | if response.status in self.ERROR_MESSAGES: 86 | raise Exception(self.ERROR_MESSAGES[response.status]) 87 | 88 | if response.status != 200: 89 | try: 90 | error_details = await response.json() 91 | except Exception: 92 | error_details = await response.text() 93 | raise Exception(f'Error: {response.status}\nResponse Body: {error_details}') 94 | 95 | if response.content_type != 'application/json': 96 | raise Exception(f"Expected JSON, got {response.content_type}") 97 | 98 | return await response.json() 99 | 100 | def _validate_category(self, category: int) -> None: 101 | if category not in (0, 1, 2, 3): 102 | raise ValueError(f'Unknown category parameter "{category}"') 103 | 104 | def _validate_sort_by(self, sort_by: str) -> None: 105 | valid_sort_by = ( 106 | 'lowest_price', 'highest_price', 'most_recent', 'expires_soon', 107 | 'lowest_float', 'highest_float', 'best_deal', 'highest_discount', 108 | 'float_rank', 'num_bids' 109 | ) 110 | if sort_by not in valid_sort_by: 111 | raise ValueError(f'Unknown sort_by parameter "{sort_by}"') 112 | 113 | def _validate_type(self, type_: str) -> None: 114 | if type_ not in ('buy_now', 'auction'): 115 | raise ValueError(f'Unknown type parameter "{type_}"') 116 | 117 | def _validate_role(self, role: str) -> None: 118 | valid_roles = ("seller", "buyer") 119 | if role not in valid_roles: 120 | raise ValueError(f'Unknown role parameter: {role}') 121 | 122 | async def get_exchange_rates(self) -> Optional[dict]: 123 | parameters = "/meta/exchange-rates" 124 | method = "GET" 125 | 126 | response = await self._request(method=method, parameters=parameters) 127 | return response 128 | 129 | async def get_me(self, *, raw_response: bool = False) -> Optional[Me]: 130 | parameters = "/me" 131 | method = "GET" 132 | 133 | response = await self._request(method=method, parameters=parameters) 134 | 135 | if raw_response: 136 | return response 137 | 138 | return Me(data=response) 139 | 140 | async def get_transactions(self, page: int = 0, limit: int = 10): 141 | parameters = f"/me/transactions?page={page}&limit={limit}&order=desc" 142 | method = "GET" 143 | response = await self._request(method=method, parameters=parameters) 144 | return response 145 | 146 | async def get_account_standing(self): 147 | parameters = "/me/account-standing" 148 | method = "GET" 149 | response = await self._request(method=method, parameters=parameters) 150 | return response 151 | 152 | async def get_location(self) -> Optional[dict]: 153 | parameters = "/meta/location" 154 | method = "GET" 155 | 156 | response = await self._request(method=method, parameters=parameters) 157 | return response 158 | 159 | async def get_pending_trades( 160 | self, limit: int = 500, page: int = 0 161 | ) -> Optional[dict]: 162 | parameters = f"/me/trades?state=pending&limit={limit}&page={page}" 163 | method = "GET" 164 | 165 | response = await self._request(method=method, parameters=parameters) 166 | return response 167 | 168 | async def get_similar( 169 | self, *, listing_id: int, raw_response: bool = False 170 | ) -> Union[Iterable[Listing], dict]: 171 | parameters = f"/listings/{listing_id}/similar" 172 | method = "GET" 173 | 174 | response = await self._request(method=method, parameters=parameters) 175 | 176 | if raw_response: 177 | return response 178 | 179 | listings = [ 180 | Listing(data=item) for item in response 181 | ] 182 | 183 | return listings 184 | 185 | async def get_buy_orders( 186 | self, *, listing_id: int, limit: int = 10, raw_response: bool = False 187 | ) -> Optional[list[BuyOrders]]: 188 | parameters = f"/listings/{listing_id}/buy-orders?limit={limit}" 189 | method = "GET" 190 | 191 | response = await self._request(method=method, parameters=parameters) 192 | 193 | if raw_response: 194 | return response 195 | 196 | listings = [ 197 | BuyOrders(data=item) for item in response 198 | ] 199 | 200 | return listings 201 | 202 | async def get_my_buy_orders(self, *, page: int = 0, limit: int = 10): 203 | parameters = f"/me/buy-orders?page={page}&limit={limit}&order=desc" 204 | method = "GET" 205 | response = await self._request(method=method, parameters=parameters) 206 | return response 207 | 208 | async def get_sales(self, market_hash_name: str, paint_index: int = None): 209 | parameters = f"/history/{market_hash_name}/sales" 210 | if paint_index is not None: 211 | parameters += f"?paint_index={paint_index}" 212 | method = "GET" 213 | response = await self._request(method=method, parameters=parameters) 214 | return response 215 | 216 | async def get_all_listings( 217 | self, 218 | *, 219 | min_price: Optional[int] = None, 220 | max_price: Optional[int] = None, 221 | cursor: Optional[str] = None, 222 | limit: int = 50, 223 | sort_by: str = 'best_deal', 224 | category: int = 0, 225 | def_index: Optional[Union[int, Iterable[int]]] = None, 226 | min_float: Optional[float] = None, 227 | max_float: Optional[float] = None, 228 | rarity: Optional[str] = None, 229 | paint_seed: Optional[int] = None, 230 | paint_index: Optional[int] = None, 231 | user_id: Optional[str] = None, 232 | collection: Optional[str] = None, 233 | market_hash_name: Optional[str] = None, 234 | type_: str = 'buy_now', 235 | raw_response: bool = False 236 | ) -> Union[Dict[str, Union[List[Listing], Optional[str]]], dict]: 237 | """ 238 | :param min_price: Only include listings have a price higher than this (in cents) 239 | :param max_price: Only include listings have a price lower than this (in cents) 240 | :param cursor: Cursor to get the next page (issued by the server) 241 | :param limit: How many listings to return. Max of 50 242 | :param sort_by: How to order the listings 243 | :param category: Can be one of: 0 = any, 1 = normal, 2 = stattrak, 3 = souvenir 244 | :param def_index: Only include listings that have one of the given def index(es) 245 | :param min_float: Only include listings that have a float higher than this 246 | :param max_float: Only include listings that have a float lower than this 247 | :param rarity: Only include listings that have this rarity 248 | :param paint_seed: Only include listings that have this paint seed 249 | :param paint_index: Only include listings that have this paint index 250 | :param user_id: Only include listings from this SteamID64 251 | :param collection: Only include listings from this collection 252 | :param market_hash_name: Only include listings that have this market hash name 253 | :param type_: Either buy_now or auction 254 | :param raw_response: Returns the raw response from the API 255 | :return: If raw_response is True, returns the full response as a dict. 256 | Otherwise, returns a dict with the following structure: 257 | { 258 | "listings": List[Listing], # list of parsed listing objects 259 | "cursor": Optional[str] # cursor string for pagination 260 | } 261 | """ 262 | self._validate_category(category) 263 | self._validate_sort_by(sort_by) 264 | self._validate_type(type_) 265 | 266 | parameters = ( 267 | f'/listings?limit={limit}&sort_by={sort_by}' 268 | f'&category={category}&type={type_}' 269 | ) 270 | if cursor is not None: 271 | parameters += f'&cursor={cursor}' 272 | if min_price is not None: 273 | parameters += f'&min_price={min_price}' 274 | if max_price is not None: 275 | parameters += f'&max_price={max_price}' 276 | if def_index is not None: 277 | if isinstance(def_index, Iterable) and not isinstance(def_index, str): 278 | def_index = ','.join(map(str, def_index)) 279 | parameters += f'&def_index={def_index}' 280 | if min_float is not None: 281 | parameters += f'&min_float={min_float}' 282 | if max_float is not None: 283 | parameters += f'&max_float={max_float}' 284 | if rarity is not None: 285 | parameters += f'&rarity={rarity}' 286 | if paint_seed is not None: 287 | parameters += f'&paint_seed={paint_seed}' 288 | if paint_index is not None: 289 | parameters += f'&paint_index={paint_index}' 290 | if user_id is not None: 291 | parameters += f'&user_id={user_id}' 292 | if collection is not None: 293 | parameters += f'&collection={collection}' 294 | if market_hash_name is not None: 295 | parameters += f'&market_hash_name={market_hash_name}' 296 | 297 | method = 'GET' 298 | response = await self._request(method=method, parameters=parameters) 299 | 300 | if raw_response: 301 | return response 302 | listings = [Listing(data=item) for item in response["data"]] 303 | return {"listings": listings, "cursor": response.get("cursor")} 304 | 305 | async def get_specific_listing( 306 | self, listing_id: int, *, raw_response: bool = False 307 | ) -> Union[Listing, dict]: 308 | parameters = f'/listings/{listing_id}' 309 | method = 'GET' 310 | 311 | response = await self._request(method=method, parameters=parameters) 312 | 313 | if raw_response: 314 | return response 315 | 316 | return Listing(data=response) 317 | 318 | async def get_stall( 319 | self, user_id: int, *, limit: int = 40, raw_response: bool = False 320 | ) -> Optional[Stall]: 321 | """ 322 | :param user_id: The ID of the user whose stall information is to be retrieved 323 | :param limit: The maximum number of listings to return. Defaults to 40. 324 | :param raw_response: Returns the raw response from the API 325 | :return: Optional[Stall]: A Stall object containing the user's listings if `raw_response` is False. 326 | """ 327 | parameters = f'/users/{user_id}/stall?limit={limit}' 328 | method = 'GET' 329 | 330 | response = await self._request(method=method, parameters=parameters) 331 | 332 | if raw_response: 333 | return response 334 | 335 | return Stall(data=response) 336 | 337 | async def get_inventory(self): 338 | parameters = f"/me/inventory" 339 | method = "GET" 340 | response = await self._request(method=method, parameters=parameters) 341 | return response 342 | 343 | async def get_watchlist(self, limit: int = 40): 344 | parameters = f"/me/watchlist?limit={limit}" 345 | method = "GET" 346 | response = await self._request(method=method, parameters=parameters) 347 | return response 348 | 349 | async def get_offers(self, limit: int = 40): 350 | parameters = f"/me/offers-timeline?limit={limit}" 351 | method = "GET" 352 | response = await self._request(method=method, parameters=parameters) 353 | return response 354 | 355 | async def get_trade_history(self, role: str = "seller", limit: int = 30, page: int = 0): 356 | self._validate_role(role) 357 | parameters = f"/me/trades?role={role}&state=failed,cancelled,verified&limit={limit}&page={page}" 358 | method = "GET" 359 | response = await self._request(method=method, parameters=parameters) 360 | return response 361 | 362 | async def delete_listing(self, *, listing_id: int): 363 | parameters = f"/listings/{listing_id}" 364 | method = "DELETE" 365 | response = await self._request(method=method, parameters=parameters) 366 | return response 367 | 368 | async def delete_buy_order(self, *, id: int): 369 | parameters = f"/buy-orders/{id}" 370 | method = "DELETE" 371 | response = await self._request(method=method, parameters=parameters) 372 | return response 373 | 374 | async def delete_watchlist(self, *, id: int): 375 | parameters = f"/listings/{id}/watchlist" 376 | method = "DELETE" 377 | response = await self._request(method=method, parameters=parameters) 378 | return response 379 | 380 | 381 | async def create_listing( 382 | self, 383 | *, 384 | asset_id: str, 385 | price: float, 386 | type_: str = "buy_now", 387 | max_offer_discount: Optional[int] = None, 388 | reserve_price: Optional[float] = None, 389 | duration_days: Optional[int] = None, 390 | description: str = "", 391 | private: bool = False, 392 | ) -> Optional[dict]: 393 | """ 394 | :param asset_id: The ID of the item to list 395 | :param price: The buy_now price or the current bid or reserve price on an auction 396 | :param type_: Either 'buy_now' or 'auction' (default: 'buy_now') 397 | :param max_offer_discount: The max discount for an offer (optional) 398 | :param reserve_price: The starting price for an auction (required if type is 'auction') 399 | :param duration_days: The auction duration in days (required if type is 'auction') 400 | :param description: User-defined description (optional) 401 | :param private: If true, will hide the listing from public searches (optional) 402 | :return: The response from the API 403 | """ 404 | self._validate_type(type_) 405 | 406 | parameters = "/listings" 407 | method = "POST" 408 | 409 | json_data = { 410 | "asset_id": asset_id, 411 | "price": price, 412 | "type": type_, 413 | "description": description, 414 | "private": private 415 | } 416 | 417 | # Add optional parameters if provided 418 | if max_offer_discount is not None: 419 | json_data["max_offer_discount"] = max_offer_discount 420 | if reserve_price is not None: 421 | json_data["reserve_price"] = reserve_price 422 | if duration_days is not None: 423 | json_data["duration_days"] = duration_days 424 | 425 | response = await self._request(method=method, parameters=parameters, json_data=json_data) 426 | return response 427 | 428 | async def create_buy_order( 429 | self, 430 | *, 431 | market_hash_name: str, 432 | max_price: int, 433 | quantity: int 434 | ) -> Optional[dict]: 435 | parameters = "/buy-orders" 436 | method = "POST" 437 | json_data = { 438 | "market_hash_name": market_hash_name, 439 | "max_price": max_price, 440 | "quantity": quantity 441 | } 442 | response = await self._request(method=method, parameters=parameters, json_data=json_data) 443 | return response 444 | 445 | async def make_offer( 446 | self, *, listing_id: int, price: int 447 | ) -> Optional[dict]: 448 | parameters = "/offers" 449 | method = "POST" 450 | json_data = { 451 | "contract_id": str(listing_id), 452 | "price": price, 453 | "cancel_previous_offer": False 454 | } 455 | response = await self._request(method=method, parameters=parameters, json_data=json_data) 456 | return response 457 | 458 | async def buy_now( 459 | self, *, total_price: int, listing_id: str 460 | ) -> Optional[dict]: 461 | parameters = "/listings/buy" 462 | method = "POST" 463 | 464 | json_data = { 465 | "total_price": total_price, 466 | "contract_ids": [str(listing_id)] 467 | } 468 | response = await self._request(method=method, parameters=parameters, json_data=json_data) 469 | return response 470 | 471 | async def accept_sale(self, *, trade_ids: list[str]): 472 | parameters = "trades/bulk/accept" 473 | method = "POST" 474 | 475 | json_data = { 476 | "trade_ids": trade_ids 477 | } 478 | 479 | response = await self._request(method=method, parameters=parameters, json_data=json_data) 480 | return response 481 | 482 | async def update_listing_price(self, *, listing_id: int, price: int): 483 | parameters = f"/listings/{listing_id}" 484 | method = "PATCH" 485 | 486 | json_data = { 487 | "price": price 488 | } 489 | response = await self._request(method=method, parameters=parameters, json_data=json_data) 490 | return response 491 | --------------------------------------------------------------------------------