dexscreener-python 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.
- dexscreener_python-0.1.0/.github/ISSUE_TEMPLATE/bug_report.md +26 -0
- dexscreener_python-0.1.0/.github/ISSUE_TEMPLATE/feature_request.md +19 -0
- dexscreener_python-0.1.0/.github/PULL_REQUEST_TEMPLATE.md +11 -0
- dexscreener_python-0.1.0/.github/workflows/ci.yml +39 -0
- dexscreener_python-0.1.0/.gitignore +35 -0
- dexscreener_python-0.1.0/CHANGELOG.md +22 -0
- dexscreener_python-0.1.0/CONTRIBUTING.md +47 -0
- dexscreener_python-0.1.0/LICENSE +21 -0
- dexscreener_python-0.1.0/PKG-INFO +208 -0
- dexscreener_python-0.1.0/README.md +178 -0
- dexscreener_python-0.1.0/dexscreener/__init__.py +11 -0
- dexscreener_python-0.1.0/dexscreener/client.py +458 -0
- dexscreener_python-0.1.0/dexscreener/models.py +92 -0
- dexscreener_python-0.1.0/dexscreener/py.typed +0 -0
- dexscreener_python-0.1.0/pyproject.toml +59 -0
- dexscreener_python-0.1.0/tests/test_client.py +165 -0
- dexscreener_python-0.1.0/tests/test_models.py +47 -0
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Bug Report
|
|
3
|
+
about: Report a bug or unexpected behavior
|
|
4
|
+
title: "[BUG] "
|
|
5
|
+
labels: bug
|
|
6
|
+
assignees: ''
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
**Describe the bug**
|
|
10
|
+
A clear and concise description of the issue.
|
|
11
|
+
|
|
12
|
+
**To reproduce**
|
|
13
|
+
```python
|
|
14
|
+
# Minimal code to reproduce
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
**Expected behavior**
|
|
18
|
+
What you expected to happen.
|
|
19
|
+
|
|
20
|
+
**Actual behavior**
|
|
21
|
+
What actually happened. Include the full traceback if applicable.
|
|
22
|
+
|
|
23
|
+
**Environment**
|
|
24
|
+
- Python version:
|
|
25
|
+
- Package version:
|
|
26
|
+
- OS:
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Feature Request
|
|
3
|
+
about: Suggest a new feature or enhancement
|
|
4
|
+
title: "[FEATURE] "
|
|
5
|
+
labels: enhancement
|
|
6
|
+
assignees: ''
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
**Is your feature request related to a problem?**
|
|
10
|
+
A clear description of the problem.
|
|
11
|
+
|
|
12
|
+
**Describe the solution you'd like**
|
|
13
|
+
What you want to happen.
|
|
14
|
+
|
|
15
|
+
**Alternatives considered**
|
|
16
|
+
Any alternative solutions you've considered.
|
|
17
|
+
|
|
18
|
+
**Additional context**
|
|
19
|
+
Any other context or screenshots.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
## What does this PR do?
|
|
2
|
+
|
|
3
|
+
<!-- Brief description of the change -->
|
|
4
|
+
|
|
5
|
+
## Checklist
|
|
6
|
+
|
|
7
|
+
- [ ] Tests added/updated
|
|
8
|
+
- [ ] `pytest tests/` passes
|
|
9
|
+
- [ ] `ruff check .` passes
|
|
10
|
+
- [ ] `mypy dexscreener/` passes
|
|
11
|
+
- [ ] `CHANGELOG.md` updated (if user-facing)
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
permissions:
|
|
10
|
+
contents: read
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
test:
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
strategy:
|
|
16
|
+
matrix:
|
|
17
|
+
python-version: ["3.11", "3.12", "3.13"]
|
|
18
|
+
|
|
19
|
+
steps:
|
|
20
|
+
- uses: actions/checkout@v4
|
|
21
|
+
|
|
22
|
+
- name: Set up Python ${{ matrix.python-version }}
|
|
23
|
+
uses: actions/setup-python@v5
|
|
24
|
+
with:
|
|
25
|
+
python-version: ${{ matrix.python-version }}
|
|
26
|
+
|
|
27
|
+
- name: Install dependencies
|
|
28
|
+
run: |
|
|
29
|
+
python -m pip install --upgrade pip
|
|
30
|
+
pip install -e ".[dev]"
|
|
31
|
+
|
|
32
|
+
- name: Lint
|
|
33
|
+
run: ruff check .
|
|
34
|
+
|
|
35
|
+
- name: Type check
|
|
36
|
+
run: mypy dexscreener/
|
|
37
|
+
|
|
38
|
+
- name: Test
|
|
39
|
+
run: pytest tests/ -v --tb=short
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
*.egg-info/
|
|
6
|
+
*.egg
|
|
7
|
+
dist/
|
|
8
|
+
build/
|
|
9
|
+
*.whl
|
|
10
|
+
|
|
11
|
+
# Virtual environments
|
|
12
|
+
.venv/
|
|
13
|
+
venv/
|
|
14
|
+
env/
|
|
15
|
+
|
|
16
|
+
# IDE
|
|
17
|
+
.idea/
|
|
18
|
+
.vscode/
|
|
19
|
+
*.swp
|
|
20
|
+
*.swo
|
|
21
|
+
*~
|
|
22
|
+
|
|
23
|
+
# Testing
|
|
24
|
+
.pytest_cache/
|
|
25
|
+
.coverage
|
|
26
|
+
htmlcov/
|
|
27
|
+
.mypy_cache/
|
|
28
|
+
.ruff_cache/
|
|
29
|
+
|
|
30
|
+
# OS
|
|
31
|
+
.DS_Store
|
|
32
|
+
Thumbs.db
|
|
33
|
+
|
|
34
|
+
# Distribution
|
|
35
|
+
*.tar.gz
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [0.1.0] - 2026-04-11
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- `DexScreenerClient` — async client for the DexScreener API
|
|
13
|
+
- `DexPairData` — typed dataclass for parsed pair data with computed properties
|
|
14
|
+
- Rate limiting with token bucket (configurable requests/second)
|
|
15
|
+
- Automatic 429 retry with exponential backoff
|
|
16
|
+
- Global 429 cooldown (prevents request storms)
|
|
17
|
+
- Response caching with configurable TTL
|
|
18
|
+
- Batch token fetch (up to 30 tokens per request)
|
|
19
|
+
- Pair search by token name, symbol, or address
|
|
20
|
+
- Boosted token detection
|
|
21
|
+
- Works on any chain: Solana, Ethereum, Base, BSC, Arbitrum, etc.
|
|
22
|
+
- `async with` context manager support
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
Thanks for considering a contribution.
|
|
4
|
+
|
|
5
|
+
## Development setup
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
git clone https://github.com/JinUltimate1995/dexscreener-python.git
|
|
9
|
+
cd dexscreener-python
|
|
10
|
+
python -m venv .venv && source .venv/bin/activate
|
|
11
|
+
pip install -e ".[dev]"
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Running tests
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
pytest tests/ -v
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Linting
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
ruff check .
|
|
24
|
+
mypy dexscreener/
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Pull requests
|
|
28
|
+
|
|
29
|
+
1. Fork the repo and create a branch from `main`.
|
|
30
|
+
2. Add tests for any new functionality.
|
|
31
|
+
3. Ensure `pytest`, `ruff check`, and `mypy` all pass.
|
|
32
|
+
4. Keep PRs focused — one feature or fix per PR.
|
|
33
|
+
5. Update `CHANGELOG.md` if your change is user-facing.
|
|
34
|
+
|
|
35
|
+
## Reporting bugs
|
|
36
|
+
|
|
37
|
+
Open an issue with:
|
|
38
|
+
- Python version
|
|
39
|
+
- `dexscreener-python` version
|
|
40
|
+
- Minimal reproduction code
|
|
41
|
+
- Expected vs actual behavior
|
|
42
|
+
|
|
43
|
+
## Code style
|
|
44
|
+
|
|
45
|
+
- We use [ruff](https://docs.astral.sh/ruff/) for linting and formatting.
|
|
46
|
+
- Type annotations are required for all public APIs.
|
|
47
|
+
- All public functions need docstrings.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 JinUltimate
|
|
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,208 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: dexscreener-python
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Async DexScreener API client with rate limiting, 429 retry, and response caching.
|
|
5
|
+
Project-URL: Homepage, https://github.com/JinUltimate1995/dexscreener-python
|
|
6
|
+
Project-URL: Repository, https://github.com/JinUltimate1995/dexscreener-python
|
|
7
|
+
Project-URL: Issues, https://github.com/JinUltimate1995/dexscreener-python/issues
|
|
8
|
+
Author: JinUltimate
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: api,crypto,defi,dex,dexscreener,ethereum,solana,trading
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Framework :: AsyncIO
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
21
|
+
Requires-Python: >=3.11
|
|
22
|
+
Requires-Dist: httpx>=0.24.0
|
|
23
|
+
Provides-Extra: dev
|
|
24
|
+
Requires-Dist: mypy>=1.0; extra == 'dev'
|
|
25
|
+
Requires-Dist: pytest-asyncio>=0.21; extra == 'dev'
|
|
26
|
+
Requires-Dist: pytest>=7.0; extra == 'dev'
|
|
27
|
+
Requires-Dist: respx>=0.21; extra == 'dev'
|
|
28
|
+
Requires-Dist: ruff>=0.4; extra == 'dev'
|
|
29
|
+
Description-Content-Type: text/markdown
|
|
30
|
+
|
|
31
|
+
<div align="center">
|
|
32
|
+
<h1>dexscreener-python</h1>
|
|
33
|
+
<p><strong>async dexscreener api client for python.</strong></p>
|
|
34
|
+
<p>rate limiting. 429 retry. response caching. request dedup. works on any chain.</p>
|
|
35
|
+
|
|
36
|
+
<br/>
|
|
37
|
+
|
|
38
|
+
<a href="https://github.com/JinUltimate1995/dexscreener-python/actions"><img src="https://img.shields.io/github/actions/workflow/status/JinUltimate1995/dexscreener-python/ci.yml?branch=main&style=flat-square&label=tests" /></a>
|
|
39
|
+
<a href="https://pypi.org/project/dexscreener-python/"><img src="https://img.shields.io/pypi/v/dexscreener-python?style=flat-square" /></a>
|
|
40
|
+
<img src="https://img.shields.io/pypi/pyversions/dexscreener-python?style=flat-square" />
|
|
41
|
+
<img src="https://img.shields.io/badge/typed-py.typed-blue?style=flat-square" />
|
|
42
|
+
<img src="https://img.shields.io/badge/async-first-blue?style=flat-square" />
|
|
43
|
+
<img src="https://img.shields.io/badge/license-MIT-green?style=flat-square" />
|
|
44
|
+
</div>
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
> extracted from a production trading system. handles rate limits gracefully.
|
|
49
|
+
|
|
50
|
+
## 🛡️ Why this library?
|
|
51
|
+
|
|
52
|
+
dexscreener's api is free and powerful, but:
|
|
53
|
+
|
|
54
|
+
- **429s kill your app** if you don't handle them
|
|
55
|
+
- **no python client exists** that handles rate limits properly
|
|
56
|
+
- **concurrent requests** for the same token waste your quota
|
|
57
|
+
|
|
58
|
+
this library solves all three:
|
|
59
|
+
|
|
60
|
+
```python
|
|
61
|
+
from dexscreener import DexScreenerClient
|
|
62
|
+
|
|
63
|
+
async with DexScreenerClient() as client:
|
|
64
|
+
# get all trading pairs for a token
|
|
65
|
+
pairs = await client.get_token_pairs("solana", "So11111111111111111111111111111111111111112")
|
|
66
|
+
|
|
67
|
+
for pair in pairs:
|
|
68
|
+
print(f"{pair.base_token_symbol}: ${pair.price_usd} | liq: ${pair.liquidity_usd}")
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
429s → automatic exponential backoff (3s → 6s → 12s).
|
|
72
|
+
duplicate requests → deduplicated into one HTTP call.
|
|
73
|
+
results → cached with configurable TTL.
|
|
74
|
+
|
|
75
|
+
## 📦 Install
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
pip install dexscreener-python
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
requires python 3.11+
|
|
82
|
+
|
|
83
|
+
## ⚡ Features
|
|
84
|
+
|
|
85
|
+
| feature | description |
|
|
86
|
+
|---|---|
|
|
87
|
+
| **any chain** | solana, ethereum, base, bsc, arbitrum — anything dexscreener supports |
|
|
88
|
+
| **adaptive rate limiting** | token bucket that backs off on 429 and recovers after success |
|
|
89
|
+
| **global 429 cooldown** | one 429 pauses ALL requests briefly (prevents request storms) |
|
|
90
|
+
| **response cache** | configurable TTL per endpoint. concurrent calls share one request. |
|
|
91
|
+
| **batch support** | fetch up to 30 tokens in a single request |
|
|
92
|
+
| **typed data** | `DexPairData` dataclass with computed properties (buy/sell ratio, age, etc.) |
|
|
93
|
+
|
|
94
|
+
## 🔧 Usage
|
|
95
|
+
|
|
96
|
+
### 💰 Token Pairs
|
|
97
|
+
|
|
98
|
+
```python
|
|
99
|
+
pairs = await client.get_token_pairs("solana", token_address)
|
|
100
|
+
|
|
101
|
+
best_pair = pairs[0] # sorted by liquidity (highest first)
|
|
102
|
+
print(f"Price: ${best_pair.price_usd}")
|
|
103
|
+
print(f"Liquidity: ${best_pair.liquidity_usd}")
|
|
104
|
+
print(f"24h Volume: ${best_pair.volume_24h}")
|
|
105
|
+
print(f"Buy/Sell 5m: {best_pair.buy_sell_ratio_5m:.2f}")
|
|
106
|
+
print(f"Age: {best_pair.pair_age_seconds // 3600}h")
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### 💵 Token Price
|
|
110
|
+
|
|
111
|
+
```python
|
|
112
|
+
price = await client.get_token_price("solana", token_address)
|
|
113
|
+
print(f"${price}")
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### 📦 Batch Fetch (up to 30 tokens)
|
|
117
|
+
|
|
118
|
+
```python
|
|
119
|
+
data = await client.get_tokens_batch("solana", [addr1, addr2, addr3])
|
|
120
|
+
for addr, pair in data.items():
|
|
121
|
+
print(f"{pair.base_token_symbol}: ${pair.price_usd}")
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### 🔍 Search
|
|
125
|
+
|
|
126
|
+
```python
|
|
127
|
+
results = await client.search_pairs("BONK")
|
|
128
|
+
for pair in results:
|
|
129
|
+
print(f"{pair.base_token_symbol} on {pair.dex_id}: ${pair.price_usd}")
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### 🚀 Boosted Tokens
|
|
133
|
+
|
|
134
|
+
```python
|
|
135
|
+
boosted = await client.get_boosted_tokens("solana")
|
|
136
|
+
# boosted tokens are paid promotions — treat with caution
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### 🎯 Specific Pair
|
|
140
|
+
|
|
141
|
+
```python
|
|
142
|
+
pair = await client.get_pair_by_address("solana", pair_address)
|
|
143
|
+
print(f"{pair.dex_id}: ${pair.price_usd}")
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
## 📋 DexPairData
|
|
147
|
+
|
|
148
|
+
all methods return `DexPairData` objects with these fields:
|
|
149
|
+
|
|
150
|
+
| field | type | description |
|
|
151
|
+
|---|---|---|
|
|
152
|
+
| `chain_id` | str | chain identifier |
|
|
153
|
+
| `dex_id` | str | dex identifier (raydium, uniswap_v3, etc.) |
|
|
154
|
+
| `pair_address` | str | pair contract address |
|
|
155
|
+
| `base_token_address` | str | base token mint/address |
|
|
156
|
+
| `base_token_symbol` | str | token symbol |
|
|
157
|
+
| `price_usd` | Decimal | current price in USD |
|
|
158
|
+
| `price_native` | Decimal | price in native token (SOL, ETH, etc.) |
|
|
159
|
+
| `liquidity_usd` | Decimal | total liquidity in USD |
|
|
160
|
+
| `volume_5m / 1h / 6h / 24h` | Decimal | volume by timeframe |
|
|
161
|
+
| `price_change_5m / 1h / 6h / 24h` | Decimal | price change % |
|
|
162
|
+
| `buys_5m / 1h / 24h` | int | buy transactions |
|
|
163
|
+
| `sells_5m / 1h / 24h` | int | sell transactions |
|
|
164
|
+
| `pair_created_at` | int | unix timestamp (ms) |
|
|
165
|
+
|
|
166
|
+
computed properties:
|
|
167
|
+
|
|
168
|
+
| property | description |
|
|
169
|
+
|---|---|
|
|
170
|
+
| `buy_sell_ratio_5m` | buy/sell ratio over 5 minutes (>1 = more buying) |
|
|
171
|
+
| `buy_sell_ratio_1h` | buy/sell ratio over 1 hour |
|
|
172
|
+
| `has_liquidity` | True if liquidity > $1000 |
|
|
173
|
+
| `pair_age_seconds` | pair age in seconds |
|
|
174
|
+
|
|
175
|
+
## ⚙️ Configuration
|
|
176
|
+
|
|
177
|
+
```python
|
|
178
|
+
client = DexScreenerClient(
|
|
179
|
+
rate_limit=5.0, # requests/second (default: 5.0, dexscreener limit is ~300/min)
|
|
180
|
+
cache_ttl=8.0, # default cache TTL in seconds
|
|
181
|
+
)
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
---
|
|
185
|
+
|
|
186
|
+
## 🆚 Comparison
|
|
187
|
+
|
|
188
|
+
| | dexscreener-python | raw `httpx` / `requests` |
|
|
189
|
+
|---|---|---|
|
|
190
|
+
| Rate limiting | ✅ adaptive token bucket | ❌ manual |
|
|
191
|
+
| 429 retry | ✅ exponential backoff | ❌ crash |
|
|
192
|
+
| Global cooldown | ✅ one 429 pauses all | ❌ per-request only |
|
|
193
|
+
| Response cache | ✅ TTL + dedup | ❌ manual |
|
|
194
|
+
| Typed data | ✅ `DexPairData` dataclass | ❌ raw dicts |
|
|
195
|
+
| Batch support | ✅ up to 30 tokens | ❌ manual loop |
|
|
196
|
+
| Async | ✅ native | depends |
|
|
197
|
+
|
|
198
|
+
## License
|
|
199
|
+
|
|
200
|
+
MIT
|
|
201
|
+
|
|
202
|
+
---
|
|
203
|
+
|
|
204
|
+
## 📦 Also by JinUltimate1995
|
|
205
|
+
|
|
206
|
+
- **[jupiter-swap-python](https://github.com/JinUltimate1995/jupiter-swap-python)** — Jupiter swap client for Python. Async. Typed.
|
|
207
|
+
- **[pumpfun-python](https://github.com/JinUltimate1995/pumpfun-python)** — PumpFun bonding curve + PumpSwap AMM. Direct swaps from Python.
|
|
208
|
+
- **[solana-rpc-resilient](https://github.com/JinUltimate1995/solana-rpc-resilient)** — Fault-tolerant Solana RPC with automatic failover.
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
<h1>dexscreener-python</h1>
|
|
3
|
+
<p><strong>async dexscreener api client for python.</strong></p>
|
|
4
|
+
<p>rate limiting. 429 retry. response caching. request dedup. works on any chain.</p>
|
|
5
|
+
|
|
6
|
+
<br/>
|
|
7
|
+
|
|
8
|
+
<a href="https://github.com/JinUltimate1995/dexscreener-python/actions"><img src="https://img.shields.io/github/actions/workflow/status/JinUltimate1995/dexscreener-python/ci.yml?branch=main&style=flat-square&label=tests" /></a>
|
|
9
|
+
<a href="https://pypi.org/project/dexscreener-python/"><img src="https://img.shields.io/pypi/v/dexscreener-python?style=flat-square" /></a>
|
|
10
|
+
<img src="https://img.shields.io/pypi/pyversions/dexscreener-python?style=flat-square" />
|
|
11
|
+
<img src="https://img.shields.io/badge/typed-py.typed-blue?style=flat-square" />
|
|
12
|
+
<img src="https://img.shields.io/badge/async-first-blue?style=flat-square" />
|
|
13
|
+
<img src="https://img.shields.io/badge/license-MIT-green?style=flat-square" />
|
|
14
|
+
</div>
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
> extracted from a production trading system. handles rate limits gracefully.
|
|
19
|
+
|
|
20
|
+
## 🛡️ Why this library?
|
|
21
|
+
|
|
22
|
+
dexscreener's api is free and powerful, but:
|
|
23
|
+
|
|
24
|
+
- **429s kill your app** if you don't handle them
|
|
25
|
+
- **no python client exists** that handles rate limits properly
|
|
26
|
+
- **concurrent requests** for the same token waste your quota
|
|
27
|
+
|
|
28
|
+
this library solves all three:
|
|
29
|
+
|
|
30
|
+
```python
|
|
31
|
+
from dexscreener import DexScreenerClient
|
|
32
|
+
|
|
33
|
+
async with DexScreenerClient() as client:
|
|
34
|
+
# get all trading pairs for a token
|
|
35
|
+
pairs = await client.get_token_pairs("solana", "So11111111111111111111111111111111111111112")
|
|
36
|
+
|
|
37
|
+
for pair in pairs:
|
|
38
|
+
print(f"{pair.base_token_symbol}: ${pair.price_usd} | liq: ${pair.liquidity_usd}")
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
429s → automatic exponential backoff (3s → 6s → 12s).
|
|
42
|
+
duplicate requests → deduplicated into one HTTP call.
|
|
43
|
+
results → cached with configurable TTL.
|
|
44
|
+
|
|
45
|
+
## 📦 Install
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
pip install dexscreener-python
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
requires python 3.11+
|
|
52
|
+
|
|
53
|
+
## ⚡ Features
|
|
54
|
+
|
|
55
|
+
| feature | description |
|
|
56
|
+
|---|---|
|
|
57
|
+
| **any chain** | solana, ethereum, base, bsc, arbitrum — anything dexscreener supports |
|
|
58
|
+
| **adaptive rate limiting** | token bucket that backs off on 429 and recovers after success |
|
|
59
|
+
| **global 429 cooldown** | one 429 pauses ALL requests briefly (prevents request storms) |
|
|
60
|
+
| **response cache** | configurable TTL per endpoint. concurrent calls share one request. |
|
|
61
|
+
| **batch support** | fetch up to 30 tokens in a single request |
|
|
62
|
+
| **typed data** | `DexPairData` dataclass with computed properties (buy/sell ratio, age, etc.) |
|
|
63
|
+
|
|
64
|
+
## 🔧 Usage
|
|
65
|
+
|
|
66
|
+
### 💰 Token Pairs
|
|
67
|
+
|
|
68
|
+
```python
|
|
69
|
+
pairs = await client.get_token_pairs("solana", token_address)
|
|
70
|
+
|
|
71
|
+
best_pair = pairs[0] # sorted by liquidity (highest first)
|
|
72
|
+
print(f"Price: ${best_pair.price_usd}")
|
|
73
|
+
print(f"Liquidity: ${best_pair.liquidity_usd}")
|
|
74
|
+
print(f"24h Volume: ${best_pair.volume_24h}")
|
|
75
|
+
print(f"Buy/Sell 5m: {best_pair.buy_sell_ratio_5m:.2f}")
|
|
76
|
+
print(f"Age: {best_pair.pair_age_seconds // 3600}h")
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### 💵 Token Price
|
|
80
|
+
|
|
81
|
+
```python
|
|
82
|
+
price = await client.get_token_price("solana", token_address)
|
|
83
|
+
print(f"${price}")
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### 📦 Batch Fetch (up to 30 tokens)
|
|
87
|
+
|
|
88
|
+
```python
|
|
89
|
+
data = await client.get_tokens_batch("solana", [addr1, addr2, addr3])
|
|
90
|
+
for addr, pair in data.items():
|
|
91
|
+
print(f"{pair.base_token_symbol}: ${pair.price_usd}")
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### 🔍 Search
|
|
95
|
+
|
|
96
|
+
```python
|
|
97
|
+
results = await client.search_pairs("BONK")
|
|
98
|
+
for pair in results:
|
|
99
|
+
print(f"{pair.base_token_symbol} on {pair.dex_id}: ${pair.price_usd}")
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### 🚀 Boosted Tokens
|
|
103
|
+
|
|
104
|
+
```python
|
|
105
|
+
boosted = await client.get_boosted_tokens("solana")
|
|
106
|
+
# boosted tokens are paid promotions — treat with caution
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### 🎯 Specific Pair
|
|
110
|
+
|
|
111
|
+
```python
|
|
112
|
+
pair = await client.get_pair_by_address("solana", pair_address)
|
|
113
|
+
print(f"{pair.dex_id}: ${pair.price_usd}")
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## 📋 DexPairData
|
|
117
|
+
|
|
118
|
+
all methods return `DexPairData` objects with these fields:
|
|
119
|
+
|
|
120
|
+
| field | type | description |
|
|
121
|
+
|---|---|---|
|
|
122
|
+
| `chain_id` | str | chain identifier |
|
|
123
|
+
| `dex_id` | str | dex identifier (raydium, uniswap_v3, etc.) |
|
|
124
|
+
| `pair_address` | str | pair contract address |
|
|
125
|
+
| `base_token_address` | str | base token mint/address |
|
|
126
|
+
| `base_token_symbol` | str | token symbol |
|
|
127
|
+
| `price_usd` | Decimal | current price in USD |
|
|
128
|
+
| `price_native` | Decimal | price in native token (SOL, ETH, etc.) |
|
|
129
|
+
| `liquidity_usd` | Decimal | total liquidity in USD |
|
|
130
|
+
| `volume_5m / 1h / 6h / 24h` | Decimal | volume by timeframe |
|
|
131
|
+
| `price_change_5m / 1h / 6h / 24h` | Decimal | price change % |
|
|
132
|
+
| `buys_5m / 1h / 24h` | int | buy transactions |
|
|
133
|
+
| `sells_5m / 1h / 24h` | int | sell transactions |
|
|
134
|
+
| `pair_created_at` | int | unix timestamp (ms) |
|
|
135
|
+
|
|
136
|
+
computed properties:
|
|
137
|
+
|
|
138
|
+
| property | description |
|
|
139
|
+
|---|---|
|
|
140
|
+
| `buy_sell_ratio_5m` | buy/sell ratio over 5 minutes (>1 = more buying) |
|
|
141
|
+
| `buy_sell_ratio_1h` | buy/sell ratio over 1 hour |
|
|
142
|
+
| `has_liquidity` | True if liquidity > $1000 |
|
|
143
|
+
| `pair_age_seconds` | pair age in seconds |
|
|
144
|
+
|
|
145
|
+
## ⚙️ Configuration
|
|
146
|
+
|
|
147
|
+
```python
|
|
148
|
+
client = DexScreenerClient(
|
|
149
|
+
rate_limit=5.0, # requests/second (default: 5.0, dexscreener limit is ~300/min)
|
|
150
|
+
cache_ttl=8.0, # default cache TTL in seconds
|
|
151
|
+
)
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
---
|
|
155
|
+
|
|
156
|
+
## 🆚 Comparison
|
|
157
|
+
|
|
158
|
+
| | dexscreener-python | raw `httpx` / `requests` |
|
|
159
|
+
|---|---|---|
|
|
160
|
+
| Rate limiting | ✅ adaptive token bucket | ❌ manual |
|
|
161
|
+
| 429 retry | ✅ exponential backoff | ❌ crash |
|
|
162
|
+
| Global cooldown | ✅ one 429 pauses all | ❌ per-request only |
|
|
163
|
+
| Response cache | ✅ TTL + dedup | ❌ manual |
|
|
164
|
+
| Typed data | ✅ `DexPairData` dataclass | ❌ raw dicts |
|
|
165
|
+
| Batch support | ✅ up to 30 tokens | ❌ manual loop |
|
|
166
|
+
| Async | ✅ native | depends |
|
|
167
|
+
|
|
168
|
+
## License
|
|
169
|
+
|
|
170
|
+
MIT
|
|
171
|
+
|
|
172
|
+
---
|
|
173
|
+
|
|
174
|
+
## 📦 Also by JinUltimate1995
|
|
175
|
+
|
|
176
|
+
- **[jupiter-swap-python](https://github.com/JinUltimate1995/jupiter-swap-python)** — Jupiter swap client for Python. Async. Typed.
|
|
177
|
+
- **[pumpfun-python](https://github.com/JinUltimate1995/pumpfun-python)** — PumpFun bonding curve + PumpSwap AMM. Direct swaps from Python.
|
|
178
|
+
- **[solana-rpc-resilient](https://github.com/JinUltimate1995/solana-rpc-resilient)** — Fault-tolerant Solana RPC with automatic failover.
|