{"id":25077387,"url":"https://github.com/nandascott/scrython","last_synced_at":"2026-01-24T22:28:45.226Z","repository":{"id":46089588,"uuid":"116210203","full_name":"NandaScott/Scrython","owner":"NandaScott","description":"A python wrapper for the Scryfall API","archived":false,"fork":false,"pushed_at":"2025-04-20T10:28:13.000Z","size":311,"stargazers_count":147,"open_issues_count":11,"forks_count":23,"subscribers_count":12,"default_branch":"main","last_synced_at":"2025-04-20T11:59:12.341Z","etag":null,"topics":["api-wrapper","json-api","magic-the-gathering","mtg-api","python","python-3","python-library","scryfall-api","wrapper"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/NandaScott.png","metadata":{"files":{"readme":"README.md","changelog":"Changelog.md","contributing":"Contributing.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2018-01-04T03:30:21.000Z","updated_at":"2025-03-22T23:16:02.000Z","dependencies_parsed_at":"2024-01-29T12:40:45.213Z","dependency_job_id":"36bc228e-1f87-4fb1-9329-89ab281764b8","html_url":"https://github.com/NandaScott/Scrython","commit_stats":{"total_commits":289,"total_committers":16,"mean_commits":18.0625,"dds":0.2698961937716263,"last_synced_commit":"ef858dbb8d024b597960ff5416410b71061c3860"},"previous_names":[],"tags_count":19,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NandaScott%2FScrython","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NandaScott%2FScrython/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NandaScott%2FScrython/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NandaScott%2FScrython/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/NandaScott","download_url":"https://codeload.github.com/NandaScott/Scrython/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254501548,"owners_count":22081526,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["api-wrapper","json-api","magic-the-gathering","mtg-api","python","python-3","python-library","scryfall-api","wrapper"],"created_at":"2025-02-07T02:24:57.482Z","updated_at":"2026-01-24T22:28:45.218Z","avatar_url":"https://github.com/NandaScott.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Scrython\n\nScrython is a wrapper for the Scryfall API, designed for an easier use.\n\n[Here is a link to the Scryfall API documentation.](https://scryfall.com/docs/api)\n\n## Installation\n\nScrython is available in PyPI, and requires no other dependencies.\n\n```python\npip install scrython\n```\n\n## ⚠️ Important: Rate Limiting\n\n**Good news!** Scrython 2.0 now includes **built-in rate limiting** enabled by default, enforcing Scryfall's 10 requests/second guideline automatically. You no longer need to manually add delays between requests.\n\n**Scryfall requires 50-100 milliseconds delay between requests** (~10 requests/second maximum).\n\n### Automatic Rate Limiting (Default):\n\n```python\nimport scrython\n\n# Rate limiting is automatic! No delays needed\ncards_to_fetch = ['Lightning Bolt', 'Counterspell', 'Black Lotus']\n\nfor card_name in cards_to_fetch:\n    card = scrython.cards.Named(fuzzy=card_name)  # Automatically rate limited\n    print(f\"{card.name} - {card.set}\")\n```\n\n### Custom Rate Limits:\n\n```python\n# Use a slower rate (5 requests/second)\ncard = scrython.cards.Named(fuzzy='Lightning Bolt', rate_limit_per_second=5)\n\n# Disable rate limiting (use with caution!)\ncard = scrython.cards.Named(fuzzy='Lightning Bolt', rate_limit=False)\n```\n\n### Legacy Code (Manual Rate Limiting):\n\nIf you prefer manual rate limiting or need finer control:\n\n```python\nimport scrython\nimport time\n\n# Disable automatic rate limiting and use manual delays\nfor card_name in cards_to_fetch:\n    card = scrython.cards.Named(fuzzy=card_name, rate_limit=False)\n    print(f\"{card.name} - {card.set}\")\n    time.sleep(0.1)  # 100ms delay\n```\n\n### Better: Use Bulk Data for Large Datasets\n\nFor large-scale data processing, use Scryfall's bulk data downloads instead:\n\n```python\nimport scrython\n\n# Download all unique cards at once\nbulk = scrython.bulk_data.ByType(type='oracle_cards')\ncards = bulk.download()\n\n# Process all cards locally without rate limits!\nfor card in cards:\n    print(f\"{card['name']} - {card['set']}\")\n\n# Bulk data files are updated every 12 hours\n```\n\n### Built-in Caching\n\nScrython 2.0 includes built-in caching with TTL (time-to-live) support:\n\n```python\nimport scrython\n\n# Enable caching with 1-hour TTL (default)\ncard = scrython.cards.Named(fuzzy='Lightning Bolt', cache=True)\n\n# First call makes API request\ncard1 = scrython.cards.Named(fuzzy='Lightning Bolt', cache=True)\n\n# Second call returns cached result (no API request!)\ncard2 = scrython.cards.Named(fuzzy='Lightning Bolt', cache=True)\n\n# Custom TTL (in seconds)\ncard = scrython.cards.Named(fuzzy='Lightning Bolt', cache=True, cache_ttl=7200)  # 2 hours\n```\n\n**Note:** Card prices become unreliable after 24 hours. Consider shorter TTLs for price-sensitive applications.\n\n### Legacy Caching (functools.lru_cache):\n\nYou can still use Python's built-in caching if preferred:\n\n```python\nfrom functools import lru_cache\nimport scrython\n\n@lru_cache(maxsize=1000)\ndef get_card_by_name(name: str):\n    \"\"\"Cache card lookups to avoid duplicate requests.\"\"\"\n    return scrython.cards.Named(fuzzy=name, rate_limit=False)\n\ncard1 = get_card_by_name('Lightning Bolt')\ncard2 = get_card_by_name('Lightning Bolt')  # Cached\n```\n\n### Custom User-Agent (Recommended)\n\nScryfall requests that applications identify themselves with a custom User-Agent:\n\n```python\nfrom scrython.base import ScrythonRequestHandler\nimport scrython\n\n# Set custom User-Agent for your application\nScrythonRequestHandler.set_user_agent('MyMTGApp/1.0 (contact@example.com)')\n\n# All subsequent requests will use your custom User-Agent\ncard = scrython.cards.Named(fuzzy='Black Lotus')\n```\n\n## Complete Usage Examples\n\n### Basic Card Lookup\n\n```python\nimport scrython\n\n# Fuzzy name search (handles typos)\ncard = scrython.cards.Named(fuzzy='Light Bolt')\nprint(card.name)  # \"Lightning Bolt\"\nprint(card.mana_cost)  # \"{R}\"\nprint(card.type_line)  # \"Instant\"\nprint(card.oracle_text)\n\n# Exact name match\ncard = scrython.cards.Named(exact='Black Lotus')\nprint(card.prices)\n# {'usd': '25000.00', 'usd_foil': None, ...}\n```\n\n### Advanced Search\n\n```python\n# Search with Scryfall syntax\nresults = scrython.cards.Search(q='type:creature cmc:1 color:red')\n\nprint(f\"Found {results.total_cards} cards\")\n\nfor card in results.data:\n    print(f\"{card.name} - {card.set_name}\")\n\n# Handle pagination\nif results.has_more:\n    print(\"More results available - implement pagination as needed\")\n```\n\n### Getting Specific Cards\n\n```python\n# By set code and collector number\ncard = scrython.cards.ByCodeNumber(code='znr', number='123')\n\n# By various IDs\ncard = scrython.cards.ByMultiverseId(id=456789)\ncard = scrython.cards.ByMTGOId(id=67890)\ncard = scrython.cards.ById(id='5f8287b1-5bb6-4e8f-9d78-8f3e3b3e1c6d')\n\n# Get random card\ncard = scrython.cards.Random()\ncard = scrython.cards.Random(q='rarity:mythic')  # Random mythic\n```\n\n### Working with Sets\n\n```python\n# Get all sets\nall_sets = scrython.sets.All()\n\nfor set_obj in all_sets.data:\n    print(f\"{set_obj.name} ({set_obj.code}) - {set_obj.card_count} cards\")\n\n# Get specific set\nset_obj = scrython.sets.ByCode(code='znr')\nprint(f\"{set_obj.name} released on {set_obj.released_at}\")\nprint(f\"Set type: {set_obj.set_type}\")\n```\n\n### Bulk Data Download\n\nBulk data files contain all Magic cards and are updated every 12 hours. This is the recommended approach for processing large datasets, as it avoids rate limits entirely.\n\n```python\nimport scrython\n\n# Get all bulk data options\nall_bulk = scrython.bulk_data.All()\n\nfor bulk in all_bulk.data:\n    print(f\"{bulk.name}: {bulk.description}\")\n    print(f\"Size: {bulk.size / 1_000_000:.1f} MB\")\n\n# Download oracle cards (all unique cards with Oracle text)\noracle_cards = scrython.bulk_data.ByType(type='oracle_cards')\n\n# Option 1: Download and return data in memory\ncards = oracle_cards.download()\nprint(f\"Downloaded {len(cards)} cards\")\n\n# Process without rate limits!\nfor card in cards:\n    if 'Lightning' in card['name']:\n        print(card['name'])\n\n# Option 2: Save to file\noracle_cards.download(filepath='oracle_cards.json')\nprint(\"Bulk data saved to oracle_cards.json\")\n\n# Option 3: Save without returning data (memory efficient)\noracle_cards.download(filepath='oracle_cards.json', return_data=False)\n\n# Option 4: Show progress bar (requires: pip install scrython[progress])\ncards = oracle_cards.download(progress=True)\n\n# Available bulk data types:\n# - 'oracle_cards': All unique cards with Oracle text\n# - 'unique_artwork': All cards with unique artwork\n# - 'default_cards': One version of each card\n# - 'all_cards': All card printings\n# - 'rulings': All card rulings\n```\n\n**Note:** The `download()` method automatically detects whether responses are gzip-compressed by checking HTTP `Content-Encoding` headers. This means it works seamlessly regardless of Scryfall's CDN configuration - you don't need to worry about compression formats.\n\n### Error Handling\n\n```python\nfrom scrython.base import ScryfallError\n\ntry:\n    card = scrython.cards.Named(exact='Nonexistent Card Name')\nexcept ScryfallError as e:\n    print(f\"Error {e.status}: {e.details}\")\n    if e.warnings:\n        print(f\"Suggestions: {e.warnings}\")\n```\n\n### Autocomplete\n\n```python\n# Get card name suggestions\nsuggestions = scrython.cards.Autocomplete(q='light')\n\nfor name in suggestions.data:\n    print(name)\n# Output: \"Light\", \"Lightning Bolt\", \"Lightning Strike\", ...\n```\n\n### Collection Queries\n\n```python\n# Fetch multiple cards by their identifiers\nidentifiers = [\n    {'id': '5f8287b1-5bb6-4e8f-9d78-8f3e3b3e1c6d'},\n    {'name': 'Lightning Bolt', 'set': 'lea'},\n    {'multiverse_id': 409574}\n]\ncards = scrython.cards.Collection(data={'identifiers': identifiers})\n\nfor card in cards.data:\n    print(f\"{card.name} - {card.set}\")\n```\n\n### Accessing Card Properties\n\n```python\ncard = scrython.cards.Named(fuzzy='Lightning Bolt')\n\n# Core identifiers\nprint(card.card_id)              # Scryfall UUID\nprint(card.oracle_id)       # Oracle ID (consistent across reprints)\nprint(card.multiverse_ids)  # Gatherer IDs\n\n# Gameplay properties\nprint(card.mana_cost)       # \"{R}\"\nprint(card.cmc)             # 1.0\nprint(card.type_line)       # \"Instant\"\nprint(card.oracle_text)     # Card rules text\nprint(card.colors)          # [\"R\"]\nprint(card.legalities)      # Format legality\n\n# Print properties\nprint(card.artist)          # Artist name\nprint(card.set_name)        # Full set name\nprint(card.rarity)          # \"common\", \"uncommon\", etc.\nprint(card.image_uris)      # Image URLs\nprint(card.prices)          # Price information\n\n# Multi-face cards\nif card.card_faces:\n    for face in card.card_faces:\n        print(f\"{face.name}: {face.mana_cost}\")\n```\n\n## Advanced Features (New in 2.0)\n\n### Magic Methods\n\nCards and other objects now support Python magic methods for better developer experience:\n\n```python\nimport scrython\n\ncard = scrython.cards.Named(fuzzy='Lightning Bolt')\n\n# Readable representation\nprint(repr(card))  # Object(id='abc123...', name='Lightning Bolt')\nprint(str(card))   # Lightning Bolt (LEA)\n\n# Equality comparison (by ID)\ncard1 = scrython.cards.Named(fuzzy='Lightning Bolt')\ncard2 = scrython.cards.Named(exact='Lightning Bolt')\nprint(card1 == card2)  # True (same card ID)\n\n# Use in sets and dicts (hashable)\nunique_cards = {card1, card2, card3}  # Deduplicates by ID\ncard_lookup = {card1: 'owned', card2: 'wanted'}\n```\n\n### Serialization\n\nExport and import card data easily:\n\n```python\nimport scrython\n\ncard = scrython.cards.Named(fuzzy='Lightning Bolt')\n\n# Export to dict\ncard_dict = card.to_dict()\n\n# Export to JSON\njson_str = card.to_json(indent=2)\n\n# Save to file\nwith open('card.json', 'w') as f:\n    f.write(card.to_json())\n\n# Import from dict (no API call!)\nfrom scrython.cards.cards import Object\nrestored_card = Object.from_dict(card_dict)\n\n# Export search results\nresults = scrython.cards.Search(q='bolt')\nall_cards = results.to_list()  # List of dicts\n```\n\n### Iteration Support\n\nIterate directly over search results with Pythonic syntax:\n\n```python\nimport scrython\n\nresults = scrython.cards.Search(q='c:red type:instant')\n\n# Direct iteration (current page)\nfor card in results:\n    print(card.name)\n\n# Get length\nprint(len(results))  # Number of cards in current page\n\n# Auto-pagination through ALL results\nfor card in results.iter_all():\n    print(card.name)  # Automatically fetches all pages\n\n# Works with list comprehensions\nnames = [card.name for card in results]\n\n# Works with filter\nred_cards = [c for c in results if c.has_color('R')]\n```\n\n### Convenience Methods\n\nQuick access to common card operations:\n\n```python\nimport scrython\n\ncard = scrython.cards.Named(fuzzy='Lightning Bolt')\n\n# Legality checks\nif card.is_legal_in('commander'):\n    print('Commander legal!')\n\n# Color checks\nif card.has_color('R'):\n    print('Red card!')\n\n# Type checks\nif card.is_instant:\n    print('Instant speed!')\n\n# Also available: is_creature, is_sorcery, is_enchantment,\n#                 is_artifact, is_planeswalker\n\n# Price helpers\ncheapest = card.lowest_price()\nmost_expensive = card.highest_price()\nprint(f'Price range: ${cheapest:.2f} - ${most_expensive:.2f}')\n\n# Image helpers (handles double-faced cards)\nurl = card.get_image_url(size='large')\nif url:\n    print(f'Image: {url}')\n```\n\n### List Convenience Methods\n\nTransform and filter search results easily:\n\n```python\nimport scrython\n\nresults = scrython.cards.Search(q='bolt')\n\n# Convert to dict keyed by name\nby_name = results.as_dict(key='name')\nprint(by_name['Lightning Bolt'].set)\n\n# Filter results\ncheap_cards = results.filter(lambda c: c.lowest_price() and c.lowest_price() \u003c 1.0)\n\n# Map/transform results\ncard_names = results.map(lambda c: c.name)\n\n# Chaining\nlea_names = [c.name for c in results.filter(lambda c: c.set == 'lea')]\n```\n\n### Combining Features\n\nPut it all together for powerful workflows:\n\n```python\nimport scrython\n\n# Search with caching and rate limiting\nresults = scrython.cards.Search(\n    q='c:red cmc\u003c=3',\n    cache=True,\n    cache_ttl=3600,\n    rate_limit_per_second=5\n)\n\n# Iterate and filter\naffordable_red = []\nfor card in results.iter_all():\n    if card.is_legal_in('commander') and card.has_color('R'):\n        price = card.lowest_price()\n        if price and price \u003c 5.0:\n            affordable_red.append({\n                'name': card.name,\n                'price': price,\n                'type': card.type_line\n            })\n\n# Export results\nimport json\nwith open('affordable_red.json', 'w') as f:\n    json.dump(affordable_red, f, indent=2)\n\nprint(f'Found {len(affordable_red)} affordable red cards!')\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnandascott%2Fscrython","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnandascott%2Fscrython","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnandascott%2Fscrython/lists"}