Metadata-Version: 2.4
Name: iploop-sdk
Version: 1.8.3
Summary: Residential proxy SDK with full scraping capabilities — render JS, bypass protection, extract data
Home-page: https://iploop.io
Author: IPLoop
Author-email: IPLoop <partners@iploop.io>
License: MIT
Project-URL: Homepage, https://iploop.io
Project-URL: Documentation, https://docs.iploop.io
Project-URL: Repository, https://github.com/iploop/iploop-python
Keywords: proxy,residential,scraping,web,rotating
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Internet :: WWW/HTTP
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.7
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: requests>=2.28
Provides-Extra: async
Requires-Dist: aiohttp>=3.8; extra == "async"
Provides-Extra: socks
Requires-Dist: requests[socks]>=2.28; extra == "socks"
Provides-Extra: render
Requires-Dist: playwright>=1.40.0; extra == "render"
Dynamic: author
Dynamic: home-page
Dynamic: license-file
Dynamic: requires-python

# IPLoop Python SDK

HTTP fetching through IPLoop proxies. New-platform accounts use a **proxy username/password pair** for traffic and a separate **API key** for account access. Existing key-only integrations retain legacy mode.

## Install

```bash
python -m pip install 'iploop-sdk[async,socks]>=1.8.3'
```

Sign up or sign in at **https://platform.iploop.io/**. Keep API keys and passwords in environment variables, not source code or shared logs.

## New platform (V2)

Set `IPLOOP_API_KEY`, `IPLOOP_PROXY_USERNAME` and `IPLOOP_PROXY_PASSWORD` to your own account values. These credentials are separate; the dashboard password is not the proxy password.

```python
import os
from iploop import IPLoop

client = IPLoop(
    api_key=os.environ['IPLOOP_API_KEY'],
    proxy_username=os.environ['IPLOOP_PROXY_USERNAME'],
    proxy_password=os.environ['IPLOOP_PROXY_PASSWORD'],
)
response = client.get('https://example.com', country='US', timeout=15, retries=2)
response.raise_for_status()
print(response.text)
```

The SDK discovers the current proxy settings from the HTTPS portal metadata endpoint. Passing both proxy credentials explicitly opts into V2; omitting both retains legacy mode. Never replace a customer's working credentials merely to migrate configuration.

### Sticky sessions

```python
session = client.session(session_id='my_session1', country='US', lifetime=5)
r1 = session.get('https://api.ipify.org?format=json', retries=1)
r2 = session.get('https://api.ipify.org?format=json', retries=1)
```

`lifetime` requests a positive integer duration in **minutes**, encoded as `sesstime` for V2. Session IDs accept letters, digits and underscores. Reusing an ID asks for the same session; device availability can force an exit change. Rotating requests can reuse an exit and do not guarantee a never-before-used IP. V2 requests without an explicit session do not acquire an SDK-generated sticky session.

### SOCKS5

```python
socks_client = IPLoop(
    api_key=os.environ['IPLOOP_API_KEY'],
    proxy_username=os.environ['IPLOOP_PROXY_USERNAME'],
    proxy_password=os.environ['IPLOOP_PROXY_PASSWORD'],
    proxy_protocol='socks5h',
)
response = socks_client.get('https://example.com', country='GB', retries=1)
```

Install the `socks` extra. `socks5h` resolves target names through the proxy. Async SOCKS is explicitly unsupported in this release; use the synchronous client.

### Async HTTP

```python
import asyncio
from iploop import AsyncIPLoop

async def main():
    async with AsyncIPLoop(
        api_key=os.environ['IPLOOP_API_KEY'],
        proxy_username=os.environ['IPLOOP_PROXY_USERNAME'],
        proxy_password=os.environ['IPLOOP_PROXY_PASSWORD'],
    ) as client:
        response = await client.get('https://example.com', country='US', retries=1)
        print(response.status_code)

asyncio.run(main())
```

Client construction discovers metadata synchronously once; the fetch transport is asynchronous. Account helper methods are synchronous, including when exposed by `AsyncIPLoop`.

### Account helpers

```python
usage = client.usage()
print(usage['account']['balance_gb'])
print(usage['account']['balance_bytes'])
print(usage['daily'])  # last 30 days: day + bytes
status = client.status()  # authenticated account + protocol metadata
```

`balance_bytes` is bytes; `balance_gb` is decimal GB. Daily bytes and account balance are independent readbacks, not one derived from the other. The V2 helper schema differs from legacy support API output.

### Feature boundaries

- Country codes are validated against ISO alpha-2 codes. An accepted code does not guarantee current supply in that location.
- V2 state/city selectors are supported from 1.8.3; rendering still raises an explicit unsupported-feature error.
- V2 `countries()` and `ask()` are explicitly unsupported rather than falling back to unrelated legacy inventory or support systems.
- The existing extractor modules, cookie handling, headers, TLS integration and batch API remain available. Their existence does not guarantee that every website or anti-bot challenge will succeed. Check returned status/content.
- `stats` is SDK request telemetry, not proof of target-site business success or an accounting ledger. Do not treat it as an SLA.

## Legacy key-only accounts

```python
from iploop import IPLoop
legacy = IPLoop(os.environ['LEGACY_IPLOOP_API_KEY'])
response = legacy.get('https://example.com', country='US')
```

Existing constructor arguments and legacy auth generation are retained. Legacy endpoints and support helpers are not the new platform's account API. Do not use the key-only example with V2 credentials.

## Errors and retries

Check response status and content before accepting a result. Retry budgets are bounded with the `retries` and `timeout` arguments. Retries and rotation cannot guarantee bypass or a fresh exit. Invalid local V2 inputs raise `ValueError`; known unsupported features raise `NotImplementedError`; account authentication failures raise `AuthError`. Connection errors are sanitized in V2 mode so credentials are not repeated in error text.

## Links

- Dashboard/signup: https://platform.iploop.io/
- Public integration docs: https://proxyclaw.ai/docs.html
- Source: https://github.com/Iploop/iploop-python

## Deployed V2 state and city selectors (1.8.3+)
```python
# client is configured with separate V2 proxy credentials as shown above
r = client.get('https://example.com', country='US', state='tx', city='austin')
r.raise_for_status()
# State is optional when country + city are sufficient
r = client.get('https://example.com', country='US', city='austin')
# Sticky request group preserves the requested geo selectors
session = client.session(session_id='geo_demo', country='US', state='tx', city='austin')
r = session.get('https://example.com')
```
`state` is also a keyword-only constructor default; async `get`/`fetch` accepts it. State/city require a valid country. Use slug selectors (letters, digits or underscores); embedded option separators are rejected. New state selectors are V2-only; existing legacy country/city behavior remains unchanged.

State/city selectors are deployed in V2. Country is required; state is optional for a city request. Exact-city accuracy is not certified: some Austin test exits were labeled San Antonio or Schertz by independent geo services. Inspect the observed location when precision matters. Invalid-country fallback on the raw backend remains unresolved; this SDK validates country codes locally.
