apiq


Nameapiq JSON
Version 0.0.2 PyPI version JSON
download
home_pagehttps://github.com/nessshon/apiq
SummaryElegant async API client framework with intuitive decorators for quick integration
upload_time2025-07-08 18:50:47
maintainerNone
docs_urlNone
authornessshon
requires_python>=3.10
licenseMIT
keywords api client async http aiohttp pydantic
VCS
bugtrack_url
requirements aiohttp aiolimiter pydantic
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # 📦 APIQ

**APIQ** is an elegant, fully asynchronous Python toolkit for building robust API clients with minimal code and maximal
type safety.
Define endpoints with simple decorators, leverage strict Pydantic models, and enjoy integrated rate limiting and
retries—**no inheritance required**.

[![PyPI](https://img.shields.io/pypi/v/apiq.svg?color=FFE873\&labelColor=3776AB)](https://pypi.python.org/pypi/apiq)
![Python Versions](https://img.shields.io/badge/Python-3.10%20--%203.12-black?color=FFE873\&labelColor=3776AB)
[![License](https://img.shields.io/github/license/nessshon/apiq)](LICENSE)

![Downloads](https://pepy.tech/badge/apiq)
![Downloads](https://pepy.tech/badge/apiq/month)
![Downloads](https://pepy.tech/badge/apiq/week)

---

## Installation

```bash
pip install apiq
```

---

## Quickstart

### 1. Define your models

Use [Pydantic](https://docs.pydantic.dev/latest/) for type-safe request and response models:

```python
from typing import List
from pydantic import BaseModel


class BulkAccountsRequest(BaseModel):
    account_ids: List[str]


class AccountInfoResponse(BaseModel):
    address: str
    balance: int
    status: str


class BulkAccountsResponse(BaseModel):
    accounts: List[AccountInfoResponse]
```

---

### 2. Define your client class

Configure all core settings via the `@apiclient` decorator:

```python
from apiq import apiclient, endpoint


@apiclient(
    base_url="https://tonapi.io",
    headers={"Authorization": "Bearer <YOUR_API_KEY>"},
    version="v2",
    rps=1,
    retries=2,
)
class TONAPI:
    @endpoint("GET")
    async def status(self) -> dict:
        """Check API status (GET /status)"""

    @endpoint("GET")
    async def rates(self, tokens: str, currencies: str) -> dict:
        """Get token rates (GET /rates?tokens={tokens}&currencies={currencies})"""
```

**Note:**
No base class required. The decorator injects all async context, rate limiting, and HTTP logic automatically.

---

### 3. Group endpoints with namespaces (optional)

For logical endpoint grouping (e.g., `/accounts`, `/users`), use the `@apinamespace` decorator:

```python
from apiq import apinamespace, endpoint


@apinamespace("accounts")
class Accounts:

    @endpoint("GET", path="/{account_id}", as_model=AccountInfoResponse)
    async def info(self, account_id: str) -> AccountInfoResponse:
        """Retrieve account info (GET /accounts/{account_id})"""

    @endpoint("POST", path="/_bulk", as_model=BulkAccountsResponse)
    async def bulk_info(self, body: BulkAccountsRequest) -> BulkAccountsResponse:
        """Retrieve info for multiple accounts (POST /accounts/_bulk)"""

    @endpoint("POST", path="/_bulk")
    async def bulk_info_dict(self, body: dict) -> dict:
        """Retrieve info for multiple accounts (dict body) (POST /accounts/_bulk)"""
```

Then compose in your main client:

```python
@apiclient(
    base_url="https://tonapi.io",
    headers={"Authorization": "Bearer <YOUR_API_KEY>"},
    version="v2",
    rps=1,
    retries=2,
)
class TONAPI:
    # ... endpoints above ...

    @property
    def accounts(self) -> Accounts:
        return Accounts(self)
```

**Note:**

* You can use `"accounts"` or `"/accounts"` in `@apinamespace` — leading slash is optional and combined automatically
  with the endpoint path.

---

### 4. Usage

```python
async def main():
    tonapi = TONAPI()

    async with tonapi:
        # Direct endpoint
        status = await tonapi.status()
        print(status)
        # Namespaced endpoint
        account = await tonapi.accounts.info("UQCDrgGaI6gWK-qlyw69xWZosurGxrpRgIgSkVsgahUtxZR0")
        print(account)
```

---

## API Configuration

All settings are passed to the `@apiclient` decorator:

| Name       | Type  | Description                                        | Default |
|------------|-------|----------------------------------------------------|---------|
| `base_url` | str   | Base URL for your API (must start with http/https) | —       |
| `headers`  | dict  | Default headers (e.g. Authorization)               | None    |
| `timeout`  | float | Default timeout (seconds)                          | None    |
| `rps`      | int   | Max requests per second (rate limit)               | 1       |
| `retries`  | int   | Max retries for 429 (Too Many Requests)            | 3       |
| `cookies`  | dict  | Cookies to send with every request                 | None    |

---

## Endpoints

* Use `@endpoint` to mark methods as API endpoints.
* All method arguments are automatically mapped to path or query parameters.
* For `POST` and `PUT` requests, the `body` can be a Pydantic model or a dict.
* The return type depends on `response_type` and the `as_model` argument.

---

### Notes

* All endpoints and clients are **fully async**; always use `async with` for resource cleanup.
* You may use flat client classes or split logic into namespaces as needed.
* If `as_model` is not set in `@endpoint`, the raw dict (parsed JSON) is returned.
* If `path` is omitted, the method name is used as the endpoint path (e.g., `status` → `/status`).

---

## Contribution

We welcome your contributions!
If you have ideas for improvement or find a bug, please create an issue or submit a pull request.

---

## License

Distributed under the [MIT License](LICENSE).
Feel free to use, modify, and distribute in accordance with the license.

            

Raw data

            {
    "_id": null,
    "home_page": "https://github.com/nessshon/apiq",
    "name": "apiq",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.10",
    "maintainer_email": null,
    "keywords": "api client async http aiohttp pydantic",
    "author": "nessshon",
    "author_email": null,
    "download_url": "https://files.pythonhosted.org/packages/45/0f/c049ba61a394749baf4cf19478ff7ee438c9847eb2dfe6fe1a91f1293132/apiq-0.0.2.tar.gz",
    "platform": null,
    "description": "# \ud83d\udce6 APIQ\n\n**APIQ** is an elegant, fully asynchronous Python toolkit for building robust API clients with minimal code and maximal\ntype safety.\nDefine endpoints with simple decorators, leverage strict Pydantic models, and enjoy integrated rate limiting and\nretries\u2014**no inheritance required**.\n\n[![PyPI](https://img.shields.io/pypi/v/apiq.svg?color=FFE873\\&labelColor=3776AB)](https://pypi.python.org/pypi/apiq)\n![Python Versions](https://img.shields.io/badge/Python-3.10%20--%203.12-black?color=FFE873\\&labelColor=3776AB)\n[![License](https://img.shields.io/github/license/nessshon/apiq)](LICENSE)\n\n![Downloads](https://pepy.tech/badge/apiq)\n![Downloads](https://pepy.tech/badge/apiq/month)\n![Downloads](https://pepy.tech/badge/apiq/week)\n\n---\n\n## Installation\n\n```bash\npip install apiq\n```\n\n---\n\n## Quickstart\n\n### 1. Define your models\n\nUse [Pydantic](https://docs.pydantic.dev/latest/) for type-safe request and response models:\n\n```python\nfrom typing import List\nfrom pydantic import BaseModel\n\n\nclass BulkAccountsRequest(BaseModel):\n    account_ids: List[str]\n\n\nclass AccountInfoResponse(BaseModel):\n    address: str\n    balance: int\n    status: str\n\n\nclass BulkAccountsResponse(BaseModel):\n    accounts: List[AccountInfoResponse]\n```\n\n---\n\n### 2. Define your client class\n\nConfigure all core settings via the `@apiclient` decorator:\n\n```python\nfrom apiq import apiclient, endpoint\n\n\n@apiclient(\n    base_url=\"https://tonapi.io\",\n    headers={\"Authorization\": \"Bearer <YOUR_API_KEY>\"},\n    version=\"v2\",\n    rps=1,\n    retries=2,\n)\nclass TONAPI:\n    @endpoint(\"GET\")\n    async def status(self) -> dict:\n        \"\"\"Check API status (GET /status)\"\"\"\n\n    @endpoint(\"GET\")\n    async def rates(self, tokens: str, currencies: str) -> dict:\n        \"\"\"Get token rates (GET /rates?tokens={tokens}&currencies={currencies})\"\"\"\n```\n\n**Note:**\nNo base class required. The decorator injects all async context, rate limiting, and HTTP logic automatically.\n\n---\n\n### 3. Group endpoints with namespaces (optional)\n\nFor logical endpoint grouping (e.g., `/accounts`, `/users`), use the `@apinamespace` decorator:\n\n```python\nfrom apiq import apinamespace, endpoint\n\n\n@apinamespace(\"accounts\")\nclass Accounts:\n\n    @endpoint(\"GET\", path=\"/{account_id}\", as_model=AccountInfoResponse)\n    async def info(self, account_id: str) -> AccountInfoResponse:\n        \"\"\"Retrieve account info (GET /accounts/{account_id})\"\"\"\n\n    @endpoint(\"POST\", path=\"/_bulk\", as_model=BulkAccountsResponse)\n    async def bulk_info(self, body: BulkAccountsRequest) -> BulkAccountsResponse:\n        \"\"\"Retrieve info for multiple accounts (POST /accounts/_bulk)\"\"\"\n\n    @endpoint(\"POST\", path=\"/_bulk\")\n    async def bulk_info_dict(self, body: dict) -> dict:\n        \"\"\"Retrieve info for multiple accounts (dict body) (POST /accounts/_bulk)\"\"\"\n```\n\nThen compose in your main client:\n\n```python\n@apiclient(\n    base_url=\"https://tonapi.io\",\n    headers={\"Authorization\": \"Bearer <YOUR_API_KEY>\"},\n    version=\"v2\",\n    rps=1,\n    retries=2,\n)\nclass TONAPI:\n    # ... endpoints above ...\n\n    @property\n    def accounts(self) -> Accounts:\n        return Accounts(self)\n```\n\n**Note:**\n\n* You can use `\"accounts\"` or `\"/accounts\"` in `@apinamespace` \u2014 leading slash is optional and combined automatically\n  with the endpoint path.\n\n---\n\n### 4. Usage\n\n```python\nasync def main():\n    tonapi = TONAPI()\n\n    async with tonapi:\n        # Direct endpoint\n        status = await tonapi.status()\n        print(status)\n        # Namespaced endpoint\n        account = await tonapi.accounts.info(\"UQCDrgGaI6gWK-qlyw69xWZosurGxrpRgIgSkVsgahUtxZR0\")\n        print(account)\n```\n\n---\n\n## API Configuration\n\nAll settings are passed to the `@apiclient` decorator:\n\n| Name       | Type  | Description                                        | Default |\n|------------|-------|----------------------------------------------------|---------|\n| `base_url` | str   | Base URL for your API (must start with http/https) | \u2014       |\n| `headers`  | dict  | Default headers (e.g. Authorization)               | None    |\n| `timeout`  | float | Default timeout (seconds)                          | None    |\n| `rps`      | int   | Max requests per second (rate limit)               | 1       |\n| `retries`  | int   | Max retries for 429 (Too Many Requests)            | 3       |\n| `cookies`  | dict  | Cookies to send with every request                 | None    |\n\n---\n\n## Endpoints\n\n* Use `@endpoint` to mark methods as API endpoints.\n* All method arguments are automatically mapped to path or query parameters.\n* For `POST` and `PUT` requests, the `body` can be a Pydantic model or a dict.\n* The return type depends on `response_type` and the `as_model` argument.\n\n---\n\n### Notes\n\n* All endpoints and clients are **fully async**; always use `async with` for resource cleanup.\n* You may use flat client classes or split logic into namespaces as needed.\n* If `as_model` is not set in `@endpoint`, the raw dict (parsed JSON) is returned.\n* If `path` is omitted, the method name is used as the endpoint path (e.g., `status` \u2192 `/status`).\n\n---\n\n## Contribution\n\nWe welcome your contributions!\nIf you have ideas for improvement or find a bug, please create an issue or submit a pull request.\n\n---\n\n## License\n\nDistributed under the [MIT License](LICENSE).\nFeel free to use, modify, and distribute in accordance with the license.\n",
    "bugtrack_url": null,
    "license": "MIT",
    "summary": "Elegant async API client framework with intuitive decorators for quick integration",
    "version": "0.0.2",
    "project_urls": {
        "Homepage": "https://github.com/nessshon/apiq",
        "Issues": "https://github.com/nessshon/apiq/issues",
        "Source": "https://github.com/nessshon/apiq"
    },
    "split_keywords": [
        "api",
        "client",
        "async",
        "http",
        "aiohttp",
        "pydantic"
    ],
    "urls": [
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "361a5e5163215ffc492e3c957030fbce31adca0fb58dc149d25957bae9d869b9",
                "md5": "281e6579e3d023ced4d2ff5eeb8fc695",
                "sha256": "2349dd4ed354f4863bc3753aa740a431e4cea6416fd1bf4210cd0da0a1da1441"
            },
            "downloads": -1,
            "filename": "apiq-0.0.2-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "281e6579e3d023ced4d2ff5eeb8fc695",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.10",
            "size": 13952,
            "upload_time": "2025-07-08T18:50:45",
            "upload_time_iso_8601": "2025-07-08T18:50:45.961748Z",
            "url": "https://files.pythonhosted.org/packages/36/1a/5e5163215ffc492e3c957030fbce31adca0fb58dc149d25957bae9d869b9/apiq-0.0.2-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "450fc049ba61a394749baf4cf19478ff7ee438c9847eb2dfe6fe1a91f1293132",
                "md5": "a32e12e28f218e978f6f9f3ac3540d38",
                "sha256": "326cb83668b8ac5c377e28c87d8411bffc7f26d3fff590f91cd993599d7773d9"
            },
            "downloads": -1,
            "filename": "apiq-0.0.2.tar.gz",
            "has_sig": false,
            "md5_digest": "a32e12e28f218e978f6f9f3ac3540d38",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.10",
            "size": 13372,
            "upload_time": "2025-07-08T18:50:47",
            "upload_time_iso_8601": "2025-07-08T18:50:47.525594Z",
            "url": "https://files.pythonhosted.org/packages/45/0f/c049ba61a394749baf4cf19478ff7ee438c9847eb2dfe6fe1a91f1293132/apiq-0.0.2.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2025-07-08 18:50:47",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "nessshon",
    "github_project": "apiq",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": false,
    "requirements": [
        {
            "name": "aiohttp",
            "specs": [
                [
                    ">=",
                    "3.7.0"
                ],
                [
                    "<=",
                    "3.12.2"
                ]
            ]
        },
        {
            "name": "aiolimiter",
            "specs": [
                [
                    "~=",
                    "1.2.1"
                ]
            ]
        },
        {
            "name": "pydantic",
            "specs": [
                [
                    "<",
                    "3.0"
                ],
                [
                    ">=",
                    "2.0"
                ]
            ]
        }
    ],
    "lcname": "apiq"
}
        
Elapsed time: 1.06350s