partnr-api 0.2.0
Official Python SDK for Partnr API v2 - Brazilian financial data
Requires Python: >=3.9
Keywords: api, b3, brazil, etf, fii, financial, market, partnr, sdk, stocks

# partnr-api

Official Python SDK for Partnr API v2 - Brazilian financial data.

## Installation

```bash
pip install partnr-api
# or
poetry add partnr-api
# or
uv add partnr-api
```

## Quickstart

```python
from partnr import PartnrClient

# Auto-detects PARTNR_API_KEY from environment
client = PartnrClient()

# Or provide explicitly
client = PartnrClient(api_key="your-api-key")

# Get company details
company = client.companies.get("PETR3")
print(company.trading_name)  # PETROBRAS

# Get historical quotes
quotes = client.quotes.get_historical_quotes("PETR4")
print(quotes[0].close_price)
```

## Configuration

```python
client = PartnrClient(
    api_key="your-api-key",      # Required
    base_url="https://data.partnr.ai/v2/",  # Optional, default
    timeout=30.0,                 # Optional, default 30s
    max_retries=2,               # Optional, default 2
    headers={                    # Optional extra headers
        "X-Custom-Header": "value",
    },
)

# Use as context manager
with PartnrClient(api_key="your-api-key") as client:
    company = client.companies.get("PETR3")
```

## Examples

### List Companies

```python
# List all companies
companies = client.companies.list()

# With optional fields
companies = client.companies.list(
    show_sector=True,
    show_market_cap=True,
)

# Access sector and market cap
for company in companies:
    print(company.symbol, company.sector.name if company.sector else None)
    print(company.market_cap.value if company.market_cap else None)
```

### Get Company by Identifier

```python
# You can use symbol, company_id (CNPJ), or ticker
company = client.companies.get("PETR3")
company2 = client.companies.get("PETR")
company3 = client.companies.get("33000167000101")

# With optional fields
company_full = client.companies.get(
    "PETR3",
    show_market_cap=True,
    show_logo=True,
    show_company_details=True,
)

# Market cap is an object with value and format
print(company_full.market_cap.value)   # 497465651767
print(company_full.market_cap.format)  # "BRL 497,465,651,767"
```

### Historical Quotes

```python
# Get historical quote data
quotes = client.quotes.get_historical_quotes(
    "PETR4",
    start_date="2024-01-01",
    end_date="2024-12-31",
    adjusted=True,  # Adjust for dividends and corporate actions
)

for quote in quotes:
    print(f"{quote.date}: {quote.close_price} (vol: {quote.volume})")
```

### List and Get News

```python
# List recent news
news = client.news.list(limit=10)

# Filter by entity (ticker, sector, etc.)
petr_news = client.news.list(query="PETR4", limit=5)

# Get full article with summary and entities
article = client.news.get(news[0].id)
print(article.summary)
print(article.key_points)
```

### Paginate Through News

```python
# Using iterator
for article in client.news.iterate(limit=20):
    print(article.title)
    # Automatically fetches next page when needed
```

### Run Screener with Filters

```python
results = client.screener.run(
    order_by="droplet:PRICE_TO_EARNINGS",
    order="asc",
    limit=10,
    primary_only=True,
    filters=[
        {"type": "close_price", "op": ">=", "value": 5},
        {"type": "sector", "in": ["BANKING", "TECHNOLOGY"]},
        {"type": "droplet:PRICE_TO_EARNINGS", "op": "<", "value": 15},
    ],
)

for item in results.data:
    print(f"{item.ticker}: R$ {item.current_price}")
```

### Financial Reports and Ratios

```python
# Get standardized reports
reports = client.companies.get_reports(
    "PETR3",
    section="INCOME_STATEMENT",
    frequency="QUARTERLY",
    aggregation="CONSOLIDATED",
)

# Get financial ratios
ratios = client.companies.get_ratios(
    "PETR3",
    ids="EBITDA_MARGIN,ROE,NET_MARGIN",
    frequency="TTM",
)

# Get valuation ratios
valuation = client.companies.get_valuation_ratios(
    "PETR3",
    ids="PRICE_TO_EARNINGS,PRICE_TO_BOOK",
)
```

### Traded Funds (FIIs, ETFs)

```python
# List all traded funds
funds = client.traded_funds.list()

# Iterate through all funds
for fund in client.traded_funds.iterate():
    print(f"{fund.symbol}: {fund.name}")

# Get fund details
hglg = client.traded_funds.get("HGLG11")
print(hglg.dividend_yield_12m)
```

### Macroeconomic Indicators

```python
# List available indicators
indicators = client.macroeconomics.list_indicators()

# Get indicator series
selic = client.macroeconomics.get_indicator_series(
    "INTEREST_RATE",
    country="BRA",
    unit="ANNUAL",
    limit=12,
)

for point in selic:
    print(f"{point.date}: {point.value}%")
```

### Daily Variations

```python
variations = client.stocks.get_variations()

print("Top gainers:")
for stock in variations.highs[:5]:
    print(f"{stock.ticker}: +{stock.variation * 100:.2f}%")

print("Top losers:")
for stock in variations.lows[:5]:
    print(f"{stock.ticker}: {stock.variation * 100:.2f}%")
```

## Error Handling

All errors include the `request_id` for debugging with Partnr support.

```python
from partnr import (
    PartnrClient,
    ApiError,
    AuthenticationError,
    NotFoundError,
    RateLimitError,
    ValidationError,
    ServerError,
)

try:
    company = client.companies.get("INVALID")
except AuthenticationError:
    print("Invalid API key")
except NotFoundError:
    print("Company not found")
except RateLimitError:
    print("Rate limit exceeded, try again later")
except ValidationError:
    print("Invalid request parameters")
except ServerError:
    print("Server error, try again later")
except ApiError as e:
    print(f"API Error: {e.message}")
    print(f"Request-Id: {e.request_id}")
    print(f"Status: {e.status}")
    print(f"Code: {e.code}")
```

## Retries

The SDK automatically retries on:
- 429 (Rate Limit) - respects `Retry-After` header (supports both seconds and HTTP-date format)
- 5xx (Server Errors)
- Network timeouts and failures

No retries on 4xx errors (except 429).

```python
client = PartnrClient(
    api_key="your-api-key",
    max_retries=3,  # Retry up to 3 times
    timeout=60.0,   # 60s timeout
)
```

## Async Support

Full async support for high-performance applications:

```python
from partnr import AsyncPartnrClient

async def main():
    async with AsyncPartnrClient(api_key="your-api-key") as client:
        # All methods are async
        company = await client.companies.get("PETR3")
        
        # Async iteration
        async for article in client.news.iterate(limit=10):
            print(article.title)

# Run with asyncio
import asyncio
asyncio.run(main())
```

## Request/Response Hooks

Add logging, metrics, or custom behavior:

```python
# Sync client
def log_request(url, options):
    print(f"[{options['method']}] {url}")

def log_response(url, response, duration_ms):
    print(f"[{response.status_code}] {url} - {duration_ms:.0f}ms")

client.set_request_hook(log_request)
client.set_response_hook(log_response)

# Async client (use async functions)
async def async_log_request(url, options):
    print(f"[{options['method']}] {url}")

async_client.set_request_hook(async_log_request)
```

## Type Hints

Full type hints with Pydantic models:

```python
from partnr.models import (
    Company,
    CompanyListItem,
    Quote,
    NewsArticle,
    ScreenerResponse,
    TradedFund,
    MacroIndicator,
)
```

## Requirements

- Python >= 3.9
- httpx >= 0.25.0
- pydantic >= 2.0.0

## License

MIT