cryptologo 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- cryptologo-0.1.0/LICENSE +21 -0
- cryptologo-0.1.0/PKG-INFO +308 -0
- cryptologo-0.1.0/README.md +281 -0
- cryptologo-0.1.0/pyproject.toml +58 -0
- cryptologo-0.1.0/src/cryptologo/__init__.py +20 -0
- cryptologo-0.1.0/src/cryptologo/client.py +289 -0
- cryptologo-0.1.0/src/cryptologo/py.typed +0 -0
- cryptologo-0.1.0/src/cryptologo/types.py +57 -0
- cryptologo-0.1.0/tests/__init__.py +0 -0
- cryptologo-0.1.0/tests/test_client.py +95 -0
- cryptologo-0.1.0/uv.lock +8 -0
cryptologo-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 dobestan
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: cryptologo
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Python client for crypto-logo.com — 413 cryptocurrency logos in SVG, PNG, WebP, ICO
|
|
5
|
+
Project-URL: Homepage, https://crypto-logo.com
|
|
6
|
+
Project-URL: Documentation, https://crypto-logo.com/api/docs/
|
|
7
|
+
Project-URL: Repository, https://github.com/dobestan/cryptologo-python
|
|
8
|
+
Project-URL: API Reference, https://crypto-logo.com/api/docs/
|
|
9
|
+
Author-email: dobestan <dobestan@gmail.com>
|
|
10
|
+
License-Expression: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: api-client,bitcoin,branding,crypto,cryptocurrency,ethereum,icon,logo,png,svg
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
22
|
+
Classifier: Topic :: Multimedia :: Graphics
|
|
23
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
24
|
+
Classifier: Typing :: Typed
|
|
25
|
+
Requires-Python: >=3.10
|
|
26
|
+
Description-Content-Type: text/markdown
|
|
27
|
+
|
|
28
|
+
# cryptologo
|
|
29
|
+
|
|
30
|
+
[](https://pypi.org/project/cryptologo/)
|
|
31
|
+
[](https://pypi.org/project/cryptologo/)
|
|
32
|
+
[](https://opensource.org/licenses/MIT)
|
|
33
|
+
[](https://pypi.org/project/cryptologo/)
|
|
34
|
+
|
|
35
|
+
Python client for [crypto-logo.com](https://crypto-logo.com) — the cryptocurrency logo database with 413 coins in SVG, PNG, WebP, JPEG, and ICO formats. Zero dependencies (stdlib `urllib` only).
|
|
36
|
+
|
|
37
|
+
Every logo is available as a scalable SVG vector and pre-generated raster derivatives in 13 standard sizes (16px to 2000px). Logos are served from Cloudflare R2 CDN with immutable cache headers for production-grade performance. The dataset is extracted from [crypto-logo.com](https://crypto-logo.com), which tracks logo assets for Bitcoin, Ethereum, Solana, and 410+ other cryptocurrencies ranked by market cap.
|
|
38
|
+
|
|
39
|
+
> **Try the interactive logo browser at [crypto-logo.com](https://crypto-logo.com)** — [Bitcoin logo](https://crypto-logo.com/bitcoin-btc/), [Ethereum logo](https://crypto-logo.com/ethereum-eth/), [API docs](https://crypto-logo.com/api/docs/)
|
|
40
|
+
|
|
41
|
+
## Table of Contents
|
|
42
|
+
|
|
43
|
+
- [Install](#install)
|
|
44
|
+
- [Quick Start](#quick-start)
|
|
45
|
+
- [What You Can Do](#what-you-can-do)
|
|
46
|
+
- [List All Coins](#list-all-coins)
|
|
47
|
+
- [Download Logos](#download-logos)
|
|
48
|
+
- [Generate CDN URLs](#generate-cdn-urls)
|
|
49
|
+
- [Fetch Logo Variants and History](#fetch-logo-variants-and-history)
|
|
50
|
+
- [Cryptocurrency Logo Standards](#cryptocurrency-logo-standards)
|
|
51
|
+
- [SVG vs PNG — When to Use Which](#svg-vs-png--when-to-use-which)
|
|
52
|
+
- [Standard Sizes for Crypto Logos](#standard-sizes-for-crypto-logos)
|
|
53
|
+
- [Brand Color Consistency](#brand-color-consistency)
|
|
54
|
+
- [API Reference](#api-reference)
|
|
55
|
+
- [API Endpoints](#api-endpoints)
|
|
56
|
+
- [Learn More About Cryptocurrency Logos](#learn-more-about-cryptocurrency-logos)
|
|
57
|
+
- [Also Available](#also-available)
|
|
58
|
+
- [License](#license)
|
|
59
|
+
|
|
60
|
+
## Install
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
pip install cryptologo
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Quick Start
|
|
67
|
+
|
|
68
|
+
```python
|
|
69
|
+
from cryptologo import CryptoLogo
|
|
70
|
+
|
|
71
|
+
client = CryptoLogo()
|
|
72
|
+
|
|
73
|
+
# List all 413 coins sorted by market cap
|
|
74
|
+
coins = client.list_coins()
|
|
75
|
+
for coin in coins[:5]:
|
|
76
|
+
print(f"{coin.name} ({coin.ticker}) — rank #{coin.market_cap_rank}")
|
|
77
|
+
# Bitcoin (BTC) — rank #1
|
|
78
|
+
# Ethereum (ETH) — rank #2
|
|
79
|
+
# Tether (USDT) — rank #3
|
|
80
|
+
|
|
81
|
+
# Download Bitcoin logo as SVG
|
|
82
|
+
client.download_logo("bitcoin-btc", "svg")
|
|
83
|
+
# Saved: bitcoin-btc.svg
|
|
84
|
+
|
|
85
|
+
# Download Ethereum logo as 512x512 PNG
|
|
86
|
+
client.download_logo("ethereum-eth", "png", width=512)
|
|
87
|
+
# Saved: ethereum-eth-512x512.png
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## What You Can Do
|
|
91
|
+
|
|
92
|
+
### List All Coins
|
|
93
|
+
|
|
94
|
+
The `list_coins()` method returns all 413 active coins with metadata including name, ticker, slug, format availability (PNG/SVG), localized search terms in 14 languages, and CoinGecko market cap ranking.
|
|
95
|
+
|
|
96
|
+
| Field | Type | Description |
|
|
97
|
+
|-------|------|-------------|
|
|
98
|
+
| `name` | str | Display name (e.g., "Bitcoin") |
|
|
99
|
+
| `ticker` | str | Trading symbol (e.g., "BTC") |
|
|
100
|
+
| `slug` | str | URL identifier (e.g., "bitcoin-btc") |
|
|
101
|
+
| `has_png` | bool | PNG logo available |
|
|
102
|
+
| `has_svg` | bool | SVG vector available |
|
|
103
|
+
| `search_terms` | list | Names in Korean, Japanese, Chinese, etc. |
|
|
104
|
+
| `market_cap_rank` | int/None | CoinGecko ranking |
|
|
105
|
+
|
|
106
|
+
```python
|
|
107
|
+
from cryptologo import CryptoLogo
|
|
108
|
+
|
|
109
|
+
client = CryptoLogo()
|
|
110
|
+
coins = client.list_coins()
|
|
111
|
+
|
|
112
|
+
# Filter coins that have SVG logos
|
|
113
|
+
svg_coins = [c for c in coins if c.has_svg]
|
|
114
|
+
print(f"{len(svg_coins)} coins have SVG logos") # 413 coins have SVG logos
|
|
115
|
+
|
|
116
|
+
# Find a specific coin by ticker
|
|
117
|
+
btc = next((c for c in coins if c.ticker == "BTC"), None)
|
|
118
|
+
if btc:
|
|
119
|
+
print(f"{btc.name}: slug={btc.slug}, rank=#{btc.market_cap_rank}")
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### Download Logos
|
|
123
|
+
|
|
124
|
+
Fetch logo image data as bytes or save directly to a file. SVG logos require no size parameter. Raster formats (PNG, WebP, JPEG, ICO) require a width.
|
|
125
|
+
|
|
126
|
+
```python
|
|
127
|
+
from cryptologo import CryptoLogo
|
|
128
|
+
|
|
129
|
+
client = CryptoLogo()
|
|
130
|
+
|
|
131
|
+
# Get raw SVG bytes — scalable vector, ideal for web
|
|
132
|
+
svg_bytes = client.get_logo("bitcoin-btc", "svg")
|
|
133
|
+
|
|
134
|
+
# Get PNG at specific size — pre-generated, not resized on-the-fly
|
|
135
|
+
png_bytes = client.get_logo("solana-sol", "png", width=256)
|
|
136
|
+
|
|
137
|
+
# Download to file with auto-generated filename
|
|
138
|
+
path = client.download_logo("bitcoin-btc", "svg")
|
|
139
|
+
print(path) # bitcoin-btc.svg
|
|
140
|
+
|
|
141
|
+
# Download to specific path
|
|
142
|
+
path = client.download_logo("ethereum-eth", "png", "logos/eth.png", width=128)
|
|
143
|
+
|
|
144
|
+
# ICO format for favicons (sizes: 16, 32, 48, 64, 128, 256)
|
|
145
|
+
client.download_logo("bitcoin-btc", "ico", width=32)
|
|
146
|
+
|
|
147
|
+
# OG image with white background (1200x630 for social sharing)
|
|
148
|
+
client.download_logo("bitcoin-btc", "png", width=1200, height=630, bg="FFFFFF")
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### Generate CDN URLs
|
|
152
|
+
|
|
153
|
+
Build URLs for direct embedding in HTML, Markdown, or documentation. The CDN serves pre-generated derivatives from Cloudflare R2 with `Cache-Control: immutable` — no Django server overhead.
|
|
154
|
+
|
|
155
|
+
```python
|
|
156
|
+
from cryptologo import CryptoLogo
|
|
157
|
+
|
|
158
|
+
client = CryptoLogo()
|
|
159
|
+
|
|
160
|
+
# CDN URL for fast image display (recommended for <img> tags)
|
|
161
|
+
url = client.get_cdn_url("bitcoin-btc", "webp", 128)
|
|
162
|
+
# https://cdn.crypto-logo.com/logos/bitcoin-btc/128x128/transparent.webp
|
|
163
|
+
|
|
164
|
+
# CDN URL for SVG vector
|
|
165
|
+
url = client.get_cdn_url("ethereum-eth", "svg")
|
|
166
|
+
# https://cdn.crypto-logo.com/logos/ethereum-eth/vector.svg
|
|
167
|
+
|
|
168
|
+
# API URL (for downloads with Content-Disposition header)
|
|
169
|
+
url = client.get_logo_url("bitcoin-btc", "png", width=512, height=512)
|
|
170
|
+
# https://crypto-logo.com/api/logo/bitcoin-btc.png?w=512&h=512
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
### Fetch Logo Variants and History
|
|
174
|
+
|
|
175
|
+
Some coins have alternate logo designs (full wordmark, diamond shape, animated) and historical versions showing brand evolution.
|
|
176
|
+
|
|
177
|
+
```python
|
|
178
|
+
from cryptologo import CryptoLogo
|
|
179
|
+
|
|
180
|
+
client = CryptoLogo()
|
|
181
|
+
|
|
182
|
+
# Get a logo variant (alternate design)
|
|
183
|
+
full_logo = client.get_asset("versions/bitcoin-btc-full.svg")
|
|
184
|
+
|
|
185
|
+
# Get a historical logo version
|
|
186
|
+
old_logo = client.get_asset("history/bitcoin-btc-2009.png")
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
## Cryptocurrency Logo Standards
|
|
190
|
+
|
|
191
|
+
### SVG vs PNG — When to Use Which
|
|
192
|
+
|
|
193
|
+
Cryptocurrency projects publish official logos in SVG (vector) and PNG (raster). Choosing the right format depends on the use case.
|
|
194
|
+
|
|
195
|
+
| Use Case | Recommended Format | Reason |
|
|
196
|
+
|----------|--------------------|--------|
|
|
197
|
+
| Website/app UI | SVG | Scales to any resolution, small file size (~2-15KB) |
|
|
198
|
+
| Exchange listing | PNG 512x512 | Standard requirement for CoinGecko, CoinMarketCap |
|
|
199
|
+
| Mobile app icon | PNG 128x128 or 256x256 | iOS/Android require raster at specific sizes |
|
|
200
|
+
| Favicon | ICO 32x32 or SVG | ICO for legacy browsers, SVG for modern |
|
|
201
|
+
| Social media / OG image | PNG 1200x630 | Open Graph standard with solid background |
|
|
202
|
+
| Print / merch | SVG | Infinite scalability without quality loss |
|
|
203
|
+
| Documentation | SVG or PNG 64-128px | SVG preferred, PNG for Markdown compatibility |
|
|
204
|
+
| Email signature | PNG 48-64px | Most email clients don't render SVG |
|
|
205
|
+
|
|
206
|
+
### Standard Sizes for Crypto Logos
|
|
207
|
+
|
|
208
|
+
The industry has converged on standard sizes. CryptoLogo pre-generates all 13 standard sizes for each coin.
|
|
209
|
+
|
|
210
|
+
| Size | Common Use |
|
|
211
|
+
|------|-----------|
|
|
212
|
+
| 16x16 | Favicon, inline text icons |
|
|
213
|
+
| 32x32 | Favicon, small UI elements |
|
|
214
|
+
| 48x48 | Tab icons, small badges |
|
|
215
|
+
| 64x64 | List items, table cells |
|
|
216
|
+
| 96x96 | Grid thumbnails |
|
|
217
|
+
| 128x128 | Card previews, mobile |
|
|
218
|
+
| 200x200 | Profile images |
|
|
219
|
+
| 256x256 | Medium detail views |
|
|
220
|
+
| 400x400 | Large cards |
|
|
221
|
+
| 512x512 | Exchange listing standard |
|
|
222
|
+
| 1024x1024 | High-DPI displays |
|
|
223
|
+
| 2000x2000 | Maximum quality raster |
|
|
224
|
+
| 1200x630 | OG image (social sharing) |
|
|
225
|
+
|
|
226
|
+
### Brand Color Consistency
|
|
227
|
+
|
|
228
|
+
Each cryptocurrency has an official brand color (e.g., Bitcoin's #F7931A orange, Ethereum's #627EEA purple). CryptoLogo stores the `brand_color` for each coin, enabling consistent color theming in portfolio apps, dashboards, and data visualizations.
|
|
229
|
+
|
|
230
|
+
Notable brand colors:
|
|
231
|
+
- **Bitcoin**: #F7931A (orange) — designed by Satoshi-era contributor
|
|
232
|
+
- **Ethereum**: #627EEA (purple) — from the Ethereum Foundation brand kit
|
|
233
|
+
- **Solana**: #9945FF (violet) — gradient-based identity system
|
|
234
|
+
- **Cardano**: #0033AD (blue) — academic/formal positioning
|
|
235
|
+
- **Dogecoin**: #C2A633 (gold) — matches the Shiba Inu meme aesthetic
|
|
236
|
+
|
|
237
|
+
## API Reference
|
|
238
|
+
|
|
239
|
+
| Method | Parameters | Returns | Description |
|
|
240
|
+
|--------|-----------|---------|-------------|
|
|
241
|
+
| `list_coins()` | — | `list[Coin]` | All 413 coins with metadata |
|
|
242
|
+
| `get_logo(slug, fmt, ...)` | slug, fmt, width, height, bg | `bytes` | Logo image bytes |
|
|
243
|
+
| `get_logo_url(slug, fmt, ...)` | slug, fmt, width, height, bg | `str` | API URL for logo |
|
|
244
|
+
| `get_cdn_url(slug, fmt, width)` | slug, fmt, width, cdn_domain | `str` | CDN URL for embedding |
|
|
245
|
+
| `download_logo(slug, fmt, dest, ...)` | slug, fmt, dest, width, height, bg | `Path` | Save logo to file |
|
|
246
|
+
| `get_asset(file_path)` | file_path | `bytes` | Variant/history asset |
|
|
247
|
+
|
|
248
|
+
## API Endpoints
|
|
249
|
+
|
|
250
|
+
The crypto-logo.com REST API is free, requires no authentication, and supports CORS.
|
|
251
|
+
|
|
252
|
+
| Method | Endpoint | Description |
|
|
253
|
+
|--------|----------|-------------|
|
|
254
|
+
| GET | `/api/logo/{slug}.{fmt}` | Logo image (png, svg, webp, jpeg, ico) |
|
|
255
|
+
| GET | `/api/coins.json` | All coins metadata (JSON array) |
|
|
256
|
+
| GET | `/api/asset/{path}` | Variant and history files |
|
|
257
|
+
| GET | `/api/docs/` | Interactive API documentation |
|
|
258
|
+
| GET | `/api/schema/` | OpenAPI 3.0 schema |
|
|
259
|
+
|
|
260
|
+
### Example
|
|
261
|
+
|
|
262
|
+
```bash
|
|
263
|
+
# List all coins
|
|
264
|
+
curl -s "https://crypto-logo.com/api/coins.json" | python3 -m json.tool | head -20
|
|
265
|
+
|
|
266
|
+
# Download Bitcoin SVG
|
|
267
|
+
curl -O "https://crypto-logo.com/api/logo/bitcoin-btc.svg"
|
|
268
|
+
|
|
269
|
+
# Download Ethereum PNG 256x256
|
|
270
|
+
curl -O "https://crypto-logo.com/api/logo/ethereum-eth.png?w=256&h=256"
|
|
271
|
+
|
|
272
|
+
# Get OG image with white background
|
|
273
|
+
curl -O "https://crypto-logo.com/api/logo/bitcoin-btc.png?w=1200&h=630&bg=FFFFFF"
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
```json
|
|
277
|
+
[
|
|
278
|
+
{
|
|
279
|
+
"name": "Bitcoin",
|
|
280
|
+
"ticker": "BTC",
|
|
281
|
+
"slug": "bitcoin-btc",
|
|
282
|
+
"has_png": true,
|
|
283
|
+
"has_svg": true,
|
|
284
|
+
"search_terms": ["\ube44\ud2b8\ucf54\uc778", "\u30d3\u30c3\u30c8\u30b3\u30a4\u30f3"],
|
|
285
|
+
"market_cap_rank": 1
|
|
286
|
+
}
|
|
287
|
+
]
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
Full API documentation at [crypto-logo.com/api/docs/](https://crypto-logo.com/api/docs/).
|
|
291
|
+
|
|
292
|
+
## Learn More About Cryptocurrency Logos
|
|
293
|
+
|
|
294
|
+
- **Browse**: [All Coins](https://crypto-logo.com/) · [Categories](https://crypto-logo.com/categories/) · [Formats](https://crypto-logo.com/formats/)
|
|
295
|
+
- **API**: [API Docs](https://crypto-logo.com/api/docs/) · [OpenAPI Schema](https://crypto-logo.com/api/schema/)
|
|
296
|
+
|
|
297
|
+
## Also Available
|
|
298
|
+
|
|
299
|
+
| Platform | Package | Install |
|
|
300
|
+
|----------|---------|---------|
|
|
301
|
+
| **npm** | [cryptologo](https://www.npmjs.com/package/cryptologo) | `npm install cryptologo` |
|
|
302
|
+
| **Go** | [cryptologo-go](https://github.com/dobestan/cryptologo-go) | `go get github.com/dobestan/cryptologo-go` |
|
|
303
|
+
| **Rust** | [cryptologo](https://crates.io/crates/cryptologo) | `cargo add cryptologo` |
|
|
304
|
+
| **Ruby** | [cryptologo](https://rubygems.org/gems/cryptologo) | `gem install cryptologo` |
|
|
305
|
+
|
|
306
|
+
## License
|
|
307
|
+
|
|
308
|
+
MIT
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
# cryptologo
|
|
2
|
+
|
|
3
|
+
[](https://pypi.org/project/cryptologo/)
|
|
4
|
+
[](https://pypi.org/project/cryptologo/)
|
|
5
|
+
[](https://opensource.org/licenses/MIT)
|
|
6
|
+
[](https://pypi.org/project/cryptologo/)
|
|
7
|
+
|
|
8
|
+
Python client for [crypto-logo.com](https://crypto-logo.com) — the cryptocurrency logo database with 413 coins in SVG, PNG, WebP, JPEG, and ICO formats. Zero dependencies (stdlib `urllib` only).
|
|
9
|
+
|
|
10
|
+
Every logo is available as a scalable SVG vector and pre-generated raster derivatives in 13 standard sizes (16px to 2000px). Logos are served from Cloudflare R2 CDN with immutable cache headers for production-grade performance. The dataset is extracted from [crypto-logo.com](https://crypto-logo.com), which tracks logo assets for Bitcoin, Ethereum, Solana, and 410+ other cryptocurrencies ranked by market cap.
|
|
11
|
+
|
|
12
|
+
> **Try the interactive logo browser at [crypto-logo.com](https://crypto-logo.com)** — [Bitcoin logo](https://crypto-logo.com/bitcoin-btc/), [Ethereum logo](https://crypto-logo.com/ethereum-eth/), [API docs](https://crypto-logo.com/api/docs/)
|
|
13
|
+
|
|
14
|
+
## Table of Contents
|
|
15
|
+
|
|
16
|
+
- [Install](#install)
|
|
17
|
+
- [Quick Start](#quick-start)
|
|
18
|
+
- [What You Can Do](#what-you-can-do)
|
|
19
|
+
- [List All Coins](#list-all-coins)
|
|
20
|
+
- [Download Logos](#download-logos)
|
|
21
|
+
- [Generate CDN URLs](#generate-cdn-urls)
|
|
22
|
+
- [Fetch Logo Variants and History](#fetch-logo-variants-and-history)
|
|
23
|
+
- [Cryptocurrency Logo Standards](#cryptocurrency-logo-standards)
|
|
24
|
+
- [SVG vs PNG — When to Use Which](#svg-vs-png--when-to-use-which)
|
|
25
|
+
- [Standard Sizes for Crypto Logos](#standard-sizes-for-crypto-logos)
|
|
26
|
+
- [Brand Color Consistency](#brand-color-consistency)
|
|
27
|
+
- [API Reference](#api-reference)
|
|
28
|
+
- [API Endpoints](#api-endpoints)
|
|
29
|
+
- [Learn More About Cryptocurrency Logos](#learn-more-about-cryptocurrency-logos)
|
|
30
|
+
- [Also Available](#also-available)
|
|
31
|
+
- [License](#license)
|
|
32
|
+
|
|
33
|
+
## Install
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
pip install cryptologo
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Quick Start
|
|
40
|
+
|
|
41
|
+
```python
|
|
42
|
+
from cryptologo import CryptoLogo
|
|
43
|
+
|
|
44
|
+
client = CryptoLogo()
|
|
45
|
+
|
|
46
|
+
# List all 413 coins sorted by market cap
|
|
47
|
+
coins = client.list_coins()
|
|
48
|
+
for coin in coins[:5]:
|
|
49
|
+
print(f"{coin.name} ({coin.ticker}) — rank #{coin.market_cap_rank}")
|
|
50
|
+
# Bitcoin (BTC) — rank #1
|
|
51
|
+
# Ethereum (ETH) — rank #2
|
|
52
|
+
# Tether (USDT) — rank #3
|
|
53
|
+
|
|
54
|
+
# Download Bitcoin logo as SVG
|
|
55
|
+
client.download_logo("bitcoin-btc", "svg")
|
|
56
|
+
# Saved: bitcoin-btc.svg
|
|
57
|
+
|
|
58
|
+
# Download Ethereum logo as 512x512 PNG
|
|
59
|
+
client.download_logo("ethereum-eth", "png", width=512)
|
|
60
|
+
# Saved: ethereum-eth-512x512.png
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## What You Can Do
|
|
64
|
+
|
|
65
|
+
### List All Coins
|
|
66
|
+
|
|
67
|
+
The `list_coins()` method returns all 413 active coins with metadata including name, ticker, slug, format availability (PNG/SVG), localized search terms in 14 languages, and CoinGecko market cap ranking.
|
|
68
|
+
|
|
69
|
+
| Field | Type | Description |
|
|
70
|
+
|-------|------|-------------|
|
|
71
|
+
| `name` | str | Display name (e.g., "Bitcoin") |
|
|
72
|
+
| `ticker` | str | Trading symbol (e.g., "BTC") |
|
|
73
|
+
| `slug` | str | URL identifier (e.g., "bitcoin-btc") |
|
|
74
|
+
| `has_png` | bool | PNG logo available |
|
|
75
|
+
| `has_svg` | bool | SVG vector available |
|
|
76
|
+
| `search_terms` | list | Names in Korean, Japanese, Chinese, etc. |
|
|
77
|
+
| `market_cap_rank` | int/None | CoinGecko ranking |
|
|
78
|
+
|
|
79
|
+
```python
|
|
80
|
+
from cryptologo import CryptoLogo
|
|
81
|
+
|
|
82
|
+
client = CryptoLogo()
|
|
83
|
+
coins = client.list_coins()
|
|
84
|
+
|
|
85
|
+
# Filter coins that have SVG logos
|
|
86
|
+
svg_coins = [c for c in coins if c.has_svg]
|
|
87
|
+
print(f"{len(svg_coins)} coins have SVG logos") # 413 coins have SVG logos
|
|
88
|
+
|
|
89
|
+
# Find a specific coin by ticker
|
|
90
|
+
btc = next((c for c in coins if c.ticker == "BTC"), None)
|
|
91
|
+
if btc:
|
|
92
|
+
print(f"{btc.name}: slug={btc.slug}, rank=#{btc.market_cap_rank}")
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### Download Logos
|
|
96
|
+
|
|
97
|
+
Fetch logo image data as bytes or save directly to a file. SVG logos require no size parameter. Raster formats (PNG, WebP, JPEG, ICO) require a width.
|
|
98
|
+
|
|
99
|
+
```python
|
|
100
|
+
from cryptologo import CryptoLogo
|
|
101
|
+
|
|
102
|
+
client = CryptoLogo()
|
|
103
|
+
|
|
104
|
+
# Get raw SVG bytes — scalable vector, ideal for web
|
|
105
|
+
svg_bytes = client.get_logo("bitcoin-btc", "svg")
|
|
106
|
+
|
|
107
|
+
# Get PNG at specific size — pre-generated, not resized on-the-fly
|
|
108
|
+
png_bytes = client.get_logo("solana-sol", "png", width=256)
|
|
109
|
+
|
|
110
|
+
# Download to file with auto-generated filename
|
|
111
|
+
path = client.download_logo("bitcoin-btc", "svg")
|
|
112
|
+
print(path) # bitcoin-btc.svg
|
|
113
|
+
|
|
114
|
+
# Download to specific path
|
|
115
|
+
path = client.download_logo("ethereum-eth", "png", "logos/eth.png", width=128)
|
|
116
|
+
|
|
117
|
+
# ICO format for favicons (sizes: 16, 32, 48, 64, 128, 256)
|
|
118
|
+
client.download_logo("bitcoin-btc", "ico", width=32)
|
|
119
|
+
|
|
120
|
+
# OG image with white background (1200x630 for social sharing)
|
|
121
|
+
client.download_logo("bitcoin-btc", "png", width=1200, height=630, bg="FFFFFF")
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### Generate CDN URLs
|
|
125
|
+
|
|
126
|
+
Build URLs for direct embedding in HTML, Markdown, or documentation. The CDN serves pre-generated derivatives from Cloudflare R2 with `Cache-Control: immutable` — no Django server overhead.
|
|
127
|
+
|
|
128
|
+
```python
|
|
129
|
+
from cryptologo import CryptoLogo
|
|
130
|
+
|
|
131
|
+
client = CryptoLogo()
|
|
132
|
+
|
|
133
|
+
# CDN URL for fast image display (recommended for <img> tags)
|
|
134
|
+
url = client.get_cdn_url("bitcoin-btc", "webp", 128)
|
|
135
|
+
# https://cdn.crypto-logo.com/logos/bitcoin-btc/128x128/transparent.webp
|
|
136
|
+
|
|
137
|
+
# CDN URL for SVG vector
|
|
138
|
+
url = client.get_cdn_url("ethereum-eth", "svg")
|
|
139
|
+
# https://cdn.crypto-logo.com/logos/ethereum-eth/vector.svg
|
|
140
|
+
|
|
141
|
+
# API URL (for downloads with Content-Disposition header)
|
|
142
|
+
url = client.get_logo_url("bitcoin-btc", "png", width=512, height=512)
|
|
143
|
+
# https://crypto-logo.com/api/logo/bitcoin-btc.png?w=512&h=512
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### Fetch Logo Variants and History
|
|
147
|
+
|
|
148
|
+
Some coins have alternate logo designs (full wordmark, diamond shape, animated) and historical versions showing brand evolution.
|
|
149
|
+
|
|
150
|
+
```python
|
|
151
|
+
from cryptologo import CryptoLogo
|
|
152
|
+
|
|
153
|
+
client = CryptoLogo()
|
|
154
|
+
|
|
155
|
+
# Get a logo variant (alternate design)
|
|
156
|
+
full_logo = client.get_asset("versions/bitcoin-btc-full.svg")
|
|
157
|
+
|
|
158
|
+
# Get a historical logo version
|
|
159
|
+
old_logo = client.get_asset("history/bitcoin-btc-2009.png")
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
## Cryptocurrency Logo Standards
|
|
163
|
+
|
|
164
|
+
### SVG vs PNG — When to Use Which
|
|
165
|
+
|
|
166
|
+
Cryptocurrency projects publish official logos in SVG (vector) and PNG (raster). Choosing the right format depends on the use case.
|
|
167
|
+
|
|
168
|
+
| Use Case | Recommended Format | Reason |
|
|
169
|
+
|----------|--------------------|--------|
|
|
170
|
+
| Website/app UI | SVG | Scales to any resolution, small file size (~2-15KB) |
|
|
171
|
+
| Exchange listing | PNG 512x512 | Standard requirement for CoinGecko, CoinMarketCap |
|
|
172
|
+
| Mobile app icon | PNG 128x128 or 256x256 | iOS/Android require raster at specific sizes |
|
|
173
|
+
| Favicon | ICO 32x32 or SVG | ICO for legacy browsers, SVG for modern |
|
|
174
|
+
| Social media / OG image | PNG 1200x630 | Open Graph standard with solid background |
|
|
175
|
+
| Print / merch | SVG | Infinite scalability without quality loss |
|
|
176
|
+
| Documentation | SVG or PNG 64-128px | SVG preferred, PNG for Markdown compatibility |
|
|
177
|
+
| Email signature | PNG 48-64px | Most email clients don't render SVG |
|
|
178
|
+
|
|
179
|
+
### Standard Sizes for Crypto Logos
|
|
180
|
+
|
|
181
|
+
The industry has converged on standard sizes. CryptoLogo pre-generates all 13 standard sizes for each coin.
|
|
182
|
+
|
|
183
|
+
| Size | Common Use |
|
|
184
|
+
|------|-----------|
|
|
185
|
+
| 16x16 | Favicon, inline text icons |
|
|
186
|
+
| 32x32 | Favicon, small UI elements |
|
|
187
|
+
| 48x48 | Tab icons, small badges |
|
|
188
|
+
| 64x64 | List items, table cells |
|
|
189
|
+
| 96x96 | Grid thumbnails |
|
|
190
|
+
| 128x128 | Card previews, mobile |
|
|
191
|
+
| 200x200 | Profile images |
|
|
192
|
+
| 256x256 | Medium detail views |
|
|
193
|
+
| 400x400 | Large cards |
|
|
194
|
+
| 512x512 | Exchange listing standard |
|
|
195
|
+
| 1024x1024 | High-DPI displays |
|
|
196
|
+
| 2000x2000 | Maximum quality raster |
|
|
197
|
+
| 1200x630 | OG image (social sharing) |
|
|
198
|
+
|
|
199
|
+
### Brand Color Consistency
|
|
200
|
+
|
|
201
|
+
Each cryptocurrency has an official brand color (e.g., Bitcoin's #F7931A orange, Ethereum's #627EEA purple). CryptoLogo stores the `brand_color` for each coin, enabling consistent color theming in portfolio apps, dashboards, and data visualizations.
|
|
202
|
+
|
|
203
|
+
Notable brand colors:
|
|
204
|
+
- **Bitcoin**: #F7931A (orange) — designed by Satoshi-era contributor
|
|
205
|
+
- **Ethereum**: #627EEA (purple) — from the Ethereum Foundation brand kit
|
|
206
|
+
- **Solana**: #9945FF (violet) — gradient-based identity system
|
|
207
|
+
- **Cardano**: #0033AD (blue) — academic/formal positioning
|
|
208
|
+
- **Dogecoin**: #C2A633 (gold) — matches the Shiba Inu meme aesthetic
|
|
209
|
+
|
|
210
|
+
## API Reference
|
|
211
|
+
|
|
212
|
+
| Method | Parameters | Returns | Description |
|
|
213
|
+
|--------|-----------|---------|-------------|
|
|
214
|
+
| `list_coins()` | — | `list[Coin]` | All 413 coins with metadata |
|
|
215
|
+
| `get_logo(slug, fmt, ...)` | slug, fmt, width, height, bg | `bytes` | Logo image bytes |
|
|
216
|
+
| `get_logo_url(slug, fmt, ...)` | slug, fmt, width, height, bg | `str` | API URL for logo |
|
|
217
|
+
| `get_cdn_url(slug, fmt, width)` | slug, fmt, width, cdn_domain | `str` | CDN URL for embedding |
|
|
218
|
+
| `download_logo(slug, fmt, dest, ...)` | slug, fmt, dest, width, height, bg | `Path` | Save logo to file |
|
|
219
|
+
| `get_asset(file_path)` | file_path | `bytes` | Variant/history asset |
|
|
220
|
+
|
|
221
|
+
## API Endpoints
|
|
222
|
+
|
|
223
|
+
The crypto-logo.com REST API is free, requires no authentication, and supports CORS.
|
|
224
|
+
|
|
225
|
+
| Method | Endpoint | Description |
|
|
226
|
+
|--------|----------|-------------|
|
|
227
|
+
| GET | `/api/logo/{slug}.{fmt}` | Logo image (png, svg, webp, jpeg, ico) |
|
|
228
|
+
| GET | `/api/coins.json` | All coins metadata (JSON array) |
|
|
229
|
+
| GET | `/api/asset/{path}` | Variant and history files |
|
|
230
|
+
| GET | `/api/docs/` | Interactive API documentation |
|
|
231
|
+
| GET | `/api/schema/` | OpenAPI 3.0 schema |
|
|
232
|
+
|
|
233
|
+
### Example
|
|
234
|
+
|
|
235
|
+
```bash
|
|
236
|
+
# List all coins
|
|
237
|
+
curl -s "https://crypto-logo.com/api/coins.json" | python3 -m json.tool | head -20
|
|
238
|
+
|
|
239
|
+
# Download Bitcoin SVG
|
|
240
|
+
curl -O "https://crypto-logo.com/api/logo/bitcoin-btc.svg"
|
|
241
|
+
|
|
242
|
+
# Download Ethereum PNG 256x256
|
|
243
|
+
curl -O "https://crypto-logo.com/api/logo/ethereum-eth.png?w=256&h=256"
|
|
244
|
+
|
|
245
|
+
# Get OG image with white background
|
|
246
|
+
curl -O "https://crypto-logo.com/api/logo/bitcoin-btc.png?w=1200&h=630&bg=FFFFFF"
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
```json
|
|
250
|
+
[
|
|
251
|
+
{
|
|
252
|
+
"name": "Bitcoin",
|
|
253
|
+
"ticker": "BTC",
|
|
254
|
+
"slug": "bitcoin-btc",
|
|
255
|
+
"has_png": true,
|
|
256
|
+
"has_svg": true,
|
|
257
|
+
"search_terms": ["\ube44\ud2b8\ucf54\uc778", "\u30d3\u30c3\u30c8\u30b3\u30a4\u30f3"],
|
|
258
|
+
"market_cap_rank": 1
|
|
259
|
+
}
|
|
260
|
+
]
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
Full API documentation at [crypto-logo.com/api/docs/](https://crypto-logo.com/api/docs/).
|
|
264
|
+
|
|
265
|
+
## Learn More About Cryptocurrency Logos
|
|
266
|
+
|
|
267
|
+
- **Browse**: [All Coins](https://crypto-logo.com/) · [Categories](https://crypto-logo.com/categories/) · [Formats](https://crypto-logo.com/formats/)
|
|
268
|
+
- **API**: [API Docs](https://crypto-logo.com/api/docs/) · [OpenAPI Schema](https://crypto-logo.com/api/schema/)
|
|
269
|
+
|
|
270
|
+
## Also Available
|
|
271
|
+
|
|
272
|
+
| Platform | Package | Install |
|
|
273
|
+
|----------|---------|---------|
|
|
274
|
+
| **npm** | [cryptologo](https://www.npmjs.com/package/cryptologo) | `npm install cryptologo` |
|
|
275
|
+
| **Go** | [cryptologo-go](https://github.com/dobestan/cryptologo-go) | `go get github.com/dobestan/cryptologo-go` |
|
|
276
|
+
| **Rust** | [cryptologo](https://crates.io/crates/cryptologo) | `cargo add cryptologo` |
|
|
277
|
+
| **Ruby** | [cryptologo](https://rubygems.org/gems/cryptologo) | `gem install cryptologo` |
|
|
278
|
+
|
|
279
|
+
## License
|
|
280
|
+
|
|
281
|
+
MIT
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "cryptologo"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Python client for crypto-logo.com — 413 cryptocurrency logos in SVG, PNG, WebP, ICO"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = "MIT"
|
|
11
|
+
requires-python = ">=3.10"
|
|
12
|
+
authors = [{ name = "dobestan", email = "dobestan@gmail.com" }]
|
|
13
|
+
keywords = [
|
|
14
|
+
"cryptocurrency",
|
|
15
|
+
"logo",
|
|
16
|
+
"bitcoin",
|
|
17
|
+
"ethereum",
|
|
18
|
+
"svg",
|
|
19
|
+
"png",
|
|
20
|
+
"crypto",
|
|
21
|
+
"branding",
|
|
22
|
+
"icon",
|
|
23
|
+
"api-client",
|
|
24
|
+
]
|
|
25
|
+
classifiers = [
|
|
26
|
+
"Development Status :: 4 - Beta",
|
|
27
|
+
"Intended Audience :: Developers",
|
|
28
|
+
"License :: OSI Approved :: MIT License",
|
|
29
|
+
"Programming Language :: Python :: 3",
|
|
30
|
+
"Programming Language :: Python :: 3.10",
|
|
31
|
+
"Programming Language :: Python :: 3.11",
|
|
32
|
+
"Programming Language :: Python :: 3.12",
|
|
33
|
+
"Programming Language :: Python :: 3.13",
|
|
34
|
+
"Programming Language :: Python :: 3.14",
|
|
35
|
+
"Topic :: Multimedia :: Graphics",
|
|
36
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
37
|
+
"Typing :: Typed",
|
|
38
|
+
]
|
|
39
|
+
|
|
40
|
+
[project.urls]
|
|
41
|
+
Homepage = "https://crypto-logo.com"
|
|
42
|
+
Documentation = "https://crypto-logo.com/api/docs/"
|
|
43
|
+
Repository = "https://github.com/dobestan/cryptologo-python"
|
|
44
|
+
"API Reference" = "https://crypto-logo.com/api/docs/"
|
|
45
|
+
|
|
46
|
+
[tool.hatch.build.targets.wheel]
|
|
47
|
+
packages = ["src/cryptologo"]
|
|
48
|
+
|
|
49
|
+
[tool.ruff]
|
|
50
|
+
target-version = "py310"
|
|
51
|
+
line-length = 120
|
|
52
|
+
|
|
53
|
+
[tool.ruff.lint]
|
|
54
|
+
select = ["E", "F", "I", "UP"]
|
|
55
|
+
|
|
56
|
+
[tool.mypy]
|
|
57
|
+
python_version = "3.10"
|
|
58
|
+
strict = true
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"""cryptologo — Python client for crypto-logo.com cryptocurrency logo API.
|
|
2
|
+
|
|
3
|
+
Fetch, download, and generate URLs for 413+ cryptocurrency logos in SVG, PNG,
|
|
4
|
+
WebP, JPEG, and ICO formats. Zero dependencies (stdlib urllib only).
|
|
5
|
+
|
|
6
|
+
Usage::
|
|
7
|
+
|
|
8
|
+
from cryptologo import CryptoLogo
|
|
9
|
+
|
|
10
|
+
client = CryptoLogo()
|
|
11
|
+
coins = client.list_coins()
|
|
12
|
+
logo_bytes = client.get_logo("bitcoin-btc", "svg")
|
|
13
|
+
client.download_logo("ethereum-eth", "png", "eth-logo.png", width=512)
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from cryptologo.client import CryptoLogo
|
|
17
|
+
from cryptologo.types import Coin
|
|
18
|
+
|
|
19
|
+
__all__ = ["CryptoLogo", "Coin"]
|
|
20
|
+
__version__ = "0.1.0"
|
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
"""CryptoLogo API client — zero dependencies (stdlib urllib only)."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
import urllib.request
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import Any
|
|
9
|
+
|
|
10
|
+
from cryptologo.types import ALL_FORMATS, ALLOWED_SIZES, ICO_SIZES, Coin
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class CryptoLogoError(Exception):
|
|
14
|
+
"""Base exception for CryptoLogo client errors."""
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class NotFoundError(CryptoLogoError):
|
|
18
|
+
"""Raised when a coin or logo is not found (HTTP 404)."""
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class CryptoLogo:
|
|
22
|
+
"""Client for the crypto-logo.com API.
|
|
23
|
+
|
|
24
|
+
Provides methods to list coins, fetch logos, generate logo URLs,
|
|
25
|
+
and download logo files. No API key required.
|
|
26
|
+
|
|
27
|
+
Args:
|
|
28
|
+
base_url: API base URL. Defaults to ``https://crypto-logo.com``.
|
|
29
|
+
timeout: Request timeout in seconds. Defaults to 30.
|
|
30
|
+
|
|
31
|
+
Example::
|
|
32
|
+
|
|
33
|
+
client = CryptoLogo()
|
|
34
|
+
|
|
35
|
+
# List all coins
|
|
36
|
+
coins = client.list_coins()
|
|
37
|
+
for coin in coins[:5]:
|
|
38
|
+
print(f"{coin.name} ({coin.ticker}) — rank #{coin.market_cap_rank}")
|
|
39
|
+
|
|
40
|
+
# Get logo bytes
|
|
41
|
+
svg_data = client.get_logo("bitcoin-btc", "svg")
|
|
42
|
+
|
|
43
|
+
# Download to file
|
|
44
|
+
client.download_logo("ethereum-eth", "png", "eth.png", width=256)
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
def __init__(
|
|
48
|
+
self,
|
|
49
|
+
base_url: str = "https://crypto-logo.com",
|
|
50
|
+
timeout: int = 30,
|
|
51
|
+
) -> None:
|
|
52
|
+
self.base_url = base_url.rstrip("/")
|
|
53
|
+
self.timeout = timeout
|
|
54
|
+
|
|
55
|
+
def list_coins(self) -> list[Coin]:
|
|
56
|
+
"""Fetch all active coins from the API.
|
|
57
|
+
|
|
58
|
+
Returns:
|
|
59
|
+
List of Coin objects sorted by market cap rank.
|
|
60
|
+
|
|
61
|
+
Example::
|
|
62
|
+
|
|
63
|
+
coins = client.list_coins()
|
|
64
|
+
bitcoin = next(c for c in coins if c.ticker == "BTC")
|
|
65
|
+
print(f"{bitcoin.name}: rank #{bitcoin.market_cap_rank}")
|
|
66
|
+
"""
|
|
67
|
+
data = self._get_json("/api/coins.json")
|
|
68
|
+
if not isinstance(data, list):
|
|
69
|
+
raise CryptoLogoError("Unexpected response format")
|
|
70
|
+
return [Coin.from_dict(item) for item in data]
|
|
71
|
+
|
|
72
|
+
def get_logo(
|
|
73
|
+
self,
|
|
74
|
+
slug: str,
|
|
75
|
+
fmt: str = "svg",
|
|
76
|
+
*,
|
|
77
|
+
width: int | None = None,
|
|
78
|
+
height: int | None = None,
|
|
79
|
+
bg: str | None = None,
|
|
80
|
+
) -> bytes:
|
|
81
|
+
"""Fetch logo image bytes from the API.
|
|
82
|
+
|
|
83
|
+
Args:
|
|
84
|
+
slug: Coin slug (e.g., "bitcoin-btc").
|
|
85
|
+
fmt: Image format — "svg", "png", "webp", "jpeg", or "ico".
|
|
86
|
+
width: Output width in pixels (required for raster formats).
|
|
87
|
+
height: Output height in pixels (defaults to width for square).
|
|
88
|
+
bg: Background hex color without # (e.g., "FFFFFF").
|
|
89
|
+
|
|
90
|
+
Returns:
|
|
91
|
+
Raw image bytes.
|
|
92
|
+
|
|
93
|
+
Raises:
|
|
94
|
+
NotFoundError: If the coin or requested derivative is not found.
|
|
95
|
+
ValueError: If the format or size is invalid.
|
|
96
|
+
|
|
97
|
+
Example::
|
|
98
|
+
|
|
99
|
+
# SVG (no size needed)
|
|
100
|
+
svg = client.get_logo("bitcoin-btc", "svg")
|
|
101
|
+
|
|
102
|
+
# PNG at 256x256
|
|
103
|
+
png = client.get_logo("bitcoin-btc", "png", width=256)
|
|
104
|
+
|
|
105
|
+
# OG image with white background
|
|
106
|
+
og = client.get_logo("bitcoin-btc", "png", width=1200, height=630, bg="FFFFFF")
|
|
107
|
+
"""
|
|
108
|
+
fmt = fmt.lower()
|
|
109
|
+
if fmt not in ALL_FORMATS:
|
|
110
|
+
raise ValueError(f"Unsupported format: {fmt}. Use one of: {', '.join(ALL_FORMATS)}")
|
|
111
|
+
|
|
112
|
+
if fmt != "svg" and width is None:
|
|
113
|
+
raise ValueError("Width is required for raster formats. Use one of: " + ", ".join(str(s) for s in ALLOWED_SIZES))
|
|
114
|
+
|
|
115
|
+
if fmt == "ico" and width is not None and width not in ICO_SIZES:
|
|
116
|
+
raise ValueError(f"ICO size must be one of: {', '.join(str(s) for s in ICO_SIZES)}")
|
|
117
|
+
|
|
118
|
+
if fmt != "svg" and width is not None and height is None:
|
|
119
|
+
height = width
|
|
120
|
+
|
|
121
|
+
url = self.get_logo_url(slug, fmt, width=width, height=height, bg=bg)
|
|
122
|
+
return self._get_bytes(url)
|
|
123
|
+
|
|
124
|
+
def get_logo_url(
|
|
125
|
+
self,
|
|
126
|
+
slug: str,
|
|
127
|
+
fmt: str = "svg",
|
|
128
|
+
*,
|
|
129
|
+
width: int | None = None,
|
|
130
|
+
height: int | None = None,
|
|
131
|
+
bg: str | None = None,
|
|
132
|
+
) -> str:
|
|
133
|
+
"""Build the API URL for a logo.
|
|
134
|
+
|
|
135
|
+
Args:
|
|
136
|
+
slug: Coin slug (e.g., "bitcoin-btc").
|
|
137
|
+
fmt: Image format.
|
|
138
|
+
width: Output width in pixels.
|
|
139
|
+
height: Output height in pixels.
|
|
140
|
+
bg: Background hex color without #.
|
|
141
|
+
|
|
142
|
+
Returns:
|
|
143
|
+
Full API URL string.
|
|
144
|
+
|
|
145
|
+
Example::
|
|
146
|
+
|
|
147
|
+
url = client.get_logo_url("bitcoin-btc", "png", width=512)
|
|
148
|
+
# https://crypto-logo.com/api/logo/bitcoin-btc.png?w=512&h=512
|
|
149
|
+
"""
|
|
150
|
+
url = f"{self.base_url}/api/logo/{slug}.{fmt}"
|
|
151
|
+
params: list[str] = []
|
|
152
|
+
if width is not None:
|
|
153
|
+
params.append(f"w={width}")
|
|
154
|
+
if height is not None:
|
|
155
|
+
params.append(f"h={height}")
|
|
156
|
+
if bg:
|
|
157
|
+
params.append(f"bg={bg}")
|
|
158
|
+
if params:
|
|
159
|
+
url += "?" + "&".join(params)
|
|
160
|
+
return url
|
|
161
|
+
|
|
162
|
+
def get_cdn_url(
|
|
163
|
+
self,
|
|
164
|
+
slug: str,
|
|
165
|
+
fmt: str = "png",
|
|
166
|
+
width: int = 128,
|
|
167
|
+
*,
|
|
168
|
+
cdn_domain: str = "cdn.crypto-logo.com",
|
|
169
|
+
) -> str:
|
|
170
|
+
"""Build a CDN URL for direct image embedding (faster than API).
|
|
171
|
+
|
|
172
|
+
The CDN serves pre-generated derivatives without going through Django.
|
|
173
|
+
Use this for ``<img>`` tags and other display contexts.
|
|
174
|
+
|
|
175
|
+
Args:
|
|
176
|
+
slug: Coin slug.
|
|
177
|
+
fmt: Image format ("png", "webp", "jpeg", "svg").
|
|
178
|
+
width: Square size in pixels.
|
|
179
|
+
cdn_domain: CDN hostname.
|
|
180
|
+
|
|
181
|
+
Returns:
|
|
182
|
+
CDN URL string.
|
|
183
|
+
|
|
184
|
+
Example::
|
|
185
|
+
|
|
186
|
+
url = client.get_cdn_url("bitcoin-btc", "webp", 128)
|
|
187
|
+
# https://cdn.crypto-logo.com/logos/bitcoin-btc/128x128/transparent.webp
|
|
188
|
+
"""
|
|
189
|
+
if fmt == "svg":
|
|
190
|
+
return f"https://{cdn_domain}/logos/{slug}/vector.svg"
|
|
191
|
+
return f"https://{cdn_domain}/logos/{slug}/{width}x{width}/transparent.{fmt}"
|
|
192
|
+
|
|
193
|
+
def download_logo(
|
|
194
|
+
self,
|
|
195
|
+
slug: str,
|
|
196
|
+
fmt: str = "svg",
|
|
197
|
+
dest: str | Path | None = None,
|
|
198
|
+
*,
|
|
199
|
+
width: int | None = None,
|
|
200
|
+
height: int | None = None,
|
|
201
|
+
bg: str | None = None,
|
|
202
|
+
) -> Path:
|
|
203
|
+
"""Download a logo to a local file.
|
|
204
|
+
|
|
205
|
+
Args:
|
|
206
|
+
slug: Coin slug (e.g., "bitcoin-btc").
|
|
207
|
+
fmt: Image format.
|
|
208
|
+
dest: Destination file path. Auto-generated if None.
|
|
209
|
+
width: Output width in pixels.
|
|
210
|
+
height: Output height in pixels.
|
|
211
|
+
bg: Background hex color without #.
|
|
212
|
+
|
|
213
|
+
Returns:
|
|
214
|
+
Path to the downloaded file.
|
|
215
|
+
|
|
216
|
+
Example::
|
|
217
|
+
|
|
218
|
+
path = client.download_logo("bitcoin-btc", "svg")
|
|
219
|
+
print(f"Saved to {path}") # bitcoin-btc.svg
|
|
220
|
+
|
|
221
|
+
path = client.download_logo("ethereum-eth", "png", width=512)
|
|
222
|
+
print(f"Saved to {path}") # ethereum-eth-512x512.png
|
|
223
|
+
"""
|
|
224
|
+
data = self.get_logo(slug, fmt, width=width, height=height, bg=bg)
|
|
225
|
+
|
|
226
|
+
if dest is None:
|
|
227
|
+
if fmt == "svg":
|
|
228
|
+
dest = Path(f"{slug}.svg")
|
|
229
|
+
elif width and height and width != height:
|
|
230
|
+
dest = Path(f"{slug}-{width}x{height}.{fmt}")
|
|
231
|
+
elif width:
|
|
232
|
+
dest = Path(f"{slug}-{width}x{width}.{fmt}")
|
|
233
|
+
else:
|
|
234
|
+
dest = Path(f"{slug}.{fmt}")
|
|
235
|
+
else:
|
|
236
|
+
dest = Path(dest)
|
|
237
|
+
|
|
238
|
+
dest.write_bytes(data)
|
|
239
|
+
return dest
|
|
240
|
+
|
|
241
|
+
def get_asset(self, file_path: str) -> bytes:
|
|
242
|
+
"""Fetch an asset file (variant or historical logo).
|
|
243
|
+
|
|
244
|
+
Args:
|
|
245
|
+
file_path: Relative path within assets/ (e.g., "versions/bitcoin-btc-full.svg").
|
|
246
|
+
|
|
247
|
+
Returns:
|
|
248
|
+
Raw file bytes.
|
|
249
|
+
|
|
250
|
+
Example::
|
|
251
|
+
|
|
252
|
+
# Get a logo variant
|
|
253
|
+
data = client.get_asset("versions/bitcoin-btc-full.svg")
|
|
254
|
+
|
|
255
|
+
# Get a historical logo
|
|
256
|
+
data = client.get_asset("history/bitcoin-btc-2009.png")
|
|
257
|
+
"""
|
|
258
|
+
url = f"{self.base_url}/api/asset/{file_path}"
|
|
259
|
+
return self._get_bytes(url)
|
|
260
|
+
|
|
261
|
+
# -- internal helpers --
|
|
262
|
+
|
|
263
|
+
def _get_json(self, path: str) -> Any:
|
|
264
|
+
"""GET JSON from relative path."""
|
|
265
|
+
url = f"{self.base_url}{path}" if path.startswith("/") else path
|
|
266
|
+
req = urllib.request.Request(url, headers={"Accept": "application/json", "User-Agent": "cryptologo-python/0.1.0"})
|
|
267
|
+
try:
|
|
268
|
+
with urllib.request.urlopen(req, timeout=self.timeout) as resp:
|
|
269
|
+
return json.loads(resp.read().decode())
|
|
270
|
+
except urllib.error.HTTPError as e:
|
|
271
|
+
if e.code == 404:
|
|
272
|
+
raise NotFoundError(f"Not found: {url}") from None
|
|
273
|
+
raise CryptoLogoError(f"HTTP {e.code}: {url}") from e
|
|
274
|
+
|
|
275
|
+
def _get_bytes(self, url: str) -> bytes:
|
|
276
|
+
"""GET binary data from full or relative URL."""
|
|
277
|
+
if url.startswith("/"):
|
|
278
|
+
url = f"{self.base_url}{url}"
|
|
279
|
+
req = urllib.request.Request(url, headers={"User-Agent": "cryptologo-python/0.1.0"})
|
|
280
|
+
try:
|
|
281
|
+
with urllib.request.urlopen(req, timeout=self.timeout) as resp:
|
|
282
|
+
return resp.read() # type: ignore[no-any-return]
|
|
283
|
+
except urllib.error.HTTPError as e:
|
|
284
|
+
if e.code == 404:
|
|
285
|
+
raise NotFoundError(f"Not found: {url}") from None
|
|
286
|
+
raise CryptoLogoError(f"HTTP {e.code}: {url}") from e
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
__all__ = ["CryptoLogo", "CryptoLogoError", "NotFoundError"]
|
|
File without changes
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"""Type definitions for the CryptoLogo API client."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass, field
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@dataclass(frozen=True)
|
|
9
|
+
class Coin:
|
|
10
|
+
"""A cryptocurrency coin with logo availability metadata.
|
|
11
|
+
|
|
12
|
+
Attributes:
|
|
13
|
+
name: Display name (e.g., "Bitcoin").
|
|
14
|
+
ticker: Trading symbol (e.g., "BTC").
|
|
15
|
+
slug: URL-safe identifier (e.g., "bitcoin-btc").
|
|
16
|
+
has_png: Whether a PNG logo is available.
|
|
17
|
+
has_svg: Whether an SVG logo is available.
|
|
18
|
+
search_terms: Localized names for search (Korean, Japanese, etc.).
|
|
19
|
+
market_cap_rank: CoinGecko market cap ranking (None if unranked).
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
name: str
|
|
23
|
+
ticker: str
|
|
24
|
+
slug: str
|
|
25
|
+
has_png: bool = False
|
|
26
|
+
has_svg: bool = False
|
|
27
|
+
search_terms: list[str] = field(default_factory=list)
|
|
28
|
+
market_cap_rank: int | None = None
|
|
29
|
+
|
|
30
|
+
@classmethod
|
|
31
|
+
def from_dict(cls, data: dict[str, object]) -> Coin:
|
|
32
|
+
"""Create a Coin from the API JSON response dict."""
|
|
33
|
+
return cls(
|
|
34
|
+
name=str(data.get("name", "")),
|
|
35
|
+
ticker=str(data.get("ticker", "")),
|
|
36
|
+
slug=str(data.get("slug", "")),
|
|
37
|
+
has_png=bool(data.get("has_png", False)),
|
|
38
|
+
has_svg=bool(data.get("has_svg", False)),
|
|
39
|
+
search_terms=list(data.get("search_terms", [])), # type: ignore[arg-type]
|
|
40
|
+
market_cap_rank=data.get("market_cap_rank") if data.get("market_cap_rank") is not None else None, # type: ignore[arg-type]
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
# Supported raster formats for logo downloads
|
|
45
|
+
RASTER_FORMATS = ("png", "webp", "jpeg", "ico")
|
|
46
|
+
|
|
47
|
+
# Supported vector formats
|
|
48
|
+
VECTOR_FORMATS = ("svg",)
|
|
49
|
+
|
|
50
|
+
# All supported formats
|
|
51
|
+
ALL_FORMATS = RASTER_FORMATS + VECTOR_FORMATS
|
|
52
|
+
|
|
53
|
+
# Standard logo sizes (square, in pixels)
|
|
54
|
+
ALLOWED_SIZES = (16, 32, 48, 64, 96, 120, 128, 200, 256, 400, 512, 1024, 2000)
|
|
55
|
+
|
|
56
|
+
# ICO-specific sizes
|
|
57
|
+
ICO_SIZES = (16, 32, 48, 64, 128, 256)
|
|
File without changes
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
"""Tests for the CryptoLogo Python client."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import pytest
|
|
6
|
+
|
|
7
|
+
from cryptologo import CryptoLogo
|
|
8
|
+
from cryptologo.client import CryptoLogoError
|
|
9
|
+
from cryptologo.types import ALLOWED_SIZES, ALL_FORMATS, ICO_SIZES, Coin
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class TestCoin:
|
|
13
|
+
def test_from_dict(self) -> None:
|
|
14
|
+
data = {
|
|
15
|
+
"name": "Bitcoin",
|
|
16
|
+
"ticker": "BTC",
|
|
17
|
+
"slug": "bitcoin-btc",
|
|
18
|
+
"has_png": True,
|
|
19
|
+
"has_svg": True,
|
|
20
|
+
"search_terms": ["\ube44\ud2b8\ucf54\uc778"],
|
|
21
|
+
"market_cap_rank": 1,
|
|
22
|
+
}
|
|
23
|
+
coin = Coin.from_dict(data)
|
|
24
|
+
assert coin.name == "Bitcoin"
|
|
25
|
+
assert coin.ticker == "BTC"
|
|
26
|
+
assert coin.slug == "bitcoin-btc"
|
|
27
|
+
assert coin.has_png is True
|
|
28
|
+
assert coin.has_svg is True
|
|
29
|
+
assert coin.market_cap_rank == 1
|
|
30
|
+
|
|
31
|
+
def test_from_dict_minimal(self) -> None:
|
|
32
|
+
coin = Coin.from_dict({"name": "Test", "ticker": "TST", "slug": "test-tst"})
|
|
33
|
+
assert coin.has_png is False
|
|
34
|
+
assert coin.market_cap_rank is None
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class TestCryptoLogoClient:
|
|
38
|
+
def test_default_base_url(self) -> None:
|
|
39
|
+
client = CryptoLogo()
|
|
40
|
+
assert client.base_url == "https://crypto-logo.com"
|
|
41
|
+
|
|
42
|
+
def test_custom_base_url(self) -> None:
|
|
43
|
+
client = CryptoLogo(base_url="http://localhost:8002/")
|
|
44
|
+
assert client.base_url == "http://localhost:8002"
|
|
45
|
+
|
|
46
|
+
def test_get_logo_url_svg(self) -> None:
|
|
47
|
+
client = CryptoLogo()
|
|
48
|
+
url = client.get_logo_url("bitcoin-btc", "svg")
|
|
49
|
+
assert url == "https://crypto-logo.com/api/logo/bitcoin-btc.svg"
|
|
50
|
+
|
|
51
|
+
def test_get_logo_url_png_with_size(self) -> None:
|
|
52
|
+
client = CryptoLogo()
|
|
53
|
+
url = client.get_logo_url("bitcoin-btc", "png", width=256, height=256)
|
|
54
|
+
assert url == "https://crypto-logo.com/api/logo/bitcoin-btc.png?w=256&h=256"
|
|
55
|
+
|
|
56
|
+
def test_get_logo_url_with_bg(self) -> None:
|
|
57
|
+
client = CryptoLogo()
|
|
58
|
+
url = client.get_logo_url("bitcoin-btc", "png", width=1200, height=630, bg="FFFFFF")
|
|
59
|
+
assert "bg=FFFFFF" in url
|
|
60
|
+
|
|
61
|
+
def test_get_cdn_url_png(self) -> None:
|
|
62
|
+
client = CryptoLogo()
|
|
63
|
+
url = client.get_cdn_url("bitcoin-btc", "png", 128)
|
|
64
|
+
assert url == "https://cdn.crypto-logo.com/logos/bitcoin-btc/128x128/transparent.png"
|
|
65
|
+
|
|
66
|
+
def test_get_cdn_url_svg(self) -> None:
|
|
67
|
+
client = CryptoLogo()
|
|
68
|
+
url = client.get_cdn_url("bitcoin-btc", "svg")
|
|
69
|
+
assert url == "https://cdn.crypto-logo.com/logos/bitcoin-btc/vector.svg"
|
|
70
|
+
|
|
71
|
+
def test_invalid_format(self) -> None:
|
|
72
|
+
client = CryptoLogo()
|
|
73
|
+
with pytest.raises(ValueError, match="Unsupported format"):
|
|
74
|
+
client.get_logo("bitcoin-btc", "bmp")
|
|
75
|
+
|
|
76
|
+
def test_raster_requires_width(self) -> None:
|
|
77
|
+
client = CryptoLogo()
|
|
78
|
+
with pytest.raises(ValueError, match="Width is required"):
|
|
79
|
+
client.get_logo("bitcoin-btc", "png")
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
class TestConstants:
|
|
83
|
+
def test_all_formats(self) -> None:
|
|
84
|
+
assert "svg" in ALL_FORMATS
|
|
85
|
+
assert "png" in ALL_FORMATS
|
|
86
|
+
assert "webp" in ALL_FORMATS
|
|
87
|
+
|
|
88
|
+
def test_allowed_sizes(self) -> None:
|
|
89
|
+
assert 128 in ALLOWED_SIZES
|
|
90
|
+
assert 512 in ALLOWED_SIZES
|
|
91
|
+
assert 2000 in ALLOWED_SIZES
|
|
92
|
+
|
|
93
|
+
def test_ico_sizes(self) -> None:
|
|
94
|
+
assert 32 in ICO_SIZES
|
|
95
|
+
assert 256 in ICO_SIZES
|