satoshi-api 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.
- satoshi_api-0.1.0/.env.example +25 -0
- satoshi_api-0.1.0/.env.production.example +10 -0
- satoshi_api-0.1.0/.github/workflows/ci.yml +38 -0
- satoshi_api-0.1.0/.github/workflows/pages.yml +34 -0
- satoshi_api-0.1.0/.github/workflows/publish.yml +26 -0
- satoshi_api-0.1.0/.gitignore +10 -0
- satoshi_api-0.1.0/CHANGELOG.md +49 -0
- satoshi_api-0.1.0/CLAUDE.md +61 -0
- satoshi_api-0.1.0/Dockerfile +26 -0
- satoshi_api-0.1.0/LICENSE +21 -0
- satoshi_api-0.1.0/PKG-INFO +209 -0
- satoshi_api-0.1.0/README.md +175 -0
- satoshi_api-0.1.0/blog-post.md +191 -0
- satoshi_api-0.1.0/cloudflared-config.yml.example +12 -0
- satoshi_api-0.1.0/demo/dashboard.html +176 -0
- satoshi_api-0.1.0/docker-compose.prod.yml +22 -0
- satoshi_api-0.1.0/docker-compose.yml +16 -0
- satoshi_api-0.1.0/docs/BUSINESS_PLAN.md +373 -0
- satoshi_api-0.1.0/docs/COMPETITIVE_ANALYSIS.md +190 -0
- satoshi_api-0.1.0/docs/SCOPE_OF_WORK.md +483 -0
- satoshi_api-0.1.0/docs/bitcoin-conf-example.conf +16 -0
- satoshi_api-0.1.0/docs/self-hosting.md +154 -0
- satoshi_api-0.1.0/docs/website/index.html +458 -0
- satoshi_api-0.1.0/pyproject.toml +47 -0
- satoshi_api-0.1.0/scripts/create_api_key.py +45 -0
- satoshi_api-0.1.0/scripts/security_check.sh +99 -0
- satoshi_api-0.1.0/scripts/seed_db.py +23 -0
- satoshi_api-0.1.0/src/bitcoin_api/__init__.py +8 -0
- satoshi_api-0.1.0/src/bitcoin_api/auth.py +67 -0
- satoshi_api-0.1.0/src/bitcoin_api/cache.py +155 -0
- satoshi_api-0.1.0/src/bitcoin_api/config.py +42 -0
- satoshi_api-0.1.0/src/bitcoin_api/db.py +136 -0
- satoshi_api-0.1.0/src/bitcoin_api/dependencies.py +39 -0
- satoshi_api-0.1.0/src/bitcoin_api/l402.py +120 -0
- satoshi_api-0.1.0/src/bitcoin_api/lightning.py +97 -0
- satoshi_api-0.1.0/src/bitcoin_api/main.py +437 -0
- satoshi_api-0.1.0/src/bitcoin_api/models.py +195 -0
- satoshi_api-0.1.0/src/bitcoin_api/pricing.py +66 -0
- satoshi_api-0.1.0/src/bitcoin_api/rate_limit.py +92 -0
- satoshi_api-0.1.0/src/bitcoin_api/routers/__init__.py +0 -0
- satoshi_api-0.1.0/src/bitcoin_api/routers/blocks.py +325 -0
- satoshi_api-0.1.0/src/bitcoin_api/routers/fees.py +225 -0
- satoshi_api-0.1.0/src/bitcoin_api/routers/mempool.py +240 -0
- satoshi_api-0.1.0/src/bitcoin_api/routers/mining.py +100 -0
- satoshi_api-0.1.0/src/bitcoin_api/routers/network.py +201 -0
- satoshi_api-0.1.0/src/bitcoin_api/routers/prices.py +90 -0
- satoshi_api-0.1.0/src/bitcoin_api/routers/status.py +78 -0
- satoshi_api-0.1.0/src/bitcoin_api/routers/transactions.py +348 -0
- satoshi_api-0.1.0/start-api.bat +4 -0
- satoshi_api-0.1.0/static/index.html +516 -0
- satoshi_api-0.1.0/tests/conftest.py +273 -0
- satoshi_api-0.1.0/tests/locustfile.py +42 -0
- satoshi_api-0.1.0/tests/test_api.py +828 -0
- satoshi_api-0.1.0/tests/test_e2e.py +213 -0
- satoshi_api-0.1.0/tests/test_l402.py +285 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Bitcoin Core RPC connection
|
|
2
|
+
BITCOIN_RPC_HOST=127.0.0.1
|
|
3
|
+
BITCOIN_RPC_PORT=8332
|
|
4
|
+
# For testnet: BITCOIN_RPC_PORT=18332
|
|
5
|
+
# For signet: BITCOIN_RPC_PORT=38332
|
|
6
|
+
# For regtest: BITCOIN_RPC_PORT=18443
|
|
7
|
+
BITCOIN_RPC_USER=
|
|
8
|
+
BITCOIN_RPC_PASSWORD=
|
|
9
|
+
# Optional: path to Bitcoin Core data directory (for cookie auth)
|
|
10
|
+
BITCOIN_DATADIR=
|
|
11
|
+
|
|
12
|
+
# API settings
|
|
13
|
+
API_PORT=9332
|
|
14
|
+
API_HOST=0.0.0.0
|
|
15
|
+
API_DB_PATH=data/bitcoin_api.db
|
|
16
|
+
|
|
17
|
+
# CORS origins (comma-separated). Use * only for local/dev.
|
|
18
|
+
# Default: http://localhost:3000,http://localhost:9332
|
|
19
|
+
CORS_ORIGINS=http://localhost:3000,http://localhost:9332
|
|
20
|
+
|
|
21
|
+
# Rate limits (requests per minute)
|
|
22
|
+
RATE_LIMIT_ANONYMOUS=30
|
|
23
|
+
RATE_LIMIT_FREE=100
|
|
24
|
+
RATE_LIMIT_PRO=500
|
|
25
|
+
RATE_LIMIT_ENTERPRISE=2000
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# Production environment for Satoshi API
|
|
2
|
+
# Copy to .env.production and fill in values
|
|
3
|
+
|
|
4
|
+
BITCOIN_RPC_HOST=host.docker.internal
|
|
5
|
+
BITCOIN_RPC_PORT=8332
|
|
6
|
+
BITCOIN_RPC_USER=satoshiapi
|
|
7
|
+
BITCOIN_RPC_PASSWORD=
|
|
8
|
+
CORS_ORIGINS=https://api.yourdomain.dev
|
|
9
|
+
API_HOST=0.0.0.0
|
|
10
|
+
API_PORT=9332
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main, master]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main, master]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
test:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
strategy:
|
|
13
|
+
matrix:
|
|
14
|
+
python-version: ["3.10", "3.11", "3.12"]
|
|
15
|
+
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v4
|
|
18
|
+
|
|
19
|
+
- name: Set up Python ${{ matrix.python-version }}
|
|
20
|
+
uses: actions/setup-python@v5
|
|
21
|
+
with:
|
|
22
|
+
python-version: ${{ matrix.python-version }}
|
|
23
|
+
|
|
24
|
+
- name: Install dependencies
|
|
25
|
+
run: |
|
|
26
|
+
pip install git+https://github.com/Bortlesboat/bitcoinlib-rpc.git
|
|
27
|
+
pip install -e ".[dev]"
|
|
28
|
+
|
|
29
|
+
- name: Lint
|
|
30
|
+
run: ruff check src/ tests/
|
|
31
|
+
|
|
32
|
+
- name: Run tests with coverage
|
|
33
|
+
run: pytest -v --cov=bitcoin_api --cov-report=term-missing --cov-fail-under=70
|
|
34
|
+
|
|
35
|
+
- name: Docker build (smoke test)
|
|
36
|
+
if: matrix.python-version == '3.12'
|
|
37
|
+
continue-on-error: true
|
|
38
|
+
run: docker build -t bitcoin-api-test .
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
name: Deploy Landing Page
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main, master]
|
|
6
|
+
paths: ["docs/website/**"]
|
|
7
|
+
workflow_dispatch:
|
|
8
|
+
|
|
9
|
+
permissions:
|
|
10
|
+
contents: read
|
|
11
|
+
pages: write
|
|
12
|
+
id-token: write
|
|
13
|
+
|
|
14
|
+
concurrency:
|
|
15
|
+
group: pages
|
|
16
|
+
cancel-in-progress: true
|
|
17
|
+
|
|
18
|
+
jobs:
|
|
19
|
+
deploy:
|
|
20
|
+
environment:
|
|
21
|
+
name: github-pages
|
|
22
|
+
url: ${{ steps.deployment.outputs.page_url }}
|
|
23
|
+
runs-on: ubuntu-latest
|
|
24
|
+
steps:
|
|
25
|
+
- uses: actions/checkout@v4
|
|
26
|
+
|
|
27
|
+
- uses: actions/configure-pages@v4
|
|
28
|
+
|
|
29
|
+
- uses: actions/upload-pages-artifact@v3
|
|
30
|
+
with:
|
|
31
|
+
path: docs/website
|
|
32
|
+
|
|
33
|
+
- id: deployment
|
|
34
|
+
uses: actions/deploy-pages@v4
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
release:
|
|
5
|
+
types: [published]
|
|
6
|
+
|
|
7
|
+
permissions:
|
|
8
|
+
contents: read
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
publish:
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
environment: pypi
|
|
14
|
+
permissions:
|
|
15
|
+
id-token: write
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v4
|
|
18
|
+
- uses: actions/setup-python@v5
|
|
19
|
+
with:
|
|
20
|
+
python-version: "3.12"
|
|
21
|
+
- name: Install build tools
|
|
22
|
+
run: pip install build
|
|
23
|
+
- name: Build package
|
|
24
|
+
run: python -m build
|
|
25
|
+
- name: Publish to PyPI
|
|
26
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## [0.2.1] - 2026-03-06
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- `GET /tx/{txid}/hex` — raw transaction hex string
|
|
7
|
+
- `GET /tx/{txid}/outspends` — spending status of each output (spent/unspent via UTXO set)
|
|
8
|
+
- `GET /blocks/{hash}/header` — block header as hex string
|
|
9
|
+
- `GET /fees/mempool-blocks` — projected next blocks from mempool, grouped by fee rate
|
|
10
|
+
- `GET /prices` — BTC price in 6 currencies from CoinGecko (60s cache)
|
|
11
|
+
- `GET /network/validate-address/{address}` — validate a Bitcoin address
|
|
12
|
+
- 9 new unit tests + 6 new e2e tests (80 unit + 21 e2e total)
|
|
13
|
+
|
|
14
|
+
### Changed
|
|
15
|
+
- RPC whitelist: added `getblockheader`, `validateaddress` (now 19 commands)
|
|
16
|
+
- Total endpoints: 27 → 33
|
|
17
|
+
|
|
18
|
+
## [0.2.0] - 2026-03-06
|
|
19
|
+
|
|
20
|
+
### Added
|
|
21
|
+
- `GET /mempool/txids` — all transaction IDs in the mempool
|
|
22
|
+
- `GET /mempool/recent` — most recent mempool entries sorted by time (configurable count, max 100)
|
|
23
|
+
- `GET /blocks/tip/height` — chain tip height
|
|
24
|
+
- `GET /blocks/tip/hash` — chain tip block hash
|
|
25
|
+
- `GET /blocks/{hash}/txids` — transaction IDs in a block
|
|
26
|
+
- `GET /blocks/{hash}/txs` — full transactions in a block (paginated, default 25, max 100)
|
|
27
|
+
- `GET /tx/{txid}/status` — transaction confirmation status
|
|
28
|
+
- `GET /network/difficulty` — difficulty epoch progress, blocks remaining, estimated retarget date
|
|
29
|
+
- Competitive analysis document (vs mempool.space — 161 endpoints mapped)
|
|
30
|
+
- 12 new unit tests + 6 new e2e tests (71 unit + 15 e2e total)
|
|
31
|
+
|
|
32
|
+
### Changed
|
|
33
|
+
- RPC whitelist: added `getrawmempool` (now 17 commands)
|
|
34
|
+
- Total endpoints: 19 → 27
|
|
35
|
+
|
|
36
|
+
## [0.1.0] - 2026-03-05
|
|
37
|
+
|
|
38
|
+
### Added
|
|
39
|
+
- REST API with 19 endpoints across 7 categories (blocks, transactions, fees, mempool, mining, network, status)
|
|
40
|
+
- Tiered API key authentication (anonymous, free, pro, enterprise)
|
|
41
|
+
- Sliding-window rate limiting (per-minute in-memory + daily DB-backed)
|
|
42
|
+
- TTL caching with reorg-safe depth awareness
|
|
43
|
+
- Input validation (txid format, block height range, fee target bounds)
|
|
44
|
+
- Structured error responses with request IDs
|
|
45
|
+
- Docker support with health checks
|
|
46
|
+
- 59 unit tests + 9 e2e tests with full endpoint coverage
|
|
47
|
+
- Security hardening: POST auth requirements, node fingerprint redaction, access logging, body size limits
|
|
48
|
+
- Production deployment support: Cloudflare Tunnel config, docker-compose.prod, self-hosting docs
|
|
49
|
+
- Bitcoin Core RPC whitelist configuration example
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# Satoshi API -- Project Instructions
|
|
2
|
+
|
|
3
|
+
## Scope of Work (MANDATORY)
|
|
4
|
+
|
|
5
|
+
**`docs/SCOPE_OF_WORK.md` is the canonical project document. It MUST be updated with every change.**
|
|
6
|
+
|
|
7
|
+
After any code change, documentation update, or architectural decision:
|
|
8
|
+
1. Update the relevant section(s) in `docs/SCOPE_OF_WORK.md`
|
|
9
|
+
2. If adding/removing endpoints: update Section 3 (API Surface)
|
|
10
|
+
3. If adding/removing files: update Section 6 (Deliverables)
|
|
11
|
+
4. If fixing bugs or addressing review findings: update Section 5.2 (Critical Issues Fixed)
|
|
12
|
+
5. If changing security controls: update Section 4 (Security Model)
|
|
13
|
+
6. If test count changes: update Section 6.1 (sprint table totals)
|
|
14
|
+
7. If adding known limitations: update Section 5.3
|
|
15
|
+
8. If deployment steps change: update Section 7
|
|
16
|
+
|
|
17
|
+
The SOW is the single source of truth for what this project is, what it does, and what state it's in. Treat it like a living design doc, not a one-time artifact.
|
|
18
|
+
|
|
19
|
+
## Architecture
|
|
20
|
+
|
|
21
|
+
- **Stack:** FastAPI + bitcoinlib-rpc + SQLite (WAL mode)
|
|
22
|
+
- **Entry point:** `src/bitcoin_api/main.py`
|
|
23
|
+
- **Config:** Pydantic Settings from env vars (`config.py`), RPC password is `SecretStr`
|
|
24
|
+
- **Auth:** API key via `X-API-Key` header, tier-based (anonymous/free/pro/lightning/enterprise)
|
|
25
|
+
- **L402:** Lightning micropayments via HTTP 402 + macaroon challenge (disabled by default)
|
|
26
|
+
- **Rate limiting:** Sliding window (in-memory) + daily (DB-backed)
|
|
27
|
+
- **Caching:** Per-cache locks, reorg-safe depth awareness, bounded LRU for hash mappings
|
|
28
|
+
|
|
29
|
+
## Testing
|
|
30
|
+
|
|
31
|
+
- Run tests: `python -m pytest tests/test_api.py tests/test_l402.py -q`
|
|
32
|
+
- E2E (requires running API): `python -m pytest tests/test_e2e.py -m e2e`
|
|
33
|
+
- Load test: `locust -f tests/locustfile.py --host http://localhost:9332`
|
|
34
|
+
- Security check: `SATOSHI_API_KEY=<key> bash scripts/security_check.sh`
|
|
35
|
+
- `authed_client` fixture for POST endpoint tests (requires API key in DB)
|
|
36
|
+
- `client` fixture is anonymous -- use for GET tests and auth rejection tests
|
|
37
|
+
|
|
38
|
+
## Conventions
|
|
39
|
+
|
|
40
|
+
- Response envelope: `{ data, meta }` on success, `{ error }` on failure
|
|
41
|
+
- All errors include `request_id` for tracing
|
|
42
|
+
- POST endpoints require `free` tier or above (403 for anonymous)
|
|
43
|
+
- Node version info redacted for anonymous users on `/network`
|
|
44
|
+
- Secrets: never log, never commit. RPC password uses `SecretStr`.
|
|
45
|
+
|
|
46
|
+
## Key Files
|
|
47
|
+
|
|
48
|
+
| File | Purpose |
|
|
49
|
+
|------|---------|
|
|
50
|
+
| `docs/SCOPE_OF_WORK.md` | Living project document (KEEP UPDATED) |
|
|
51
|
+
| `src/bitcoin_api/main.py` | App, middleware, exception handlers |
|
|
52
|
+
| `src/bitcoin_api/config.py` | Settings from env vars |
|
|
53
|
+
| `src/bitcoin_api/auth.py` | API key auth |
|
|
54
|
+
| `src/bitcoin_api/rate_limit.py` | Rate limiting |
|
|
55
|
+
| `src/bitcoin_api/cache.py` | TTL + LRU caching |
|
|
56
|
+
| `src/bitcoin_api/lightning.py` | Lightning client (Alby Hub + mock) |
|
|
57
|
+
| `src/bitcoin_api/l402.py` | L402 macaroon minting/verification |
|
|
58
|
+
| `src/bitcoin_api/pricing.py` | Endpoint pricing map (sats) |
|
|
59
|
+
| `tests/test_api.py` | Unit tests (80) |
|
|
60
|
+
| `tests/test_l402.py` | L402 tests (32) |
|
|
61
|
+
| `tests/test_e2e.py` | E2E tests (21) |
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
FROM python:3.12-slim
|
|
2
|
+
|
|
3
|
+
WORKDIR /app
|
|
4
|
+
|
|
5
|
+
COPY pyproject.toml .
|
|
6
|
+
COPY src/ src/
|
|
7
|
+
|
|
8
|
+
RUN apt-get update && apt-get install -y --no-install-recommends git && \
|
|
9
|
+
pip install --no-cache-dir git+https://github.com/Bortlesboat/bitcoinlib-rpc.git && \
|
|
10
|
+
pip install --no-cache-dir . && \
|
|
11
|
+
rm -rf /var/lib/apt/lists/*
|
|
12
|
+
|
|
13
|
+
COPY scripts/ scripts/
|
|
14
|
+
|
|
15
|
+
RUN mkdir -p /app/data && \
|
|
16
|
+
adduser --disabled-password --no-create-home apiuser && \
|
|
17
|
+
chown -R apiuser:apiuser /app/data
|
|
18
|
+
|
|
19
|
+
USER apiuser
|
|
20
|
+
|
|
21
|
+
EXPOSE 9332
|
|
22
|
+
|
|
23
|
+
HEALTHCHECK --interval=30s --timeout=5s \
|
|
24
|
+
CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:9332/healthz')" || exit 1
|
|
25
|
+
|
|
26
|
+
CMD ["python", "-m", "bitcoin_api.main"]
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Andy Barnes
|
|
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,209 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: satoshi-api
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Satoshi API — developer-friendly Bitcoin REST API powered by your own node
|
|
5
|
+
Project-URL: Homepage, https://bitcoinsapi.com
|
|
6
|
+
Project-URL: Documentation, https://bitcoinsapi.com/docs
|
|
7
|
+
Project-URL: Repository, https://github.com/Bortlesboat/bitcoin-api
|
|
8
|
+
Project-URL: Issues, https://github.com/Bortlesboat/bitcoin-api/issues
|
|
9
|
+
Author: Bortlesboat
|
|
10
|
+
License-Expression: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: api,bitcoin,blockchain,node,rest
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Framework :: FastAPI
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Topic :: Office/Business :: Financial
|
|
18
|
+
Requires-Python: >=3.10
|
|
19
|
+
Requires-Dist: bitcoinlib-rpc>=0.1.0
|
|
20
|
+
Requires-Dist: cachetools>=5.3
|
|
21
|
+
Requires-Dist: fastapi>=0.115.0
|
|
22
|
+
Requires-Dist: pydantic-settings>=2.0
|
|
23
|
+
Requires-Dist: uvicorn[standard]>=0.30.0
|
|
24
|
+
Provides-Extra: dev
|
|
25
|
+
Requires-Dist: httpx; extra == 'dev'
|
|
26
|
+
Requires-Dist: locust; extra == 'dev'
|
|
27
|
+
Requires-Dist: pytest; extra == 'dev'
|
|
28
|
+
Requires-Dist: pytest-asyncio; extra == 'dev'
|
|
29
|
+
Requires-Dist: pytest-cov; extra == 'dev'
|
|
30
|
+
Requires-Dist: ruff; extra == 'dev'
|
|
31
|
+
Provides-Extra: l402
|
|
32
|
+
Requires-Dist: httpx>=0.27.0; extra == 'l402'
|
|
33
|
+
Description-Content-Type: text/markdown
|
|
34
|
+
|
|
35
|
+
# Satoshi API
|
|
36
|
+
|
|
37
|
+
[](https://github.com/Bortlesboat/bitcoin-api/actions/workflows/ci.yml)
|
|
38
|
+
[](https://www.python.org/downloads/)
|
|
39
|
+
[](LICENSE)
|
|
40
|
+
|
|
41
|
+
The thinnest REST layer over your Bitcoin node. One `pip install`, instant API.
|
|
42
|
+
|
|
43
|
+
Powered by [bitcoinlib-rpc](https://github.com/Bortlesboat/bitcoinlib-rpc) for analyzed data (fees, mempool congestion, block stats) rather than raw RPC dumps. Part of the AI agent pipeline: **bitcoinlib-rpc** -> **satoshi-api** -> [bitcoin-mcp](https://github.com/Bortlesboat/bitcoin-mcp).
|
|
44
|
+
|
|
45
|
+
## Prerequisites
|
|
46
|
+
|
|
47
|
+
- **Bitcoin Core** running with `server=1` in `bitcoin.conf` (RPC enabled)
|
|
48
|
+
- Python 3.10+
|
|
49
|
+
|
|
50
|
+
**Node configuration notes:**
|
|
51
|
+
- Transaction lookups (`/tx/{txid}`, `/tx/{txid}/raw`) for confirmed transactions require `txindex=1`
|
|
52
|
+
- Block template (`/mining/nextblock`) requires at least one connected peer on mainnet
|
|
53
|
+
- Block stats (`/blocks/{height}/stats`) is unavailable for pruned blocks below the prune height
|
|
54
|
+
|
|
55
|
+
## Quick Start
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
pip install bitcoin-api # or: pip install -e . for local dev
|
|
59
|
+
|
|
60
|
+
# Point at your node
|
|
61
|
+
export BITCOIN_RPC_USER=your_user
|
|
62
|
+
export BITCOIN_RPC_PASSWORD=your_password
|
|
63
|
+
# For testnet: export BITCOIN_RPC_PORT=18332
|
|
64
|
+
# For signet: export BITCOIN_RPC_PORT=38332
|
|
65
|
+
|
|
66
|
+
# Run
|
|
67
|
+
bitcoin-api # or: satoshi-api
|
|
68
|
+
# -> http://localhost:9332/docs
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Docker
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
# Create .env with your node credentials
|
|
75
|
+
echo "BITCOIN_RPC_USER=your_user" > .env
|
|
76
|
+
echo "BITCOIN_RPC_PASSWORD=your_password" >> .env
|
|
77
|
+
|
|
78
|
+
docker compose up -d
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Endpoints
|
|
82
|
+
|
|
83
|
+
All endpoints are under `/api/v1/`. Interactive docs at `/docs`.
|
|
84
|
+
|
|
85
|
+
| Endpoint | Description |
|
|
86
|
+
|----------|-------------|
|
|
87
|
+
| `GET /health` | Node ping (no auth required) |
|
|
88
|
+
| `GET /status` | Sync progress, peers, disk usage |
|
|
89
|
+
| `GET /network` | Version, connections, relay fee |
|
|
90
|
+
| `GET /network/forks` | Chain tips / fork detection |
|
|
91
|
+
| `GET /network/difficulty` | Difficulty epoch progress, retarget estimate |
|
|
92
|
+
| `GET /blocks/latest` | Latest block analysis |
|
|
93
|
+
| `GET /blocks/tip/height` | Chain tip height |
|
|
94
|
+
| `GET /blocks/tip/hash` | Chain tip block hash |
|
|
95
|
+
| `GET /blocks/{height_or_hash}` | Block by height or hash |
|
|
96
|
+
| `GET /blocks/{height}/stats` | Raw block statistics |
|
|
97
|
+
| `GET /blocks/{hash}/txids` | Transaction IDs in a block |
|
|
98
|
+
| `GET /blocks/{hash}/txs` | Full transactions in a block (paginated) |
|
|
99
|
+
| `GET /blocks/{hash}/header` | Block header hex string |
|
|
100
|
+
| `GET /tx/{txid}` | Transaction analysis (fees, SegWit, Taproot, inscriptions) |
|
|
101
|
+
| `GET /tx/{txid}/raw` | Raw decoded transaction |
|
|
102
|
+
| `GET /tx/{txid}/status` | Confirmation status |
|
|
103
|
+
| `GET /tx/{txid}/hex` | Raw transaction hex string |
|
|
104
|
+
| `GET /tx/{txid}/outspends` | Spending status of each output |
|
|
105
|
+
| `GET /utxo/{txid}/{vout}` | UTXO lookup |
|
|
106
|
+
| `GET /mempool` | Mempool analysis (fee buckets, congestion) |
|
|
107
|
+
| `GET /mempool/info` | Raw mempool stats |
|
|
108
|
+
| `GET /mempool/tx/{txid}` | Mempool entry for a transaction |
|
|
109
|
+
| `GET /mempool/txids` | All transaction IDs in the mempool |
|
|
110
|
+
| `GET /mempool/recent` | Most recent mempool entries |
|
|
111
|
+
| `GET /fees` | Fee estimates (1, 3, 6, 25, 144 blocks) |
|
|
112
|
+
| `GET /fees/recommended` | Human-readable fee recommendation |
|
|
113
|
+
| `GET /fees/{target}` | Fee estimate for specific block target |
|
|
114
|
+
| `GET /fees/mempool-blocks` | Projected next blocks from mempool by fee rate |
|
|
115
|
+
| `GET /mining` | Hashrate, difficulty, retarget estimate |
|
|
116
|
+
| `GET /mining/nextblock` | Block template analysis |
|
|
117
|
+
| `GET /network/validate-address/{addr}` | Validate a Bitcoin address |
|
|
118
|
+
| `GET /prices` | BTC price in USD, EUR, GBP, JPY, CAD, AUD |
|
|
119
|
+
| `POST /decode` | Decode raw transaction hex |
|
|
120
|
+
| `POST /broadcast` | Broadcast signed transaction |
|
|
121
|
+
|
|
122
|
+
## Examples
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
# Health check
|
|
126
|
+
curl http://localhost:9332/api/v1/health
|
|
127
|
+
|
|
128
|
+
# Fee recommendation
|
|
129
|
+
curl http://localhost:9332/api/v1/fees/recommended
|
|
130
|
+
|
|
131
|
+
# Analyze a transaction
|
|
132
|
+
curl http://localhost:9332/api/v1/tx/a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d
|
|
133
|
+
|
|
134
|
+
# Latest block
|
|
135
|
+
curl http://localhost:9332/api/v1/blocks/latest
|
|
136
|
+
|
|
137
|
+
# With API key (higher rate limits)
|
|
138
|
+
curl -H "X-API-Key: your_key_here" http://localhost:9332/api/v1/mempool
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
## API Keys
|
|
142
|
+
|
|
143
|
+
Anonymous access works out of the box. For higher rate limits, create a key:
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
python scripts/create_api_key.py --tier free --label "my-app"
|
|
147
|
+
python scripts/create_api_key.py --tier pro --label "production"
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
Pass the key via `X-API-Key` header. Query parameter `?api_key=` is deprecated (sunset: 2026-09-01).
|
|
151
|
+
|
|
152
|
+
## Rate Limits
|
|
153
|
+
|
|
154
|
+
| Tier | Req/min | Req/day |
|
|
155
|
+
|------|---------|---------|
|
|
156
|
+
| Anonymous | 30 | 1,000 |
|
|
157
|
+
| Free (API key) | 100 | 10,000 |
|
|
158
|
+
| Pro | 500 | 100,000 |
|
|
159
|
+
| Enterprise | 2,000 | Unlimited |
|
|
160
|
+
|
|
161
|
+
Rate limit info in response headers: `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset`, `X-RateLimit-Daily-Limit`, `X-RateLimit-Daily-Remaining`.
|
|
162
|
+
|
|
163
|
+
Every response includes `X-Request-ID` (UUID) and `X-Auth-Tier` headers.
|
|
164
|
+
|
|
165
|
+
## Response Format
|
|
166
|
+
|
|
167
|
+
Standard envelope on all endpoints:
|
|
168
|
+
|
|
169
|
+
```json
|
|
170
|
+
{
|
|
171
|
+
"data": { ... },
|
|
172
|
+
"meta": {
|
|
173
|
+
"timestamp": "2026-03-05T12:00:00+00:00",
|
|
174
|
+
"request_id": "550e8400-e29b-41d4-a716-446655440000",
|
|
175
|
+
"node_height": 880000,
|
|
176
|
+
"chain": "main"
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
Errors:
|
|
182
|
+
|
|
183
|
+
```json
|
|
184
|
+
{
|
|
185
|
+
"error": {
|
|
186
|
+
"status": 404,
|
|
187
|
+
"title": "Not Found",
|
|
188
|
+
"detail": "Transaction not found",
|
|
189
|
+
"request_id": "550e8400-e29b-41d4-a716-446655440000"
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
## Input Validation
|
|
195
|
+
|
|
196
|
+
- Transaction IDs and block hashes must be exactly 64 hex characters
|
|
197
|
+
- Invalid formats return 422 immediately (no wasted RPC calls)
|
|
198
|
+
- Invalid API keys return 401 (not silent downgrade to anonymous)
|
|
199
|
+
|
|
200
|
+
## Development
|
|
201
|
+
|
|
202
|
+
```bash
|
|
203
|
+
pip install -e ".[dev]"
|
|
204
|
+
pytest
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
## License
|
|
208
|
+
|
|
209
|
+
MIT
|