Metadata-Version: 2.5
Name: huntapi
Version: 0.1.0
Summary: Official Python SDK for the HuntAPI media download API.
Project-URL: Homepage, https://www.huntapi.com
Project-URL: Documentation, https://docs.huntapi.com
Project-URL: Repository, https://github.com/huntapi/huntapi-python
Project-URL: Issues, https://github.com/huntapi/huntapi-python/issues
Author-email: HuntAPI <contact@huntapi.com>
License-Expression: MIT
License-File: LICENSE
Keywords: api,async-api,audio-download-api,huntapi,media-metadata,sdk,video-download-api
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Internet :: WWW/HTTP
Classifier: Typing :: Typed
Requires-Python: >=3.11
Requires-Dist: httpx<1,>=0.27
Provides-Extra: dev
Requires-Dist: build<2,>=1.2; extra == 'dev'
Requires-Dist: mypy>=1.13; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.24; extra == 'dev'
Requires-Dist: pytest>=8.3; extra == 'dev'
Requires-Dist: ruff<1,>=0.8; extra == 'dev'
Description-Content-Type: text/markdown

# HuntAPI Python SDK

The official Python SDK for the [HuntAPI](https://www.huntapi.com) media download API.

Download video and audio from public media URLs, or extract their metadata, with one async REST API.

The SDK exposes **6 typed methods**, one per documented endpoint, with blocking and `asyncio` clients built on [HTTPX](https://www.python-httpx.org/), plus a generic `request()`.

## Requirements

- Python 3.11 or newer

## Installation

```sh
pip install huntapi
```

## Get your API key

[Sign up for HuntAPI](https://app.huntapi.com/register) or sign in, then copy your API key from the [dashboard](https://app.huntapi.com). The client reads the `HUNTAPI_API_KEY` environment variable when no key is passed.

## Quick start

```python
import os

from huntapi import HuntApiClient

with HuntApiClient(os.environ["HUNTAPI_API_KEY"]) as client:
    response = client.media_metadata(query="https://www.youtube.com/watch?v=Z5YP8kznWZs")
    print(response.data)
```

Keyword arguments mirror the [API reference](https://docs.huntapi.com). Python keywords get a trailing underscore (`from_=`), and the wire name is used on the request.

## Async jobs

Download endpoints are asynchronous: they return a `job_id`, and jobs can run for up to 12 hours. `wait_for_job()` polls `GET /v1/jobs/{id}` until the job reaches a terminal status (`CompletedJob`, `Error`, `NotFound`, `BadRequest`):

```python
submitted = client.video_download(query="https://www.youtube.com/watch?v=Z5YP8kznWZs", video_quality="720p")
job = client.wait_for_job(
    submitted.data["job_id"],
    on_progress=lambda j: print(j["status"], j.get("progress")),
)
print(job.data)
```

## Async I/O

```python
import asyncio

from huntapi import AsyncHuntApiClient


async def main() -> None:
    async with AsyncHuntApiClient() as client:
        response = await client.media_metadata(query="https://www.youtube.com/watch?v=Z5YP8kznWZs")
        print(response.data)


asyncio.run(main())
```

## GET or POST

Methods use GET when the endpoint supports it (parameters in the query string). POST sends the same parameters as a JSON body:

```python
from huntapi import RequestOptions

client.audio_download(query="test", request_options=RequestOptions(method="POST"))
```

`None` values are omitted, `False` and `0` are sent, and sequences repeat the parameter name.

## Any endpoint

```python
response = client.request("GET", "/v1/usage")
```

## Errors

```python
from huntapi import HuntApiAPIError

try:
    client.media_metadata(query="https://www.youtube.com/watch?v=Z5YP8kznWZs")
except HuntApiAPIError as error:
    print(error.status, error, error.response_body)
```

| Error | When |
| --- | --- |
| `HuntApiConfigurationError` | Missing API key, invalid options, unsupported method, closed client. |
| `HuntApiSerializationError` | A required parameter is missing or a value can't be sent. |
| `HuntApiTransportError` | Network failure or timeout. |
| `HuntApiAPIError` | Non-2xx status (`status`, `error_message`, `response_body`, `raw_response`). |
| `HuntApiResponseDecodingError` | A success response could not be decoded. |

All of them extend `HuntApiError`.

## Retries

Requests are retried up to 3 times after a network error, a `408`, `500`, `502`, `503` or `504`, or a rate limit — which the API returns as `401` (or `429`) with a `Rate limit exceeded` message. Per-key quota errors (`…: quota daily`) are not retried, nor are `400`, `402`, `404` or client-side timeouts. The delay starts at 1 second and grows 1.5× per retry (capped at 30 seconds); a `Retry-After` header wins.

```python
client = HuntApiClient(max_retries=5, retry_delay=0.5)

# or for one request
client.media_metadata(query="https://www.youtube.com/watch?v=Z5YP8kznWZs", request_options=RequestOptions(max_retries=0))
```

## Batch requests

`batch()` runs a function per item on 10 threads (`concurrency`) and returns one `BatchResult` per item, in input order. A failing item never stops the others. `AsyncHuntApiClient.batch()` does the same with `asyncio`.

```python
inputs = ["https://www.youtube.com/watch?v=Z5YP8kznWZs", "https://www.youtube.com/watch?v=jNQXAC9IVRw"]
results = client.batch(inputs, lambda query: client.media_metadata(query=query), concurrency=5)

for result in results:
    if result.ok:
        print(result.value.data)
    else:
        print(inputs[result.index], result.error)
```

| Constant | Default | Description |
| --- | --- | --- |
| `DEFAULT_CONCURRENCY` | `10` | Tasks run at the same time by batch |
| `DEFAULT_TIMEOUT` | `120.0` | Request timeout (seconds) |
| `MAX_RETRIES` | `3` | Retry attempts after a network error, a 408/5xx or a rate limit |
| `RETRY_BACKOFF_FACTOR` | `1.5` | Exponential backoff multiplier |
| `DEFAULT_RETRY_DELAY` | `1.0` | Delay before the first retry (seconds) |

## Timeouts and custom HTTPX clients

```python
import httpx

from huntapi import HuntApiClient, RequestOptions

client = HuntApiClient(timeout=60)  # seconds, default 120, None disables
client.media_metadata(query="https://www.youtube.com/watch?v=Z5YP8kznWZs", request_options=RequestOptions(timeout=30))

# Bring your own HTTPX client (proxies, limits, event hooks…). You keep ownership of it.
http_client = httpx.Client(proxy="http://localhost:8080")
client = HuntApiClient(http_client=http_client)
```

## Endpoints

6 methods, generated from the [OpenAPI document](https://app.huntapi.com/openapi).

| Method | HTTP | Description |
| --- | --- | --- |
| `audio_download()` | GET / POST `/v1/audio/download` | Download audio from a URL via HuntAPI and receive a job id. |
| `jobs()` | GET `/v1/jobs/{id}` | Poll status, progress (0–100), and result of an async HuntAPI job. |
| `media_metadata()` | GET `/v1/media/metadata` | Extract metadata from a media URL without downloading the file. |
| `stats()` | GET `/v1/stats` | Infrastructure health for an engine or endpoint over the last hour. Not scoped to your account or tenant. Enabled per account: HTTP 403 mea… |
| `usage()` | GET `/v1/usage` | Retrieve real-time usage statistics and credit balance for your Huntapi account. |
| `video_download()` | GET / POST `/v1/video/download` | Video Download |

## License

MIT — see [LICENSE](LICENSE).
