| |
| """ |
| Crypto API Clean Client - Integration with HuggingFace Space |
| https://really-amin-crypto-api-clean-fixed.hf.space |
| |
| Provides access to 281+ cryptocurrency resources across 12 categories: |
| - RPC Nodes (24) |
| - Block Explorers (33) |
| - Market Data APIs (33) |
| - News APIs (17) |
| - Sentiment APIs (14) |
| - On-chain Analytics APIs (14) |
| - Whale Tracking APIs (10) |
| - HuggingFace Resources (9) |
| - Free HTTP Endpoints (13) |
| - Local Backend Routes (106) |
| - CORS Proxies (7) |
| - Community Sentiment APIs (1) |
| """ |
|
|
| import httpx |
| import asyncio |
| import logging |
| from typing import Dict, Any, List, Optional |
| from datetime import datetime, timezone |
| from functools import lru_cache |
|
|
| logger = logging.getLogger(__name__) |
|
|
| |
| CRYPTO_API_CLEAN_BASE_URL = "https://really-amin-crypto-api-clean-fixed.hf.space" |
|
|
| |
| _resources_cache: Dict[str, Any] = {} |
| _cache_timestamp: Optional[datetime] = None |
| CACHE_TTL_SECONDS = 300 |
|
|
|
|
| class CryptoAPICleanService: |
| """ |
| Service for accessing Crypto API Clean HuggingFace Space |
| Provides comprehensive cryptocurrency resource database access |
| """ |
| |
| def __init__(self, timeout: int = 15): |
| self.base_url = CRYPTO_API_CLEAN_BASE_URL |
| self.timeout = timeout |
| self._client: Optional[httpx.AsyncClient] = None |
| |
| async def _get_client(self) -> httpx.AsyncClient: |
| """Get or create async HTTP client""" |
| if self._client is None or self._client.is_closed: |
| self._client = httpx.AsyncClient(timeout=self.timeout) |
| return self._client |
| |
| async def close(self): |
| """Close the HTTP client""" |
| if self._client and not self._client.is_closed: |
| await self._client.aclose() |
| |
| async def _request(self, endpoint: str, params: Dict = None) -> Dict[str, Any]: |
| """ |
| Make async request to Crypto API Clean with proper error handling |
| |
| Returns standardized response format matching project patterns |
| """ |
| provider = "CryptoAPIClean" |
| start_time = datetime.now(timezone.utc) |
| |
| try: |
| client = await self._get_client() |
| url = f"{self.base_url}{endpoint}" |
| |
| response = await client.get(url, params=params) |
| response.raise_for_status() |
| |
| data = response.json() |
| response_time_ms = (datetime.now(timezone.utc) - start_time).total_seconds() * 1000 |
| |
| logger.info(f"✅ {provider} - {endpoint} - {response_time_ms:.0f}ms") |
| |
| return { |
| "provider": provider, |
| "endpoint": endpoint, |
| "data": data, |
| "timestamp": datetime.now(timezone.utc).isoformat(), |
| "response_time_ms": response_time_ms, |
| "success": True, |
| "error": None |
| } |
| |
| except httpx.HTTPStatusError as e: |
| logger.error(f"❌ {provider} - {endpoint} - HTTP {e.response.status_code}") |
| return { |
| "provider": provider, |
| "endpoint": endpoint, |
| "data": None, |
| "timestamp": datetime.now(timezone.utc).isoformat(), |
| "success": False, |
| "error": f"HTTP {e.response.status_code}", |
| "error_type": "http_error" |
| } |
| except httpx.TimeoutException: |
| logger.error(f"❌ {provider} - {endpoint} - Timeout") |
| return { |
| "provider": provider, |
| "endpoint": endpoint, |
| "data": None, |
| "timestamp": datetime.now(timezone.utc).isoformat(), |
| "success": False, |
| "error": "Request timeout", |
| "error_type": "timeout" |
| } |
| except Exception as e: |
| logger.error(f"❌ {provider} - {endpoint} - {str(e)}") |
| return { |
| "provider": provider, |
| "endpoint": endpoint, |
| "data": None, |
| "timestamp": datetime.now(timezone.utc).isoformat(), |
| "success": False, |
| "error": str(e), |
| "error_type": "exception" |
| } |
| |
| |
| |
| async def get_resources_stats(self) -> Dict[str, Any]: |
| """ |
| Get resources database statistics |
| Returns: total_resources, total_categories, category breakdown |
| """ |
| return await self._request("/api/resources/stats") |
| |
| async def get_all_resources(self) -> Dict[str, Any]: |
| """ |
| Get complete list of all 281+ resources |
| Returns: Full resource database |
| """ |
| return await self._request("/api/resources/list") |
| |
| async def get_categories(self) -> Dict[str, Any]: |
| """ |
| Get list of all 12 resource categories |
| Categories: rpc_nodes, block_explorers, market_data_apis, news_apis, |
| sentiment_apis, onchain_analytics_apis, whale_tracking_apis, |
| hf_resources, free_http_endpoints, cors_proxies, |
| community_sentiment_apis, local_backend_routes |
| """ |
| return await self._request("/api/categories") |
| |
| async def get_resources_by_category(self, category: str) -> Dict[str, Any]: |
| """ |
| Get resources for a specific category |
| |
| Args: |
| category: Category name (e.g., 'market_data_apis', 'sentiment_apis') |
| """ |
| return await self._request(f"/api/resources/category/{category}") |
| |
| |
| |
| async def health_check(self) -> Dict[str, Any]: |
| """Check API health status""" |
| return await self._request("/health") |
| |
| |
| |
| async def get_total_resources_count(self) -> int: |
| """ |
| Get total number of resources available |
| |
| Returns: |
| int: Total resource count |
| """ |
| result = await self.get_resources_stats() |
| if result["success"] and result["data"]: |
| return result["data"].get("total_resources", 0) |
| return 0 |
| |
| async def get_category_summary(self) -> Dict[str, int]: |
| """ |
| Get summary of resources per category |
| |
| Returns: |
| Dict mapping category names to resource counts |
| """ |
| result = await self.get_resources_stats() |
| if result["success"] and result["data"]: |
| return result["data"].get("categories", {}) |
| return {} |
| |
| async def get_market_data_providers(self) -> List[Dict[str, Any]]: |
| """ |
| Get all market data API providers (33 resources) |
| """ |
| result = await self.get_resources_by_category("market_data_apis") |
| if result["success"] and result["data"]: |
| return result["data"].get("resources", []) |
| return [] |
| |
| async def get_sentiment_providers(self) -> List[Dict[str, Any]]: |
| """ |
| Get all sentiment API providers (14 resources) |
| """ |
| result = await self.get_resources_by_category("sentiment_apis") |
| if result["success"] and result["data"]: |
| return result["data"].get("resources", []) |
| return [] |
| |
| async def get_rpc_nodes(self) -> List[Dict[str, Any]]: |
| """ |
| Get all RPC node providers (24 resources) |
| """ |
| result = await self.get_resources_by_category("rpc_nodes") |
| if result["success"] and result["data"]: |
| return result["data"].get("resources", []) |
| return [] |
| |
| async def get_block_explorers(self) -> List[Dict[str, Any]]: |
| """ |
| Get all block explorer APIs (33 resources) |
| """ |
| result = await self.get_resources_by_category("block_explorers") |
| if result["success"] and result["data"]: |
| return result["data"].get("resources", []) |
| return [] |
|
|
|
|
| |
|
|
| _service_instance: Optional[CryptoAPICleanService] = None |
|
|
|
|
| def get_crypto_api_clean_service() -> CryptoAPICleanService: |
| """Get singleton instance of Crypto API Clean Service""" |
| global _service_instance |
| if _service_instance is None: |
| _service_instance = CryptoAPICleanService() |
| return _service_instance |
|
|
|
|
| |
|
|
| async def fetch_crypto_api_clean_stats() -> Dict[str, Any]: |
| """Fetch resource statistics from Crypto API Clean""" |
| service = get_crypto_api_clean_service() |
| return await service.get_resources_stats() |
|
|
|
|
| async def fetch_crypto_api_clean_resources(category: Optional[str] = None) -> Dict[str, Any]: |
| """Fetch resources from Crypto API Clean""" |
| service = get_crypto_api_clean_service() |
| if category: |
| return await service.get_resources_by_category(category) |
| return await service.get_all_resources() |
|
|
|
|
| |
|
|
| if __name__ == "__main__": |
| async def main(): |
| service = get_crypto_api_clean_service() |
| |
| print("=" * 70) |
| print("Testing Crypto API Clean Service") |
| print("=" * 70) |
| |
| |
| print("\n1. Health Check:") |
| result = await service.health_check() |
| print(f" Success: {result['success']}") |
| |
| |
| print("\n2. Resource Statistics:") |
| result = await service.get_resources_stats() |
| if result['success']: |
| data = result['data'] |
| print(f" Total Resources: {data.get('total_resources')}") |
| print(f" Total Categories: {data.get('total_categories')}") |
| print(f" Categories:") |
| for cat, count in data.get('categories', {}).items(): |
| print(f" - {cat}: {count}") |
| |
| |
| print("\n3. Market Data Providers:") |
| providers = await service.get_market_data_providers() |
| print(f" Found {len(providers)} providers") |
| |
| |
| print("\n4. Sentiment Providers:") |
| providers = await service.get_sentiment_providers() |
| print(f" Found {len(providers)} providers") |
| |
| await service.close() |
| print("\n" + "=" * 70) |
| print("Tests completed!") |
| |
| asyncio.run(main()) |
|
|