readyapis 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.
- readyapis-0.1.0/.gitignore +15 -0
- readyapis-0.1.0/PKG-INFO +205 -0
- readyapis-0.1.0/PUBLISHING.md +141 -0
- readyapis-0.1.0/README.md +176 -0
- readyapis-0.1.0/pyproject.toml +47 -0
- readyapis-0.1.0/src/readyapis/__init__.py +35 -0
- readyapis-0.1.0/src/readyapis/_client.py +256 -0
- readyapis-0.1.0/src/readyapis/_errors.py +86 -0
- readyapis-0.1.0/src/readyapis/_types.py +175 -0
- readyapis-0.1.0/src/readyapis/_version.py +7 -0
- readyapis-0.1.0/src/readyapis/calendar.py +85 -0
- readyapis-0.1.0/src/readyapis/email.py +39 -0
- readyapis-0.1.0/src/readyapis/fx.py +82 -0
- readyapis-0.1.0/src/readyapis/geo.py +108 -0
- readyapis-0.1.0/src/readyapis/intel.py +146 -0
- readyapis-0.1.0/src/readyapis/meta.py +52 -0
- readyapis-0.1.0/src/readyapis/py.typed +0 -0
- readyapis-0.1.0/src/readyapis/tax.py +123 -0
readyapis-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: readyapis
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Official Python SDK for Ready APIs — curated REST APIs and composite signals.
|
|
5
|
+
Project-URL: Homepage, https://readyapis.com
|
|
6
|
+
Project-URL: Documentation, https://readyapis.com/docs
|
|
7
|
+
Project-URL: Repository, https://github.com/ReadyAPIs-com/readyapis
|
|
8
|
+
Project-URL: Issues, https://github.com/ReadyAPIs-com/readyapis/issues
|
|
9
|
+
Author-email: Ready APIs <support@readyapis.com>
|
|
10
|
+
License-Expression: MIT
|
|
11
|
+
Keywords: api,calendar,email,fx,geo,intel,readyapis,sdk,tax
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
23
|
+
Classifier: Topic :: Internet :: WWW/HTTP
|
|
24
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
25
|
+
Classifier: Typing :: Typed
|
|
26
|
+
Requires-Python: >=3.9
|
|
27
|
+
Requires-Dist: httpx<1.0,>=0.24
|
|
28
|
+
Description-Content-Type: text/markdown
|
|
29
|
+
|
|
30
|
+
# readyapis
|
|
31
|
+
|
|
32
|
+
> Official Python SDK for [Ready APIs](https://readyapis.com) — curated REST APIs and composite signals.
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
pip install readyapis
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Six hero APIs (geo, tax, fx, email, calendar, intel) plus identity / catalog metadata, in one tiny typed client. No Pydantic, no codegen — just `httpx` and the standard library.
|
|
39
|
+
|
|
40
|
+
## Quick start
|
|
41
|
+
|
|
42
|
+
```python
|
|
43
|
+
from readyapis import Client
|
|
44
|
+
|
|
45
|
+
# Demo mode — no key needed. Hits /demo/api/v1/* with allowlisted params.
|
|
46
|
+
client = Client()
|
|
47
|
+
|
|
48
|
+
zip_data = client.geo.zip("30301")
|
|
49
|
+
print(zip_data.city, zip_data.state) # "Atlanta" "GA"
|
|
50
|
+
|
|
51
|
+
tax = client.tax.calculate(income=125000, state="NY", year=2026, filing_status="single")
|
|
52
|
+
print(tax.federal["tax"], tax.taxable_income) # 18733.42 108900.0
|
|
53
|
+
|
|
54
|
+
fx = client.fx.convert(amount=100, from_="USD", to="EUR")
|
|
55
|
+
print(fx.converted) # 85.4555
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Authentication
|
|
59
|
+
|
|
60
|
+
Set an API key as an environment variable (recommended) or pass it explicitly:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
export READYAPIS_API_KEY=ra_live_...
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
```python
|
|
67
|
+
from readyapis import Client
|
|
68
|
+
|
|
69
|
+
client = Client() # reads READYAPIS_API_KEY
|
|
70
|
+
client = Client(api_key="ra_live_...") # explicit override
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Get a key at <https://readyapis.com/register>. Free tier: 1,000 credits/month.
|
|
74
|
+
|
|
75
|
+
## Demo mode
|
|
76
|
+
|
|
77
|
+
If no API key is configured, the client enters **demo mode** automatically. It hits `/demo/api/v1/*` routes, which require no auth but only accept a small set of allowlisted parameters (ZIP `30301`, email `hello@stripe.com`, etc.) — perfect for tinkering and CI.
|
|
78
|
+
|
|
79
|
+
```python
|
|
80
|
+
client = Client()
|
|
81
|
+
assert client.is_demo
|
|
82
|
+
|
|
83
|
+
# Demo allowlist: 10001, 30301, 30303, 60601, 78701, 94105, 98101.
|
|
84
|
+
client.geo.zip("30301") # OK
|
|
85
|
+
client.geo.zip("12345") # raises ApiError (demo_parameter_not_allowed)
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Namespaces
|
|
89
|
+
|
|
90
|
+
```python
|
|
91
|
+
client.geo.zip("30301")
|
|
92
|
+
client.geo.city(name="Atlanta", state="GA")
|
|
93
|
+
client.geo.ip("8.8.8.8")
|
|
94
|
+
client.geo.enrich(zip="30301")
|
|
95
|
+
client.geo.nearby(lat=33.7488, lon=-84.3877, radius_miles=10)
|
|
96
|
+
|
|
97
|
+
client.tax.calculate(income=125000, state="NY", year=2026, filing_status="single")
|
|
98
|
+
client.tax.brackets(year=2026) # federal
|
|
99
|
+
client.tax.brackets(year=2026, state="NY") # state
|
|
100
|
+
client.tax.deductions(year=2026, filing_status="single")
|
|
101
|
+
|
|
102
|
+
client.fx.rates(base="USD")
|
|
103
|
+
client.fx.rates(base="USD", symbols=["EUR", "GBP", "JPY"])
|
|
104
|
+
client.fx.convert(amount=100, from_="USD", to="EUR")
|
|
105
|
+
|
|
106
|
+
client.email.validate("hello@stripe.com")
|
|
107
|
+
|
|
108
|
+
client.calendar.holidays(country="US", year=2026)
|
|
109
|
+
client.calendar.business_days(start="2026-01-01", end="2026-01-31")
|
|
110
|
+
|
|
111
|
+
client.intel.site_risk(zip="30301", site_profile="insurance_underwriting")
|
|
112
|
+
|
|
113
|
+
client.meta.whoami() # requires API key
|
|
114
|
+
client.meta.catalog() # free
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
The full list of demo-allowed `site_profile` values: `data_center_site`, `insurance_underwriting`, `remote_office`, `retail_storefront`, `small_office`, `warehouse_distribution`. Production mode accepts more.
|
|
118
|
+
|
|
119
|
+
## Response shape
|
|
120
|
+
|
|
121
|
+
Every response is wrapped in a `Response` object that proxies the JSON:API-flavored envelope. You can use ergonomic attribute access, dict-style lookup, or get the raw payload.
|
|
122
|
+
|
|
123
|
+
```python
|
|
124
|
+
r = client.geo.zip("30301")
|
|
125
|
+
|
|
126
|
+
r.city # "Atlanta" — flattened from data.attributes
|
|
127
|
+
r.state # "GA"
|
|
128
|
+
r["county"] # "Fulton" — dict-style works too
|
|
129
|
+
r.data # the full data dict
|
|
130
|
+
r.attributes # just the attributes dict
|
|
131
|
+
r.meta # {credits_used, source, ...}
|
|
132
|
+
r.raw # the full underlying JSON
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
For list responses, iterate or index:
|
|
136
|
+
|
|
137
|
+
```python
|
|
138
|
+
holidays = client.calendar.holidays(country="US", year=2026)
|
|
139
|
+
assert holidays.is_list
|
|
140
|
+
for h in holidays:
|
|
141
|
+
print(h.date, h.name)
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
## Errors
|
|
145
|
+
|
|
146
|
+
All errors inherit from `ApiError`. Specific subclasses let you branch on common cases:
|
|
147
|
+
|
|
148
|
+
```python
|
|
149
|
+
from readyapis import Client, ApiError, AuthError, RateLimitError, NotFoundError
|
|
150
|
+
|
|
151
|
+
try:
|
|
152
|
+
client.geo.zip("99999")
|
|
153
|
+
except RateLimitError as e:
|
|
154
|
+
print(f"slow down — retry in {e.retry_after}s")
|
|
155
|
+
except AuthError as e:
|
|
156
|
+
print(f"bad key: {e.code}")
|
|
157
|
+
except NotFoundError as e:
|
|
158
|
+
print(f"not found: {e.message}")
|
|
159
|
+
except ApiError as e:
|
|
160
|
+
print(f"{e.status_code} {e.code}: {e.message}")
|
|
161
|
+
print(e.body) # full error envelope
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
Every `ApiError` exposes `.status_code`, `.code`, `.message`, `.body`, and (for 429s) `.retry_after`.
|
|
165
|
+
|
|
166
|
+
## Retry behavior
|
|
167
|
+
|
|
168
|
+
The client automatically retries `429`, `500`, `502`, `503`, and `504` responses with exponential backoff (0.5s, 1s, 2s — three attempts total) plus small jitter. On `429`, it honors `Retry-After` if present. Disable per-client:
|
|
169
|
+
|
|
170
|
+
```python
|
|
171
|
+
client = Client(retry=False)
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
## Idempotency
|
|
175
|
+
|
|
176
|
+
`POST` endpoints accept an `idempotency_key` keyword. The server treats two requests with the same key (within the idempotency window) as the same logical operation — safe to retry from the client.
|
|
177
|
+
|
|
178
|
+
```python
|
|
179
|
+
client.intel.rank(
|
|
180
|
+
candidates=[...],
|
|
181
|
+
objective="enterprise_buyer",
|
|
182
|
+
idempotency_key="run-2026-05-14-abc",
|
|
183
|
+
)
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
## Configuration
|
|
187
|
+
|
|
188
|
+
```python
|
|
189
|
+
Client(
|
|
190
|
+
api_key=None, # str | None — falls back to READYAPIS_API_KEY env
|
|
191
|
+
base_url="https://readyapis.com", # override for staging/local
|
|
192
|
+
timeout=30.0, # per-request seconds
|
|
193
|
+
retry=True, # retry 429 + 5xx with backoff
|
|
194
|
+
user_agent=None, # override the UA string
|
|
195
|
+
http_client=None, # bring your own httpx.Client
|
|
196
|
+
)
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
## Compatibility
|
|
200
|
+
|
|
201
|
+
Python 3.9 and later. Only runtime dependency is `httpx >= 0.24, < 1.0`.
|
|
202
|
+
|
|
203
|
+
## License
|
|
204
|
+
|
|
205
|
+
MIT
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
# Publishing `readyapis` to PyPI
|
|
2
|
+
|
|
3
|
+
This package is built and tested. The homepage already advertises
|
|
4
|
+
`pip install readyapis`, so **until you publish, that command will return
|
|
5
|
+
"No matching distribution found" from PyPI.** Publish to make it real.
|
|
6
|
+
|
|
7
|
+
## One-time setup (do once, then never again)
|
|
8
|
+
|
|
9
|
+
1. **Have a PyPI account.** Sign up at <https://pypi.org/account/register/> if
|
|
10
|
+
you don't already, using `jmoncrief@gmail.com` or whichever email you
|
|
11
|
+
want associated with the published package.
|
|
12
|
+
|
|
13
|
+
2. **Enable 2FA.** PyPI requires it before you can create API tokens. Use
|
|
14
|
+
<https://pypi.org/manage/account/two-factor/> — TOTP (e.g. 1Password,
|
|
15
|
+
Authy, Google Authenticator) works fine.
|
|
16
|
+
|
|
17
|
+
3. **Create a project-scoped API token.** Go to
|
|
18
|
+
<https://pypi.org/manage/account/token/>, name it `readyapis-publish`,
|
|
19
|
+
scope it to the `readyapis` project (you may need to upload a first
|
|
20
|
+
release using an "Entire account" token, then narrow the scope after).
|
|
21
|
+
Copy the token — it starts with `pypi-AgEIcHl…`. You only see it once.
|
|
22
|
+
|
|
23
|
+
4. **Store the token** in `~/.pypirc` (mode `0600`):
|
|
24
|
+
|
|
25
|
+
```ini
|
|
26
|
+
[pypi]
|
|
27
|
+
username = __token__
|
|
28
|
+
password = pypi-AgEIcHl…YOUR_TOKEN_HERE
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Or set it on the fly via `TWINE_USERNAME=__token__ TWINE_PASSWORD=pypi-… twine upload …`.
|
|
32
|
+
|
|
33
|
+
No org / scope ceremony needed — the package is unscoped (`readyapis`),
|
|
34
|
+
owned by your personal PyPI account directly. (We tried checking the
|
|
35
|
+
`readyapis` name at <https://pypi.org/pypi/readyapis/json> first — it
|
|
36
|
+
returned `Not Found`, so the slot is available.)
|
|
37
|
+
|
|
38
|
+
## Build
|
|
39
|
+
|
|
40
|
+
From the repo root:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
cd sdk/python
|
|
44
|
+
python -m pip install --upgrade build twine
|
|
45
|
+
python -m build
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
That produces two artifacts in `dist/`:
|
|
49
|
+
|
|
50
|
+
- `readyapis-0.1.0-py3-none-any.whl` ← the wheel
|
|
51
|
+
- `readyapis-0.1.0.tar.gz` ← the sdist
|
|
52
|
+
|
|
53
|
+
Sanity-check them before uploading:
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
python -m twine check dist/*
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Both files should print `PASSED`.
|
|
60
|
+
|
|
61
|
+
## Publish
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
cd sdk/python
|
|
65
|
+
python -m twine upload dist/*
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
That's it. Within ~30 seconds:
|
|
69
|
+
|
|
70
|
+
- The package shows up at <https://pypi.org/project/readyapis/>
|
|
71
|
+
- `pip install readyapis` works from any machine on the planet
|
|
72
|
+
- The homepage `pip install readyapis` hint (if added) starts functioning
|
|
73
|
+
|
|
74
|
+
## Verify
|
|
75
|
+
|
|
76
|
+
After publish, in a scratch dir:
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
mkdir /tmp/rapi-py-test && cd /tmp/rapi-py-test
|
|
80
|
+
python -m venv .venv && source .venv/bin/activate
|
|
81
|
+
pip install readyapis
|
|
82
|
+
python -c "
|
|
83
|
+
from readyapis import Client
|
|
84
|
+
r = Client().geo.zip('30301')
|
|
85
|
+
print(r.city, r.state) # 'Atlanta' 'GA'
|
|
86
|
+
"
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Subsequent releases
|
|
90
|
+
|
|
91
|
+
Bump `version` in two places (kept manually in sync):
|
|
92
|
+
|
|
93
|
+
1. `sdk/python/pyproject.toml` — the `version = "0.1.0"` line.
|
|
94
|
+
2. `sdk/python/src/readyapis/_version.py` — the `__version__ = "0.1.0"` line.
|
|
95
|
+
|
|
96
|
+
Then:
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
cd sdk/python
|
|
100
|
+
rm -rf dist/
|
|
101
|
+
python -m build
|
|
102
|
+
python -m twine upload dist/*
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Recommended workflow:
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
cd sdk/python
|
|
109
|
+
# ... bump version in both files ...
|
|
110
|
+
git commit -am "readyapis 0.1.1"
|
|
111
|
+
git tag readyapis-py-0.1.1
|
|
112
|
+
git push --follow-tags
|
|
113
|
+
|
|
114
|
+
rm -rf dist/
|
|
115
|
+
python -m build
|
|
116
|
+
python -m twine upload dist/*
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## If you change your mind
|
|
120
|
+
|
|
121
|
+
PyPI **does not allow re-uploading the same version**, even if you delete it.
|
|
122
|
+
If you publish a broken release, bump the patch version (`0.1.0` → `0.1.1`)
|
|
123
|
+
and publish that. You can `pip install readyapis==0.1.1` but `0.1.0` will
|
|
124
|
+
remain in the index (with a "yanked" tag if you yank it).
|
|
125
|
+
|
|
126
|
+
For early days this is fine — just don't publish anything secret.
|
|
127
|
+
|
|
128
|
+
## What `pip` users actually download
|
|
129
|
+
|
|
130
|
+
The published wheel contains just the `src/readyapis/` tree plus `README.md`
|
|
131
|
+
(per the hatchling include rules in `pyproject.toml`):
|
|
132
|
+
|
|
133
|
+
- `readyapis/__init__.py`
|
|
134
|
+
- `readyapis/_client.py`
|
|
135
|
+
- `readyapis/_errors.py`
|
|
136
|
+
- `readyapis/_types.py`
|
|
137
|
+
- `readyapis/_version.py`
|
|
138
|
+
- `readyapis/geo.py`, `tax.py`, `fx.py`, `email.py`, `calendar.py`, `intel.py`, `meta.py`
|
|
139
|
+
- `readyapis/py.typed` ← marks the package as typed for mypy / pyright
|
|
140
|
+
|
|
141
|
+
Single runtime dependency: `httpx >= 0.24, < 1.0`. Total wheel size: ~10 KB.
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
# readyapis
|
|
2
|
+
|
|
3
|
+
> Official Python SDK for [Ready APIs](https://readyapis.com) — curated REST APIs and composite signals.
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
pip install readyapis
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
Six hero APIs (geo, tax, fx, email, calendar, intel) plus identity / catalog metadata, in one tiny typed client. No Pydantic, no codegen — just `httpx` and the standard library.
|
|
10
|
+
|
|
11
|
+
## Quick start
|
|
12
|
+
|
|
13
|
+
```python
|
|
14
|
+
from readyapis import Client
|
|
15
|
+
|
|
16
|
+
# Demo mode — no key needed. Hits /demo/api/v1/* with allowlisted params.
|
|
17
|
+
client = Client()
|
|
18
|
+
|
|
19
|
+
zip_data = client.geo.zip("30301")
|
|
20
|
+
print(zip_data.city, zip_data.state) # "Atlanta" "GA"
|
|
21
|
+
|
|
22
|
+
tax = client.tax.calculate(income=125000, state="NY", year=2026, filing_status="single")
|
|
23
|
+
print(tax.federal["tax"], tax.taxable_income) # 18733.42 108900.0
|
|
24
|
+
|
|
25
|
+
fx = client.fx.convert(amount=100, from_="USD", to="EUR")
|
|
26
|
+
print(fx.converted) # 85.4555
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Authentication
|
|
30
|
+
|
|
31
|
+
Set an API key as an environment variable (recommended) or pass it explicitly:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
export READYAPIS_API_KEY=ra_live_...
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
```python
|
|
38
|
+
from readyapis import Client
|
|
39
|
+
|
|
40
|
+
client = Client() # reads READYAPIS_API_KEY
|
|
41
|
+
client = Client(api_key="ra_live_...") # explicit override
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Get a key at <https://readyapis.com/register>. Free tier: 1,000 credits/month.
|
|
45
|
+
|
|
46
|
+
## Demo mode
|
|
47
|
+
|
|
48
|
+
If no API key is configured, the client enters **demo mode** automatically. It hits `/demo/api/v1/*` routes, which require no auth but only accept a small set of allowlisted parameters (ZIP `30301`, email `hello@stripe.com`, etc.) — perfect for tinkering and CI.
|
|
49
|
+
|
|
50
|
+
```python
|
|
51
|
+
client = Client()
|
|
52
|
+
assert client.is_demo
|
|
53
|
+
|
|
54
|
+
# Demo allowlist: 10001, 30301, 30303, 60601, 78701, 94105, 98101.
|
|
55
|
+
client.geo.zip("30301") # OK
|
|
56
|
+
client.geo.zip("12345") # raises ApiError (demo_parameter_not_allowed)
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Namespaces
|
|
60
|
+
|
|
61
|
+
```python
|
|
62
|
+
client.geo.zip("30301")
|
|
63
|
+
client.geo.city(name="Atlanta", state="GA")
|
|
64
|
+
client.geo.ip("8.8.8.8")
|
|
65
|
+
client.geo.enrich(zip="30301")
|
|
66
|
+
client.geo.nearby(lat=33.7488, lon=-84.3877, radius_miles=10)
|
|
67
|
+
|
|
68
|
+
client.tax.calculate(income=125000, state="NY", year=2026, filing_status="single")
|
|
69
|
+
client.tax.brackets(year=2026) # federal
|
|
70
|
+
client.tax.brackets(year=2026, state="NY") # state
|
|
71
|
+
client.tax.deductions(year=2026, filing_status="single")
|
|
72
|
+
|
|
73
|
+
client.fx.rates(base="USD")
|
|
74
|
+
client.fx.rates(base="USD", symbols=["EUR", "GBP", "JPY"])
|
|
75
|
+
client.fx.convert(amount=100, from_="USD", to="EUR")
|
|
76
|
+
|
|
77
|
+
client.email.validate("hello@stripe.com")
|
|
78
|
+
|
|
79
|
+
client.calendar.holidays(country="US", year=2026)
|
|
80
|
+
client.calendar.business_days(start="2026-01-01", end="2026-01-31")
|
|
81
|
+
|
|
82
|
+
client.intel.site_risk(zip="30301", site_profile="insurance_underwriting")
|
|
83
|
+
|
|
84
|
+
client.meta.whoami() # requires API key
|
|
85
|
+
client.meta.catalog() # free
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
The full list of demo-allowed `site_profile` values: `data_center_site`, `insurance_underwriting`, `remote_office`, `retail_storefront`, `small_office`, `warehouse_distribution`. Production mode accepts more.
|
|
89
|
+
|
|
90
|
+
## Response shape
|
|
91
|
+
|
|
92
|
+
Every response is wrapped in a `Response` object that proxies the JSON:API-flavored envelope. You can use ergonomic attribute access, dict-style lookup, or get the raw payload.
|
|
93
|
+
|
|
94
|
+
```python
|
|
95
|
+
r = client.geo.zip("30301")
|
|
96
|
+
|
|
97
|
+
r.city # "Atlanta" — flattened from data.attributes
|
|
98
|
+
r.state # "GA"
|
|
99
|
+
r["county"] # "Fulton" — dict-style works too
|
|
100
|
+
r.data # the full data dict
|
|
101
|
+
r.attributes # just the attributes dict
|
|
102
|
+
r.meta # {credits_used, source, ...}
|
|
103
|
+
r.raw # the full underlying JSON
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
For list responses, iterate or index:
|
|
107
|
+
|
|
108
|
+
```python
|
|
109
|
+
holidays = client.calendar.holidays(country="US", year=2026)
|
|
110
|
+
assert holidays.is_list
|
|
111
|
+
for h in holidays:
|
|
112
|
+
print(h.date, h.name)
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## Errors
|
|
116
|
+
|
|
117
|
+
All errors inherit from `ApiError`. Specific subclasses let you branch on common cases:
|
|
118
|
+
|
|
119
|
+
```python
|
|
120
|
+
from readyapis import Client, ApiError, AuthError, RateLimitError, NotFoundError
|
|
121
|
+
|
|
122
|
+
try:
|
|
123
|
+
client.geo.zip("99999")
|
|
124
|
+
except RateLimitError as e:
|
|
125
|
+
print(f"slow down — retry in {e.retry_after}s")
|
|
126
|
+
except AuthError as e:
|
|
127
|
+
print(f"bad key: {e.code}")
|
|
128
|
+
except NotFoundError as e:
|
|
129
|
+
print(f"not found: {e.message}")
|
|
130
|
+
except ApiError as e:
|
|
131
|
+
print(f"{e.status_code} {e.code}: {e.message}")
|
|
132
|
+
print(e.body) # full error envelope
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
Every `ApiError` exposes `.status_code`, `.code`, `.message`, `.body`, and (for 429s) `.retry_after`.
|
|
136
|
+
|
|
137
|
+
## Retry behavior
|
|
138
|
+
|
|
139
|
+
The client automatically retries `429`, `500`, `502`, `503`, and `504` responses with exponential backoff (0.5s, 1s, 2s — three attempts total) plus small jitter. On `429`, it honors `Retry-After` if present. Disable per-client:
|
|
140
|
+
|
|
141
|
+
```python
|
|
142
|
+
client = Client(retry=False)
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
## Idempotency
|
|
146
|
+
|
|
147
|
+
`POST` endpoints accept an `idempotency_key` keyword. The server treats two requests with the same key (within the idempotency window) as the same logical operation — safe to retry from the client.
|
|
148
|
+
|
|
149
|
+
```python
|
|
150
|
+
client.intel.rank(
|
|
151
|
+
candidates=[...],
|
|
152
|
+
objective="enterprise_buyer",
|
|
153
|
+
idempotency_key="run-2026-05-14-abc",
|
|
154
|
+
)
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
## Configuration
|
|
158
|
+
|
|
159
|
+
```python
|
|
160
|
+
Client(
|
|
161
|
+
api_key=None, # str | None — falls back to READYAPIS_API_KEY env
|
|
162
|
+
base_url="https://readyapis.com", # override for staging/local
|
|
163
|
+
timeout=30.0, # per-request seconds
|
|
164
|
+
retry=True, # retry 429 + 5xx with backoff
|
|
165
|
+
user_agent=None, # override the UA string
|
|
166
|
+
http_client=None, # bring your own httpx.Client
|
|
167
|
+
)
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
## Compatibility
|
|
171
|
+
|
|
172
|
+
Python 3.9 and later. Only runtime dependency is `httpx >= 0.24, < 1.0`.
|
|
173
|
+
|
|
174
|
+
## License
|
|
175
|
+
|
|
176
|
+
MIT
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "readyapis"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Official Python SDK for Ready APIs — curated REST APIs and composite signals."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.9"
|
|
11
|
+
license = "MIT"
|
|
12
|
+
authors = [{ name = "Ready APIs", email = "support@readyapis.com" }]
|
|
13
|
+
keywords = ["readyapis", "api", "sdk", "geo", "tax", "fx", "email", "calendar", "intel"]
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Development Status :: 4 - Beta",
|
|
16
|
+
"Intended Audience :: Developers",
|
|
17
|
+
"License :: OSI Approved :: MIT License",
|
|
18
|
+
"Operating System :: OS Independent",
|
|
19
|
+
"Programming Language :: Python :: 3",
|
|
20
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
21
|
+
"Programming Language :: Python :: 3.9",
|
|
22
|
+
"Programming Language :: Python :: 3.10",
|
|
23
|
+
"Programming Language :: Python :: 3.11",
|
|
24
|
+
"Programming Language :: Python :: 3.12",
|
|
25
|
+
"Programming Language :: Python :: 3.13",
|
|
26
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
27
|
+
"Topic :: Internet :: WWW/HTTP",
|
|
28
|
+
"Typing :: Typed",
|
|
29
|
+
]
|
|
30
|
+
dependencies = ["httpx>=0.24,<1.0"]
|
|
31
|
+
|
|
32
|
+
[project.urls]
|
|
33
|
+
Homepage = "https://readyapis.com"
|
|
34
|
+
Documentation = "https://readyapis.com/docs"
|
|
35
|
+
Repository = "https://github.com/ReadyAPIs-com/readyapis"
|
|
36
|
+
Issues = "https://github.com/ReadyAPIs-com/readyapis/issues"
|
|
37
|
+
|
|
38
|
+
[tool.hatch.build.targets.wheel]
|
|
39
|
+
packages = ["src/readyapis"]
|
|
40
|
+
|
|
41
|
+
[tool.hatch.build.targets.sdist]
|
|
42
|
+
include = [
|
|
43
|
+
"src/readyapis",
|
|
44
|
+
"README.md",
|
|
45
|
+
"PUBLISHING.md",
|
|
46
|
+
"pyproject.toml",
|
|
47
|
+
]
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"""Official Python SDK for Ready APIs.
|
|
2
|
+
|
|
3
|
+
Quick start:
|
|
4
|
+
|
|
5
|
+
from readyapis import Client
|
|
6
|
+
|
|
7
|
+
client = Client() # demo mode, no key needed
|
|
8
|
+
zip_data = client.geo.zip("30301")
|
|
9
|
+
print(zip_data.city, zip_data.state) # "Atlanta", "GA"
|
|
10
|
+
|
|
11
|
+
Set ``READYAPIS_API_KEY`` in your environment, or pass ``api_key="ra_live_..."``
|
|
12
|
+
explicitly, to hit the production API.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from ._client import Client
|
|
16
|
+
from ._errors import (
|
|
17
|
+
ApiError,
|
|
18
|
+
AuthError,
|
|
19
|
+
NotFoundError,
|
|
20
|
+
RateLimitError,
|
|
21
|
+
ServerError,
|
|
22
|
+
)
|
|
23
|
+
from ._types import Response
|
|
24
|
+
from ._version import __version__
|
|
25
|
+
|
|
26
|
+
__all__ = [
|
|
27
|
+
"Client",
|
|
28
|
+
"Response",
|
|
29
|
+
"ApiError",
|
|
30
|
+
"AuthError",
|
|
31
|
+
"NotFoundError",
|
|
32
|
+
"RateLimitError",
|
|
33
|
+
"ServerError",
|
|
34
|
+
"__version__",
|
|
35
|
+
]
|