changenow-client
Modern, fully typed Python client library for the ChangeNOW cryptocurrency exchange API (v2).
Official API documentation: https://documenter.getpostman.com/view/8180765/SVfTPnM8
The PyPI package is changenow-client. Import it as changenow.
Features
- ⚡ Standard & Fixed-Rate Swaps — Floating estimates and fixed-rate locks via
rateId. - 🪙 Multi-Network Assets — Currencies, pairs, limits, and fees include the network (BTC, ERC-20, TRC-20, and the rest of the v2 catalog).
- 🐍 Modern Pythonic Design — Type annotations, context manager support (
with ChangeNow(...) as client:), and a dedicatedChangeNowError. - 🔒 Non-Custodial — Create an exchange, then poll status until ChangeNOW pays the payout address.
Installation
pip install changenow-client
Requires Python 3.10 or higher.
Authentication
Partner attribution is the partner API key. The client sends it in one header:
from changenow import CHANGENOW_API_KEY_HEADER
print(CHANGENOW_API_KEY_HEADER) # "x-changenow-api-key"
TODO: the partner header name is pending confirmation from ChangeNOW. The published v2 docs and 401 text use x-changenow-api-key. The available-pairs sample uses x-api-key. Change CHANGENOW_API_KEY_HEADER in src/changenow/client.py if ChangeNOW confirms a different name. Do not add a second partner header until they confirm one.
user_id and payload on create are optional and stay disabled until ChangeNOW enables "Special partner fields". contact_email and source are optional create fields from the public examples. Pass the end-user IP as user_ip to send the documented x-forwarded-for header.
Quick Start (Safe Check)
The quick check only reads currencies, the minimum, an estimate, and an address check. It does not create an exchange:
import os
from changenow import ChangeNow
with ChangeNow(api_key=os.environ["CHANGENOW_API_KEY"]) as client:
currencies = client.get_currencies(active=True, flow="standard")
print(f"Available currencies: {len(currencies)}")
minimum = client.get_min_amount("btc", "eth", "btc", "eth", flow="standard")
print(f"Minimum: {minimum['minAmount']} BTC")
estimate = client.get_estimated_amount(
"btc",
"eth",
from_amount="0.1",
from_network="btc",
to_network="eth",
flow="standard",
)
print(f"Estimated receive amount: {estimate['toAmount']} ETH")
Usage Guide
1. Creating a Standard (Floating) Exchange
from changenow import ChangeNow, ChangeNowError
with ChangeNow(api_key="your_api_key") as client:
order = client.create_exchange(
"btc",
"eth",
"0x742d35Cc6634C0532925a3b844Bc454e4438f44e",
from_network="btc",
to_network="eth",
from_amount="0.1",
refund_address="bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh",
flow="standard",
user_id="user-42", # optional, requires Special partner fields
contact_email="trader@example.com",
source="wallet",
)
print(f"Send BTC to: {order['payinAddress']}")
print(f"Exchange id: {order['id']}")
2. Creating a Fixed-Rate Exchange
quote = client.get_estimated_amount(
"btc",
"eth",
from_amount="0.1",
from_network="btc",
to_network="eth",
flow="fixed-rate",
use_rate_id=True,
)
fixed_order = client.create_exchange(
"btc",
"eth",
"0x742d35Cc6634C0532925a3b844Bc454e4438f44e",
from_network="btc",
to_network="eth",
from_amount="0.1",
flow="fixed-rate",
rate_id=quote["rateId"],
)
print(f"Locked until {quote['validUntil']}")
print(f"Payout: {fixed_order['toAmount']} ETH")
3. Tracking Status
status = client.get_exchange(order["id"])
print(status["status"]) # waiting, confirming, exchanging, sending, finished, ...
if status["status"] == "finished":
print(f"Payout hash: {status.get('payoutHash')}")
API Reference
ChangeNow Class
Constructor
ChangeNow(
api_key: str | None = None,
base_url: str | None = None,
timeout: float = 30.0,
user_agent: str | None = None,
user_ip: str | None = None,
)
| Parameter | Type | Default | Description |
|---|---|---|---|
api_key |
str |
CHANGENOW_API_KEY |
Partner API key. |
base_url |
str |
"https://api.changenow.io/v2" |
API base URL. |
timeout |
float |
30.0 |
Request timeout in seconds. |
user_agent |
str |
"changenow-client/0.1.0" |
User-Agent header. |
user_ip |
str |
unset | Sent as x-forwarded-for. |
Methods
get_currencies(...)—GET /exchange/currenciesget_available_pairs(...)—GET /exchange/available-pairsget_min_amount(...)—GET /exchange/min-amountget_range(...)—GET /exchange/rangeget_estimated_amount(...)—GET /exchange/estimated-amount(standard and fixed-rate)create_exchange(...)—POST /exchangeget_exchange(exchange_id)—GET /exchange/by-idvalidate_address(currency, address)—GET /validate/addressget_user_addresses(name)—GET /addresses-by-nameget_network_fee(...)—GET /exchange/network-feeget_market_estimate(...)—GET /markets/estimate(crypto and fiat, informational, fees excluded)get_exchanges(...)—GET /exchanges(private API key)get_exchange_actions(exchange_id)—GET /exchange/actionscontinue_exchange(exchange_id)—POST /exchange/continuerefund_exchange(...)—POST /exchange/refundget_private_transfer_estimate(...)—GET /private-transfer/estimated-amountcreate_private_transfer(...)—POST /private-transfer
Several of these routes are enabled per partner key. A 403 means the endpoint is not activated for that key.
Error Handling
from changenow import ChangeNow, ChangeNowError
try:
with ChangeNow(api_key="invalid_key") as client:
client.get_currencies()
except ChangeNowError as exc:
print(f"{exc.code}: {exc.message} (HTTP {exc.status_code})")
Address validation does not raise for a bad address. The response is HTTP 200 with result: false.
Live Tests
Unit tests mock HTTP and always run. The live suite calls the real API and runs only when CHANGENOW_API_KEY is set. It is read-only: currencies, minimum amount, a standard-flow estimate, and address validation. It never creates an exchange.
uv run pytest -m "not live"
CHANGENOW_API_KEY=your_key uv run pytest -m live
License
ISC
Metadata
Release files for changenow-client 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| changenow_client-0.1.0.tar.gz | 25.1 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| changenow_client-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 35.8 kB
Release files / changenow_client-0.1.0.tar.gz
| Download URL | changenow_client-0.1.0.tar.gz |
|---|---|
| Size | 25.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
4f04f2e5944485f3a1d0363e7889fca0318b6ccdc620bfa97d9a877185add8e3
|
|
BLAKE2b-256 checksum How to use checksums |
811434cf3f4a938012b68ca4de3ab6fd568257021c90cd7c9b3034681f084fdb
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.13.6 {"installer":{"name":"uv","version":"0.13.6","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / changenow_client-0.1.0-py3-none-any.whl
| Download URL | changenow_client-0.1.0-py3-none-any.whl |
|---|---|
| Size | 10.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
9541027f845b73cd3f920d3cd0946a69df0ed1dec6c2ead04430fabb8a312f12
|
|
BLAKE2b-256 checksum How to use checksums |
94c1734775240fef98b8f28969fd20816fdb982ebd46e582b034722c54f53d81
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.13.6 {"installer":{"name":"uv","version":"0.13.6","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Security for changenow-client 0.1.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.