namecheap-python 1.1.0__tar.gz → 1.3.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.
- namecheap_python-1.3.0/CLAUDE.md +2 -0
- namecheap_python-1.3.0/CONTRIBUTING.md +113 -0
- {namecheap_python-1.1.0 → namecheap_python-1.3.0}/PKG-INFO +96 -13
- {namecheap_python-1.1.0 → namecheap_python-1.3.0}/README.md +95 -12
- {namecheap_python-1.1.0 → namecheap_python-1.3.0}/pyproject.toml +1 -1
- {namecheap_python-1.1.0 → namecheap_python-1.3.0}/src/namecheap/__init__.py +16 -2
- {namecheap_python-1.1.0 → namecheap_python-1.3.0}/src/namecheap/_api/dns.py +73 -1
- {namecheap_python-1.1.0 → namecheap_python-1.3.0}/src/namecheap/_api/domains.py +93 -1
- namecheap_python-1.3.0/src/namecheap/_api/users.py +32 -0
- {namecheap_python-1.1.0 → namecheap_python-1.3.0}/src/namecheap/client.py +8 -0
- {namecheap_python-1.1.0 → namecheap_python-1.3.0}/src/namecheap/models.py +71 -0
- {namecheap_python-1.1.0 → namecheap_python-1.3.0}/src/namecheap_cli/__main__.py +183 -50
- {namecheap_python-1.1.0 → namecheap_python-1.3.0}/uv.lock +1 -1
- namecheap_python-1.1.0/docs/dev/README.md +0 -220
- namecheap_python-1.1.0/pending.md +0 -94
- {namecheap_python-1.1.0 → namecheap_python-1.3.0}/.env.example +0 -0
- {namecheap_python-1.1.0 → namecheap_python-1.3.0}/.github/cliff.toml +0 -0
- {namecheap_python-1.1.0 → namecheap_python-1.3.0}/.github/workflows/release.yml +0 -0
- {namecheap_python-1.1.0 → namecheap_python-1.3.0}/.gitignore +0 -0
- {namecheap_python-1.1.0 → namecheap_python-1.3.0}/.pre-commit-config.yaml +0 -0
- {namecheap_python-1.1.0 → namecheap_python-1.3.0}/CLI.md +0 -0
- {namecheap_python-1.1.0 → namecheap_python-1.3.0}/LICENSE +0 -0
- {namecheap_python-1.1.0 → namecheap_python-1.3.0}/MANIFEST.in +0 -0
- {namecheap_python-1.1.0 → namecheap_python-1.3.0}/examples/README.md +0 -0
- {namecheap_python-1.1.0 → namecheap_python-1.3.0}/examples/quickstart.py +0 -0
- {namecheap_python-1.1.0 → namecheap_python-1.3.0}/src/namecheap/_api/__init__.py +0 -0
- {namecheap_python-1.1.0 → namecheap_python-1.3.0}/src/namecheap/_api/base.py +0 -0
- {namecheap_python-1.1.0 → namecheap_python-1.3.0}/src/namecheap/errors.py +0 -0
- {namecheap_python-1.1.0 → namecheap_python-1.3.0}/src/namecheap/logging.py +0 -0
- {namecheap_python-1.1.0 → namecheap_python-1.3.0}/src/namecheap_cli/README.md +0 -0
- {namecheap_python-1.1.0 → namecheap_python-1.3.0}/src/namecheap_cli/__init__.py +0 -0
- {namecheap_python-1.1.0 → namecheap_python-1.3.0}/src/namecheap_cli/completion.py +0 -0
- {namecheap_python-1.1.0 → namecheap_python-1.3.0}/src/namecheap_dns_tui/README.md +0 -0
- {namecheap_python-1.1.0 → namecheap_python-1.3.0}/src/namecheap_dns_tui/__init__.py +0 -0
- {namecheap_python-1.1.0 → namecheap_python-1.3.0}/src/namecheap_dns_tui/__main__.py +0 -0
- {namecheap_python-1.1.0 → namecheap_python-1.3.0}/src/namecheap_dns_tui/assets/screenshot1.png +0 -0
- {namecheap_python-1.1.0 → namecheap_python-1.3.0}/src/namecheap_dns_tui/assets/screenshot2.png +0 -0
- {namecheap_python-1.1.0 → namecheap_python-1.3.0}/src/namecheap_dns_tui/assets/screenshot3.png +0 -0
- {namecheap_python-1.1.0 → namecheap_python-1.3.0}/src/namecheap_dns_tui/assets/screenshot4.png +0 -0
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
## Setup
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
git clone https://github.com/adriangalilea/namecheap-python.git
|
|
7
|
+
cd namecheap-python
|
|
8
|
+
uv sync --all-extras
|
|
9
|
+
pre-commit install
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## Code Quality
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
uv run ruff check # lint
|
|
16
|
+
uv run ruff check --fix # auto-fix
|
|
17
|
+
uv run ruff format # format
|
|
18
|
+
|
|
19
|
+
pre-commit run --all-files # run all hooks
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Building
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
uv build
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Release
|
|
29
|
+
|
|
30
|
+
Automated via GitHub Actions on push to `main`. If the version in `pyproject.toml` doesn't exist on PyPI, CI publishes automatically, creates a git tag, and generates a GitHub release with changelog via [git-cliff](https://git-cliff.org/).
|
|
31
|
+
|
|
32
|
+
Bump version in both `pyproject.toml` and `src/namecheap/__init__.py`.
|
|
33
|
+
|
|
34
|
+
## Debugging
|
|
35
|
+
|
|
36
|
+
```python
|
|
37
|
+
import logging
|
|
38
|
+
logging.basicConfig(level=logging.DEBUG)
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Or set `NAMECHEAP_DEBUG=true`.
|
|
42
|
+
|
|
43
|
+
## Architecture
|
|
44
|
+
|
|
45
|
+
### Project Structure
|
|
46
|
+
|
|
47
|
+
```
|
|
48
|
+
src/
|
|
49
|
+
├── namecheap/ # Core SDK
|
|
50
|
+
│ ├── client.py # Main client — wires up API classes
|
|
51
|
+
│ ├── models.py # Pydantic models (all API data types live here)
|
|
52
|
+
│ ├── errors.py # Exceptions with helpful messages
|
|
53
|
+
│ ├── logging.py # Colored logging and error display
|
|
54
|
+
│ └── _api/ # API implementations
|
|
55
|
+
│ ├── base.py # BaseAPI with _request() — all API calls go through here
|
|
56
|
+
│ ├── domains.py # namecheap.domains.* endpoints
|
|
57
|
+
│ └── dns.py # namecheap.domains.dns.* endpoints + builder
|
|
58
|
+
├── namecheap_cli/ # CLI (click)
|
|
59
|
+
│ ├── __main__.py # All commands in one file
|
|
60
|
+
│ └── completion.py # Shell completions
|
|
61
|
+
└── namecheap_dns_tui/ # TUI (textual)
|
|
62
|
+
└── __main__.py
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Key Patterns
|
|
66
|
+
|
|
67
|
+
**SDK layer** (`src/namecheap/_api/`):
|
|
68
|
+
- Every API class extends `BaseAPI` which provides `_request(command, params, model=, path=)`
|
|
69
|
+
- `_request` handles auth params, XML parsing, error handling, and Pydantic deserialization
|
|
70
|
+
- Pass `model=SomeModel` to auto-parse, or omit for raw dict access
|
|
71
|
+
- Pass `path="DotSeparated.Path"` to navigate the XML response
|
|
72
|
+
- Domain parsing uses `tldextract` to split into SLD/TLD — see any method in `dns.py`
|
|
73
|
+
- Return Pydantic models, not dicts — see `Nameservers`, `DNSRecord`, `Domain`, etc.
|
|
74
|
+
- Use `assert` for assumptions, `ValueError` for bad user input
|
|
75
|
+
|
|
76
|
+
**Models** (`src/namecheap/models.py`):
|
|
77
|
+
- `XMLModel` base handles XML attribute aliases (`@Name` → `name`) and boolean parsing
|
|
78
|
+
- All new data types go here and get exported from `__init__.py`
|
|
79
|
+
|
|
80
|
+
**CLI layer** (`src/namecheap_cli/__main__.py`):
|
|
81
|
+
- Commands use `@pass_config` decorator for client initialization
|
|
82
|
+
- Interactive operations use `rich.progress.Progress` with `SpinnerColumn` for loading
|
|
83
|
+
- Destructive operations use `rich.prompt.Confirm.ask` with `--yes/-y` to skip
|
|
84
|
+
- Table output via `rich.table.Table`, with `output_formatter()` for JSON/YAML alternatives
|
|
85
|
+
- Error handling: catch `NamecheapError`, print with `[red]`, `sys.exit(1)`
|
|
86
|
+
|
|
87
|
+
**DNS Builder** (`src/namecheap/_api/dns.py`):
|
|
88
|
+
- Fluent interface: `builder.a(...).mx(...).txt(...)` returns `Self` for chaining
|
|
89
|
+
- Default TTL is 1799 (displays as "Automatic" in Namecheap UI)
|
|
90
|
+
|
|
91
|
+
### Adding a New API Endpoint
|
|
92
|
+
|
|
93
|
+
1. Add Pydantic model in `models.py`, export from `__init__.py`
|
|
94
|
+
2. Add method in the appropriate `_api/*.py` file using `self._request()`
|
|
95
|
+
3. Add CLI command in `__main__.py` following existing patterns
|
|
96
|
+
4. Update the API Coverage table in `README.md`
|
|
97
|
+
|
|
98
|
+
Best reference: `dns.py:get_nameservers()` for SDK, `__main__.py:dns_nameservers()` for CLI.
|
|
99
|
+
|
|
100
|
+
## PR Checklist
|
|
101
|
+
|
|
102
|
+
- [ ] `uv run ruff check` passes
|
|
103
|
+
- [ ] `uv run ruff format --check` passes
|
|
104
|
+
- [ ] Examples work correctly
|
|
105
|
+
- [ ] Documentation updated if needed
|
|
106
|
+
|
|
107
|
+
## Links
|
|
108
|
+
|
|
109
|
+
- [Namecheap API Documentation](https://www.namecheap.com/support/api/methods/)
|
|
110
|
+
- [Pydantic Documentation](https://docs.pydantic.dev/)
|
|
111
|
+
- [Click Documentation](https://click.palletsprojects.com/)
|
|
112
|
+
- [Textual Documentation](https://textual.textualize.io/)
|
|
113
|
+
- [Ruff Documentation](https://docs.astral.sh/ruff/)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: namecheap-python
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.3.0
|
|
4
4
|
Summary: A friendly Python SDK for Namecheap API
|
|
5
5
|
Project-URL: Homepage, https://github.com/adriangalilea/namecheap-python
|
|
6
6
|
Project-URL: Repository, https://github.com/adriangalilea/namecheap-python
|
|
@@ -42,6 +42,12 @@ Description-Content-Type: text/markdown
|
|
|
42
42
|
|
|
43
43
|
# Namecheap Python SDK
|
|
44
44
|
|
|
45
|
+
[](https://pypi.org/project/namecheap-python/)
|
|
46
|
+
[](https://pepy.tech/project/namecheap-python)
|
|
47
|
+
[](https://pepy.tech/project/namecheap-python)
|
|
48
|
+
[](https://pypi.org/project/namecheap-python/)
|
|
49
|
+
[](https://opensource.org/licenses/MIT)
|
|
50
|
+
|
|
45
51
|
A modern, friendly Python SDK for the Namecheap API with comprehensive CLI and TUI tools.
|
|
46
52
|
|
|
47
53
|
## 🚀 Features
|
|
@@ -206,6 +212,38 @@ Adding CNAME record to tdo.garden...
|
|
|
206
212
|
```
|
|
207
213
|
|
|
208
214
|
|
|
215
|
+
Check account balance:
|
|
216
|
+
|
|
217
|
+
```bash
|
|
218
|
+
❯ namecheap-cli account balance
|
|
219
|
+
Account Balance
|
|
220
|
+
┏━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━┓
|
|
221
|
+
┃ Field ┃ Amount ┃
|
|
222
|
+
┡━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━┩
|
|
223
|
+
│ Available Balance │ 0.00 USD │
|
|
224
|
+
│ Account Balance │ 0.00 USD │
|
|
225
|
+
│ Earned Amount │ 0.00 USD │
|
|
226
|
+
│ Withdrawable │ 0.00 USD │
|
|
227
|
+
│ Auto-Renew Required │ 20.16 USD │
|
|
228
|
+
└─────────────────────┴───────────┘
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
Get detailed domain info:
|
|
232
|
+
|
|
233
|
+
```bash
|
|
234
|
+
❯ namecheap-cli domain info self.fm
|
|
235
|
+
|
|
236
|
+
Domain Information: self.fm
|
|
237
|
+
|
|
238
|
+
Status: Ok
|
|
239
|
+
Owner: adriangalilea
|
|
240
|
+
Created: 07/15/2023
|
|
241
|
+
Expires: 07/15/2026
|
|
242
|
+
Premium: No
|
|
243
|
+
WHOIS Guard: ✓ Enabled
|
|
244
|
+
DNS Provider: CUSTOM
|
|
245
|
+
```
|
|
246
|
+
|
|
209
247
|
You can also export DNS records:
|
|
210
248
|
|
|
211
249
|
```bash
|
|
@@ -320,6 +358,48 @@ nc.dns.set_custom_nameservers("example.com", [
|
|
|
320
358
|
nc.dns.set_default_nameservers("example.com")
|
|
321
359
|
```
|
|
322
360
|
|
|
361
|
+
### Domain Info
|
|
362
|
+
|
|
363
|
+
```python
|
|
364
|
+
info = nc.domains.get_info("example.com")
|
|
365
|
+
print(info.status) # 'Ok'
|
|
366
|
+
print(info.whoisguard_enabled) # True
|
|
367
|
+
print(info.dns_provider) # 'CUSTOM'
|
|
368
|
+
print(info.created) # '07/15/2023'
|
|
369
|
+
print(info.expires) # '07/15/2026'
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
### Account Balance
|
|
373
|
+
|
|
374
|
+
```python
|
|
375
|
+
bal = nc.users.get_balances()
|
|
376
|
+
print(f"{bal.available_balance} {bal.currency}") # '4932.96 USD'
|
|
377
|
+
print(bal.funds_required_for_auto_renew) # Decimal('20.16')
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
### Email Forwarding
|
|
381
|
+
|
|
382
|
+
```python
|
|
383
|
+
# Read
|
|
384
|
+
rules = nc.dns.get_email_forwarding("example.com")
|
|
385
|
+
for r in rules:
|
|
386
|
+
print(f"{r.mailbox} -> {r.forward_to}")
|
|
387
|
+
|
|
388
|
+
# Write (replaces all existing rules)
|
|
389
|
+
nc.dns.set_email_forwarding("example.com", [
|
|
390
|
+
EmailForward(mailbox="info", forward_to="me@gmail.com"),
|
|
391
|
+
EmailForward(mailbox="support", forward_to="help@gmail.com"),
|
|
392
|
+
])
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
### Domain Contacts
|
|
396
|
+
|
|
397
|
+
```python
|
|
398
|
+
contacts = nc.domains.get_contacts("example.com")
|
|
399
|
+
print(f"{contacts.registrant.first_name} {contacts.registrant.last_name}")
|
|
400
|
+
print(contacts.registrant.email)
|
|
401
|
+
```
|
|
402
|
+
|
|
323
403
|
### Domain Management
|
|
324
404
|
|
|
325
405
|
```python
|
|
@@ -393,22 +473,23 @@ nc.dns.builder().a("www", "192.0.2.1", ttl=1799) # Shows as "Automatic"
|
|
|
393
473
|
nc.dns.builder().a("www", "192.0.2.1", ttl=1800) # Shows as "30 min"
|
|
394
474
|
```
|
|
395
475
|
|
|
396
|
-
##
|
|
397
|
-
|
|
398
|
-
The following Namecheap API features are planned for future releases:
|
|
399
|
-
|
|
400
|
-
- **SSL API** - Certificate management
|
|
401
|
-
- **Domain Transfer API** - Transfer domains between registrars
|
|
402
|
-
- **Domain NS API** - Glue record management (child nameservers)
|
|
403
|
-
- **Users API** - Account management and balance checking
|
|
404
|
-
- **Whois API** - WHOIS information lookups
|
|
405
|
-
- **Email Forwarding** - Email forwarding configuration
|
|
476
|
+
## 📊 [API Coverage](https://www.namecheap.com/support/api/methods/)
|
|
406
477
|
|
|
407
|
-
|
|
478
|
+
| API | Status | Methods |
|
|
479
|
+
|-----|--------|---------|
|
|
480
|
+
| `namecheap.domains.*` | ✅ Done | `check`, `list`, `getInfo`, `getContacts`, `register`, `renew`, `setContacts`, `lock`/`unlock` |
|
|
481
|
+
| `namecheap.domains.dns.*` | ✅ Done | `getHosts`, `setHosts` (builder pattern), `add`, `delete`, `export`, `getList`, `setCustom`, `setDefault`, `getEmailForwarding`, `setEmailForwarding` |
|
|
482
|
+
| `namecheap.users.*` | ⚠️ Partial | `getBalances`, `getPricing` (needs debugging). Planned: `changePassword`, `update`, `create`, `login`, `resetPassword` |
|
|
483
|
+
| `namecheap.domains.*` | 🚧 Planned | `getTldList`, `reactivate` |
|
|
484
|
+
| `namecheap.users.address.*` | 🚧 Planned | `create`, `delete`, `getInfo`, `getList`, `setDefault`, `update` |
|
|
485
|
+
| `namecheap.ssl.*` | 🚧 Planned | `create`, `activate`, `renew`, `revoke`, `getList`, `getInfo`, `parseCSR`, `reissue`, and more |
|
|
486
|
+
| `namecheap.domains.transfer.*` | 🚧 Planned | `create`, `getStatus`, `updateStatus`, `getList` |
|
|
487
|
+
| `namecheap.domains.ns.*` | 🚧 Planned | Glue records — `create`, `delete`, `getInfo`, `update` |
|
|
488
|
+
| `namecheap.domainprivacy.*` | 🚧 Planned | `enable`, `disable`, `renew`, `getList`, `changeemailaddress` |
|
|
408
489
|
|
|
409
490
|
## 🛠️ Development
|
|
410
491
|
|
|
411
|
-
See [
|
|
492
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) for setup and development guidelines.
|
|
412
493
|
|
|
413
494
|
## 📝 License
|
|
414
495
|
|
|
@@ -420,4 +501,6 @@ Contributions are welcome! Please feel free to submit a Pull Request. See the [D
|
|
|
420
501
|
|
|
421
502
|
### Contributors
|
|
422
503
|
|
|
504
|
+
- [@huntertur](https://github.com/huntertur) — Rich dependency fix
|
|
505
|
+
- [@jeffmcadams](https://github.com/jeffmcadams) — Domain serialization round-trip
|
|
423
506
|
- [@cosmin](https://github.com/cosmin) — Nameserver management
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# Namecheap Python SDK
|
|
2
2
|
|
|
3
|
+
[](https://pypi.org/project/namecheap-python/)
|
|
4
|
+
[](https://pepy.tech/project/namecheap-python)
|
|
5
|
+
[](https://pepy.tech/project/namecheap-python)
|
|
6
|
+
[](https://pypi.org/project/namecheap-python/)
|
|
7
|
+
[](https://opensource.org/licenses/MIT)
|
|
8
|
+
|
|
3
9
|
A modern, friendly Python SDK for the Namecheap API with comprehensive CLI and TUI tools.
|
|
4
10
|
|
|
5
11
|
## 🚀 Features
|
|
@@ -164,6 +170,38 @@ Adding CNAME record to tdo.garden...
|
|
|
164
170
|
```
|
|
165
171
|
|
|
166
172
|
|
|
173
|
+
Check account balance:
|
|
174
|
+
|
|
175
|
+
```bash
|
|
176
|
+
❯ namecheap-cli account balance
|
|
177
|
+
Account Balance
|
|
178
|
+
┏━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━┓
|
|
179
|
+
┃ Field ┃ Amount ┃
|
|
180
|
+
┡━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━┩
|
|
181
|
+
│ Available Balance │ 0.00 USD │
|
|
182
|
+
│ Account Balance │ 0.00 USD │
|
|
183
|
+
│ Earned Amount │ 0.00 USD │
|
|
184
|
+
│ Withdrawable │ 0.00 USD │
|
|
185
|
+
│ Auto-Renew Required │ 20.16 USD │
|
|
186
|
+
└─────────────────────┴───────────┘
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
Get detailed domain info:
|
|
190
|
+
|
|
191
|
+
```bash
|
|
192
|
+
❯ namecheap-cli domain info self.fm
|
|
193
|
+
|
|
194
|
+
Domain Information: self.fm
|
|
195
|
+
|
|
196
|
+
Status: Ok
|
|
197
|
+
Owner: adriangalilea
|
|
198
|
+
Created: 07/15/2023
|
|
199
|
+
Expires: 07/15/2026
|
|
200
|
+
Premium: No
|
|
201
|
+
WHOIS Guard: ✓ Enabled
|
|
202
|
+
DNS Provider: CUSTOM
|
|
203
|
+
```
|
|
204
|
+
|
|
167
205
|
You can also export DNS records:
|
|
168
206
|
|
|
169
207
|
```bash
|
|
@@ -278,6 +316,48 @@ nc.dns.set_custom_nameservers("example.com", [
|
|
|
278
316
|
nc.dns.set_default_nameservers("example.com")
|
|
279
317
|
```
|
|
280
318
|
|
|
319
|
+
### Domain Info
|
|
320
|
+
|
|
321
|
+
```python
|
|
322
|
+
info = nc.domains.get_info("example.com")
|
|
323
|
+
print(info.status) # 'Ok'
|
|
324
|
+
print(info.whoisguard_enabled) # True
|
|
325
|
+
print(info.dns_provider) # 'CUSTOM'
|
|
326
|
+
print(info.created) # '07/15/2023'
|
|
327
|
+
print(info.expires) # '07/15/2026'
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
### Account Balance
|
|
331
|
+
|
|
332
|
+
```python
|
|
333
|
+
bal = nc.users.get_balances()
|
|
334
|
+
print(f"{bal.available_balance} {bal.currency}") # '4932.96 USD'
|
|
335
|
+
print(bal.funds_required_for_auto_renew) # Decimal('20.16')
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
### Email Forwarding
|
|
339
|
+
|
|
340
|
+
```python
|
|
341
|
+
# Read
|
|
342
|
+
rules = nc.dns.get_email_forwarding("example.com")
|
|
343
|
+
for r in rules:
|
|
344
|
+
print(f"{r.mailbox} -> {r.forward_to}")
|
|
345
|
+
|
|
346
|
+
# Write (replaces all existing rules)
|
|
347
|
+
nc.dns.set_email_forwarding("example.com", [
|
|
348
|
+
EmailForward(mailbox="info", forward_to="me@gmail.com"),
|
|
349
|
+
EmailForward(mailbox="support", forward_to="help@gmail.com"),
|
|
350
|
+
])
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
### Domain Contacts
|
|
354
|
+
|
|
355
|
+
```python
|
|
356
|
+
contacts = nc.domains.get_contacts("example.com")
|
|
357
|
+
print(f"{contacts.registrant.first_name} {contacts.registrant.last_name}")
|
|
358
|
+
print(contacts.registrant.email)
|
|
359
|
+
```
|
|
360
|
+
|
|
281
361
|
### Domain Management
|
|
282
362
|
|
|
283
363
|
```python
|
|
@@ -351,22 +431,23 @@ nc.dns.builder().a("www", "192.0.2.1", ttl=1799) # Shows as "Automatic"
|
|
|
351
431
|
nc.dns.builder().a("www", "192.0.2.1", ttl=1800) # Shows as "30 min"
|
|
352
432
|
```
|
|
353
433
|
|
|
354
|
-
##
|
|
355
|
-
|
|
356
|
-
The following Namecheap API features are planned for future releases:
|
|
357
|
-
|
|
358
|
-
- **SSL API** - Certificate management
|
|
359
|
-
- **Domain Transfer API** - Transfer domains between registrars
|
|
360
|
-
- **Domain NS API** - Glue record management (child nameservers)
|
|
361
|
-
- **Users API** - Account management and balance checking
|
|
362
|
-
- **Whois API** - WHOIS information lookups
|
|
363
|
-
- **Email Forwarding** - Email forwarding configuration
|
|
434
|
+
## 📊 [API Coverage](https://www.namecheap.com/support/api/methods/)
|
|
364
435
|
|
|
365
|
-
|
|
436
|
+
| API | Status | Methods |
|
|
437
|
+
|-----|--------|---------|
|
|
438
|
+
| `namecheap.domains.*` | ✅ Done | `check`, `list`, `getInfo`, `getContacts`, `register`, `renew`, `setContacts`, `lock`/`unlock` |
|
|
439
|
+
| `namecheap.domains.dns.*` | ✅ Done | `getHosts`, `setHosts` (builder pattern), `add`, `delete`, `export`, `getList`, `setCustom`, `setDefault`, `getEmailForwarding`, `setEmailForwarding` |
|
|
440
|
+
| `namecheap.users.*` | ⚠️ Partial | `getBalances`, `getPricing` (needs debugging). Planned: `changePassword`, `update`, `create`, `login`, `resetPassword` |
|
|
441
|
+
| `namecheap.domains.*` | 🚧 Planned | `getTldList`, `reactivate` |
|
|
442
|
+
| `namecheap.users.address.*` | 🚧 Planned | `create`, `delete`, `getInfo`, `getList`, `setDefault`, `update` |
|
|
443
|
+
| `namecheap.ssl.*` | 🚧 Planned | `create`, `activate`, `renew`, `revoke`, `getList`, `getInfo`, `parseCSR`, `reissue`, and more |
|
|
444
|
+
| `namecheap.domains.transfer.*` | 🚧 Planned | `create`, `getStatus`, `updateStatus`, `getList` |
|
|
445
|
+
| `namecheap.domains.ns.*` | 🚧 Planned | Glue records — `create`, `delete`, `getInfo`, `update` |
|
|
446
|
+
| `namecheap.domainprivacy.*` | 🚧 Planned | `enable`, `disable`, `renew`, `getList`, `changeemailaddress` |
|
|
366
447
|
|
|
367
448
|
## 🛠️ Development
|
|
368
449
|
|
|
369
|
-
See [
|
|
450
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) for setup and development guidelines.
|
|
370
451
|
|
|
371
452
|
## 📝 License
|
|
372
453
|
|
|
@@ -378,4 +459,6 @@ Contributions are welcome! Please feel free to submit a Pull Request. See the [D
|
|
|
378
459
|
|
|
379
460
|
### Contributors
|
|
380
461
|
|
|
462
|
+
- [@huntertur](https://github.com/huntertur) — Rich dependency fix
|
|
463
|
+
- [@jeffmcadams](https://github.com/jeffmcadams) — Domain serialization round-trip
|
|
381
464
|
- [@cosmin](https://github.com/cosmin) — Nameserver management
|
|
@@ -12,15 +12,29 @@ from __future__ import annotations
|
|
|
12
12
|
|
|
13
13
|
from .client import Namecheap
|
|
14
14
|
from .errors import ConfigurationError, NamecheapError, ValidationError
|
|
15
|
-
from .models import
|
|
15
|
+
from .models import (
|
|
16
|
+
AccountBalance,
|
|
17
|
+
Contact,
|
|
18
|
+
DNSRecord,
|
|
19
|
+
Domain,
|
|
20
|
+
DomainCheck,
|
|
21
|
+
DomainContacts,
|
|
22
|
+
DomainInfo,
|
|
23
|
+
EmailForward,
|
|
24
|
+
Nameservers,
|
|
25
|
+
)
|
|
16
26
|
|
|
17
|
-
__version__ = "1.
|
|
27
|
+
__version__ = "1.3.0"
|
|
18
28
|
__all__ = [
|
|
29
|
+
"AccountBalance",
|
|
19
30
|
"ConfigurationError",
|
|
20
31
|
"Contact",
|
|
21
32
|
"DNSRecord",
|
|
22
33
|
"Domain",
|
|
23
34
|
"DomainCheck",
|
|
35
|
+
"DomainContacts",
|
|
36
|
+
"DomainInfo",
|
|
37
|
+
"EmailForward",
|
|
24
38
|
"Namecheap",
|
|
25
39
|
"NamecheapError",
|
|
26
40
|
"Nameservers",
|
|
@@ -6,7 +6,7 @@ from typing import TYPE_CHECKING, Any, Literal
|
|
|
6
6
|
|
|
7
7
|
import tldextract
|
|
8
8
|
|
|
9
|
-
from namecheap.models import DNSRecord, Nameservers
|
|
9
|
+
from namecheap.models import DNSRecord, EmailForward, Nameservers
|
|
10
10
|
|
|
11
11
|
from .base import BaseAPI
|
|
12
12
|
|
|
@@ -494,3 +494,75 @@ class DnsAPI(BaseAPI):
|
|
|
494
494
|
nameservers = [ns_data] if isinstance(ns_data, str) else ns_data
|
|
495
495
|
|
|
496
496
|
return Nameservers(is_default=is_default, nameservers=nameservers)
|
|
497
|
+
|
|
498
|
+
def get_email_forwarding(self, domain: str) -> list[EmailForward]:
|
|
499
|
+
"""
|
|
500
|
+
Get email forwarding rules for a domain.
|
|
501
|
+
|
|
502
|
+
Args:
|
|
503
|
+
domain: Domain name
|
|
504
|
+
|
|
505
|
+
Returns:
|
|
506
|
+
List of EmailForward rules
|
|
507
|
+
|
|
508
|
+
Examples:
|
|
509
|
+
>>> rules = nc.dns.get_email_forwarding("example.com")
|
|
510
|
+
>>> for r in rules:
|
|
511
|
+
... print(f"{r.mailbox} -> {r.forward_to}")
|
|
512
|
+
"""
|
|
513
|
+
result: Any = self._request(
|
|
514
|
+
"namecheap.domains.dns.getEmailForwarding",
|
|
515
|
+
{"DomainName": domain},
|
|
516
|
+
path="DomainDNSGetEmailForwardingResult",
|
|
517
|
+
)
|
|
518
|
+
|
|
519
|
+
if not result:
|
|
520
|
+
return []
|
|
521
|
+
|
|
522
|
+
forwards = result.get("Forward", [])
|
|
523
|
+
if isinstance(forwards, dict):
|
|
524
|
+
forwards = [forwards]
|
|
525
|
+
assert isinstance(forwards, list), f"Unexpected Forward type: {type(forwards)}"
|
|
526
|
+
|
|
527
|
+
return [
|
|
528
|
+
EmailForward(mailbox=f.get("@mailbox", ""), forward_to=f.get("#text", ""))
|
|
529
|
+
for f in forwards
|
|
530
|
+
]
|
|
531
|
+
|
|
532
|
+
def set_email_forwarding(
|
|
533
|
+
self, domain: str, rules: list[EmailForward] | list[dict[str, str]]
|
|
534
|
+
) -> bool:
|
|
535
|
+
"""
|
|
536
|
+
Set email forwarding rules for a domain. Replaces all existing rules.
|
|
537
|
+
|
|
538
|
+
Args:
|
|
539
|
+
domain: Domain name
|
|
540
|
+
rules: List of EmailForward or dicts with 'mailbox' and 'forward_to' keys
|
|
541
|
+
|
|
542
|
+
Returns:
|
|
543
|
+
True if successful
|
|
544
|
+
|
|
545
|
+
Examples:
|
|
546
|
+
>>> nc.dns.set_email_forwarding("example.com", [
|
|
547
|
+
... EmailForward(mailbox="info", forward_to="me@gmail.com"),
|
|
548
|
+
... EmailForward(mailbox="support", forward_to="help@gmail.com"),
|
|
549
|
+
... ])
|
|
550
|
+
"""
|
|
551
|
+
assert rules, "At least one forwarding rule is required"
|
|
552
|
+
|
|
553
|
+
params: dict[str, Any] = {"DomainName": domain}
|
|
554
|
+
for i, rule in enumerate(rules, 1):
|
|
555
|
+
if isinstance(rule, EmailForward):
|
|
556
|
+
params[f"MailBox{i}"] = rule.mailbox
|
|
557
|
+
params[f"ForwardTo{i}"] = rule.forward_to
|
|
558
|
+
else:
|
|
559
|
+
params[f"MailBox{i}"] = rule["mailbox"]
|
|
560
|
+
params[f"ForwardTo{i}"] = rule["forward_to"]
|
|
561
|
+
|
|
562
|
+
result: Any = self._request(
|
|
563
|
+
"namecheap.domains.dns.setEmailForwarding",
|
|
564
|
+
params,
|
|
565
|
+
path="DomainDNSSetEmailForwardingResult",
|
|
566
|
+
)
|
|
567
|
+
|
|
568
|
+
return bool(result and result.get("@IsSuccess", "false").lower() == "true")
|
|
@@ -9,7 +9,7 @@ from typing import Any
|
|
|
9
9
|
import tldextract
|
|
10
10
|
|
|
11
11
|
from namecheap.logging import logger
|
|
12
|
-
from namecheap.models import Contact, Domain, DomainCheck
|
|
12
|
+
from namecheap.models import Contact, Domain, DomainCheck, DomainContacts, DomainInfo
|
|
13
13
|
|
|
14
14
|
from .base import BaseAPI
|
|
15
15
|
|
|
@@ -110,6 +110,98 @@ class DomainsAPI(BaseAPI):
|
|
|
110
110
|
return [results]
|
|
111
111
|
return results if isinstance(results, list) else []
|
|
112
112
|
|
|
113
|
+
def get_info(self, domain: str) -> DomainInfo:
|
|
114
|
+
"""
|
|
115
|
+
Get detailed information about a domain.
|
|
116
|
+
|
|
117
|
+
Args:
|
|
118
|
+
domain: Domain name
|
|
119
|
+
|
|
120
|
+
Returns:
|
|
121
|
+
DomainInfo with status, whoisguard, DNS provider, etc.
|
|
122
|
+
|
|
123
|
+
Examples:
|
|
124
|
+
>>> info = nc.domains.get_info("example.com")
|
|
125
|
+
>>> print(f"{info.domain} status={info.status} whoisguard={info.whoisguard_enabled}")
|
|
126
|
+
"""
|
|
127
|
+
result: Any = self._request(
|
|
128
|
+
"namecheap.domains.getInfo",
|
|
129
|
+
{"DomainName": domain},
|
|
130
|
+
path="DomainGetInfoResult",
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
assert result, f"API returned empty result for {domain} getInfo"
|
|
134
|
+
|
|
135
|
+
# Extract nested fields into flat structure
|
|
136
|
+
domain_details = result.get("DomainDetails", {})
|
|
137
|
+
whoisguard = result.get("Whoisguard", {})
|
|
138
|
+
dns_details = result.get("DnsDetails", {})
|
|
139
|
+
|
|
140
|
+
flat = {
|
|
141
|
+
"@ID": result.get("@ID"),
|
|
142
|
+
"@DomainName": result.get("@DomainName"),
|
|
143
|
+
"@OwnerName": result.get("@OwnerName"),
|
|
144
|
+
"@IsOwner": result.get("@IsOwner"),
|
|
145
|
+
"@IsPremium": result.get("@IsPremium", "false"),
|
|
146
|
+
"@Status": result.get("@Status"),
|
|
147
|
+
"created": domain_details.get("CreatedDate"),
|
|
148
|
+
"expires": domain_details.get("ExpiredDate"),
|
|
149
|
+
"whoisguard_enabled": whoisguard.get("@Enabled", "false").lower() == "true"
|
|
150
|
+
if isinstance(whoisguard, dict)
|
|
151
|
+
else False,
|
|
152
|
+
"dns_provider": dns_details.get("@ProviderType")
|
|
153
|
+
if isinstance(dns_details, dict)
|
|
154
|
+
else None,
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return DomainInfo.model_validate(flat)
|
|
158
|
+
|
|
159
|
+
def get_contacts(self, domain: str) -> DomainContacts:
|
|
160
|
+
"""
|
|
161
|
+
Get contact information for a domain.
|
|
162
|
+
|
|
163
|
+
Args:
|
|
164
|
+
domain: Domain name
|
|
165
|
+
|
|
166
|
+
Returns:
|
|
167
|
+
DomainContacts with registrant, tech, admin, and aux_billing contacts
|
|
168
|
+
|
|
169
|
+
Examples:
|
|
170
|
+
>>> contacts = nc.domains.get_contacts("example.com")
|
|
171
|
+
>>> print(contacts.registrant.email)
|
|
172
|
+
"""
|
|
173
|
+
result: Any = self._request(
|
|
174
|
+
"namecheap.domains.getContacts",
|
|
175
|
+
{"DomainName": domain},
|
|
176
|
+
path="DomainContactsResult",
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
assert result, f"API returned empty result for {domain} getContacts"
|
|
180
|
+
|
|
181
|
+
def parse_contact(data: dict[str, Any]) -> Contact:
|
|
182
|
+
return Contact.model_validate(
|
|
183
|
+
{
|
|
184
|
+
"FirstName": data.get("FirstName", ""),
|
|
185
|
+
"LastName": data.get("LastName", ""),
|
|
186
|
+
"Organization": data.get("Organization"),
|
|
187
|
+
"Address1": data.get("Address1", ""),
|
|
188
|
+
"Address2": data.get("Address2"),
|
|
189
|
+
"City": data.get("City", ""),
|
|
190
|
+
"StateProvince": data.get("StateProvince", ""),
|
|
191
|
+
"PostalCode": data.get("PostalCode", ""),
|
|
192
|
+
"Country": data.get("Country", ""),
|
|
193
|
+
"Phone": data.get("Phone", ""),
|
|
194
|
+
"EmailAddress": data.get("EmailAddress", ""),
|
|
195
|
+
}
|
|
196
|
+
)
|
|
197
|
+
|
|
198
|
+
return DomainContacts(
|
|
199
|
+
registrant=parse_contact(result.get("Registrant", {})),
|
|
200
|
+
tech=parse_contact(result.get("Tech", {})),
|
|
201
|
+
admin=parse_contact(result.get("Admin", {})),
|
|
202
|
+
aux_billing=parse_contact(result.get("AuxBilling", {})),
|
|
203
|
+
)
|
|
204
|
+
|
|
113
205
|
def register(
|
|
114
206
|
self,
|
|
115
207
|
domain: str,
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"""Users API."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
from namecheap.models import AccountBalance
|
|
8
|
+
|
|
9
|
+
from .base import BaseAPI
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class UsersAPI(BaseAPI):
|
|
13
|
+
"""User account operations."""
|
|
14
|
+
|
|
15
|
+
def get_balances(self) -> AccountBalance:
|
|
16
|
+
"""
|
|
17
|
+
Get account balance information.
|
|
18
|
+
|
|
19
|
+
Returns:
|
|
20
|
+
AccountBalance with available balance, earned amount, etc.
|
|
21
|
+
|
|
22
|
+
Examples:
|
|
23
|
+
>>> bal = nc.users.get_balances()
|
|
24
|
+
>>> print(f"{bal.available_balance} {bal.currency}")
|
|
25
|
+
"""
|
|
26
|
+
result: Any = self._request(
|
|
27
|
+
"namecheap.users.getBalances",
|
|
28
|
+
path="UserGetBalancesResult",
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
assert result, "API returned empty result for getBalances"
|
|
32
|
+
return AccountBalance.model_validate(result)
|