Metadata-Version: 2.4
Name: ipatlas
Version: 0.1.0
Summary: Official IP-Atlas API client — IP geolocation, ASN, VPN detection
Author-email: Trellis Digital Services LLC <support@ip-atlas.io>
License: MIT
Project-URL: Homepage, https://ip-atlas.io
Project-URL: Documentation, https://ip-atlas.io/docs
Project-URL: Repository, https://github.com/Jakeharlan73/ip-atlas
Project-URL: Issue Tracker, https://github.com/Jakeharlan73/ip-atlas/issues
Keywords: ip,geolocation,asn,vpn,ip-atlas,ipinfo,maxmind
Requires-Python: >=3.8
Description-Content-Type: text/markdown
Requires-Dist: requests>=2.28
Provides-Extra: dev
Requires-Dist: pytest; extra == "dev"
Requires-Dist: responses; extra == "dev"

# ipatlas

Official Python client for the [IP-Atlas](https://ip-atlas.io) API — IP geolocation, ASN lookup, VPN/proxy/Tor detection, batch lookup, and account management.

## Install

```bash
pip install ipatlas
```

Requires Python 3.8+. Depends on [`requests`](https://requests.readthedocs.io/).

## Quickstart

### Without an API key (free anonymous tier)

```python
from ipatlas import IPAtlas

client = IPAtlas()

# Look up your own IP
me = client.lookup_self()
print(me["country"], me["org"])

# Look up a specific IP (anonymous is limited to 60 req/min)
result = client.lookup("8.8.8.8")
print(result["asn"], result["is_datacenter"])
```

### With an API key

```python
from ipatlas import IPAtlas

client = IPAtlas(api_key="ipa_live_...")

result = client.lookup("1.1.1.1")
print(result)
```

### Batch lookup (up to 100 IPs)

```python
from ipatlas import IPAtlas

client = IPAtlas(api_key="ipa_live_...")

batch = client.lookup_batch(["8.8.8.8", "1.1.1.1", "not-an-ip"])
for item in batch["results"]:
    if "error" in item:
        print(f"{item['ip']}: {item['error']}")
    else:
        print(item["ip"], item.get("country"), item.get("org"))
```

Responses come back in the same order as the input list. Each element is either a successful lookup dict or a per-IP error dict `{"ip": ..., "error": "..."}`. Request-level failures (auth, quota) raise `IPAtlasError`.

### Account management

```python
info = client.account()
print(f"Plan: {info['plan']}, used {info['monthly_used']}/{info['monthly_limit']} this month")

# Rotate the key (old one is revoked, new one returned once)
new = client.rotate_key()
print(new["api_key"])
```

### Error handling

```python
from ipatlas import IPAtlas, IPAtlasError

client = IPAtlas(api_key="ipa_live_...")

try:
    result = client.lookup("not-an-ip")
except IPAtlasError as e:
    print(f"API error {e.status_code}: {e.body}")
```

## API

### `IPAtlas(api_key=None, base_url='https://api.ip-atlas.io')`

| Parameter   | Type  | Default                      | Description                         |
|-------------|-------|------------------------------|-------------------------------------|
| `api_key`   | `str` | `None`                       | API key sent as `X-API-Key` header  |
| `base_url`  | `str` | `https://api.ip-atlas.io`    | Override the API base URL           |

### `client.lookup(ip) -> dict`

Returns geolocation + ASN + threat data for the given IPv4 or IPv6 address.

### `client.lookup_self() -> dict`

Returns data for the caller's own IP address.

### `client.lookup_batch(ips) -> dict`

Looks up up to 100 IPs in a single request. Returns `{"results": [...]}` where each element is in the same order as the input list.

### `client.account() -> dict`

Returns the plan, key prefix, daily/monthly usage, and Stripe linkage for the bound API key.

### `client.rotate_key() -> dict`

Revokes the current key and issues a replacement with the same plan. The new plaintext key is only returned once.

## Links

- [Documentation](https://ip-atlas.io/docs)
- [Get an API key](https://ip-atlas.io/#pricing)
- [GitHub](https://github.com/Jakeharlan73/ip-atlas)
