Metadata-Version: 2.4
Name: solscanpy-api
Version: 1.0.0
Summary: A lightweight, typed Python client for the Solscan Pro API v2.0
Author: rkohl
License-Expression: MIT
Keywords: solscan,solana,blockchain,explorer,api,web3,solana-explorer
Requires-Python: >=3.12
Description-Content-Type: text/markdown
License-File: LICENSE-MIT
Requires-Dist: requests
Requires-Dist: pydantic
Requires-Dist: pytest
Dynamic: license-file

# solscan-api

A lightweight, typed Python client for the [Solscan Pro API v2.0](https://pro-api.solscan.io/pro-api-docs/v2.0/reference).

Every endpoint returns a validated [pydantic](https://docs.pydantic.dev/) model on success, or an `ErrorModel` on failure — so you get autocompletion and type safety throughout.

## Features

- Full coverage of the Solscan Pro API v2.0: `account`, `block`, `token`, `transaction`, `nft`, `market`, and `program` endpoints
- Typed pydantic response models with snake_case API fields mapped to camelCase attributes
- Consistent error handling: network and HTTP failures return an `ErrorModel` instead of raising
- Optional query parameters expressed as small, discoverable helper classes

## Requirements

- Python >= 3.12
- A Solscan Pro API key ([get one here](https://pro-api.solscan.io/))

## Installation

```shell
pip install solscan-api
```

## Quickstart

```python
from solscan import Solscan

client = Solscan(apiKey="YOUR_API_KEY")

# Fetch token metadata
meta = client.token.meta(address="So11111111111111111111111111111111111111112")
print(meta.symbol, meta.price)

# Every method returns either a model or an ErrorModel
from solscan.error import ErrorModel

result = client.block.last()
if isinstance(result, ErrorModel):
    print(f"Request failed [{result.code}]: {result.message}")
else:
    for block in result:
        print(block.blockHeight, block.transactionsCount)
```

## Usage

The client is organized into one property per API namespace. Each returns a sub-client exposing that namespace's methods.

### Account

```python
client.account.balanceChange(address)
client.account.detail(address)
client.account.defiActivities(address)
client.account.fundedBy(addresses)
client.account.leaderboards(AccountOptionals.leaderboards())
client.account.portfolio(address)
client.account.transactions(address)

# Nested sub-clients (pass the client instance)
client.account.meta(client).metadata(address)
client.account.meta(client).multi(address)
client.account.staking(client).stake(address)
client.account.staking(client).rewards(address)
```

### Block

```python
client.block.detail(block)
client.block.last(limit)
client.block.transactions(block)
```

### Token

```python
client.token.transfer(address)
client.token.defiActivities(address)
client.token.markets(token)
client.token.meta(address)
client.token.metaMulti(address)
client.token.price(address)
client.token.priceMulti(address)
client.token.holders(address)
client.token.list()
client.token.top()
client.token.trending()
```

### Transaction

```python
client.transaction.last()
client.transaction.detail(tx)
client.transaction.actions(tx)
client.transaction.fees()
```

### NFT

```python
client.nft.news()
client.nft.activities()
client.nft.collectionLists()
client.nft.collectionItems(collection)
```

### Market

```python
client.market.list()
client.market.info(address)
client.market.volume(address)
client.market.positions(address)
```

### Program

```python
client.program.list()
client.program.popularPlatforms()
client.program.analytics(address, range)
```

## Optional parameters

Endpoints that accept query parameters take an optional helper object. Each module exposes its parameters under its own `Optionals` class. For example:

```python
from solscan.types import PageSize, TxFilter
from solscan.token.optionals import Optionals as TokenOptionals
from solscan.transaction.optionals import Optionals as TxOptionals

# Token transfers, paginated
client.token.transfer(
    address="So11111111111111111111111111111111111111112",
    optional=TokenOptionals.transfer(page=1, pageSize=PageSize.size20),
)

# Latest transactions, filtered
client.transaction.last(
    optional=TxOptionals.last(filter=TxFilter.exceptVote),
)
```

Shared enums (page sizes, sort order, limits, time ranges, etc.) live in `solscan.types`.

## Error handling

Requests never raise on transport or HTTP errors. Instead, every method returns an `ErrorModel` with a `code` and `message`:

```python
from solscan.error import ErrorModel

result = client.token.price(address="invalid")
if isinstance(result, ErrorModel):
    print(result.code, result.message)
```

## Running Tests

```bash
python -m pytest tests/ -v
```

The test suite mocks all network calls, so it runs fully offline and requires no API key.

## License

Released under the [MIT License](LICENSE-MIT).
