---
title: Python
slug: sdks/server-sdks/python
excerpt: Pine Labs Server SDK for Python.
hidden: false
sidebar_order: 3
metadata:
  title: "Python SDK — Official Python Library | Pine Labs Gateway"
  description: "Official Pine Labs Python SDK (pinelabs-python). Install via pip, authenticate with API keys, and integrate payment APIs in your Python backend with type hints."
  keywords: "Python SDK, Pine Labs Python, Python payment SDK, pip package, Python backend integration, Python payment library"
  robots: index
---
 
`pinelabs-python` is the official Python SDK for the **Pine Labs Online Payment Gateway** (Plural). It is generated from the same OpenAPI spec that powers this documentation, so every operation, request, and response model is fully typed.
 
- **Package:** [`pinelabs-python`](https://pypi.org/project/pinelabs-python/) on PyPI
- **Runtime:** Python ≥ 3.8
- **Type hints:** Full PEP 561 type information ships with the package
 
## Install
 
<CodeTabs>
<Tab label="pip">
    ```bash
    pip install pinelabs-python
    ```
</Tab>
<Tab label="poetry">
    ```bash
    poetry add pinelabs-python
    ```
</Tab>
<Tab label="uv">
    ```bash
    uv add pinelabs-python
    ```
</Tab>
</CodeTabs>
 
## Quickstart
 
The Pine Labs API uses **OAuth2 with the `client_credentials` grant**. Exchange your `<client-id>` / `<client-secret>` for an access token, then pass it to `PinelabsApi`.
 
```python
from pinelabs import Amount, PinelabsApi
 
base_url = "https://pluraluat.v2.pinepg.in"  # UAT
# base_url = "https://api.pluralpay.in"      # Production
 
# 1. Get a token. The /token endpoint does not require authentication, so we
#    pass token="" on this bootstrap client.
auth = PinelabsApi(base_url=base_url, token="")
 
token_response = auth.authentication.generate_token(
    grant_type="client_credentials",
    client_id="<client-id>",
    client_secret="<client-secret>",
)
 
# 2. Build an authenticated client
client = PinelabsApi(
    base_url=base_url,
    token=token_response.access_token,
)
 
# 3. Call any operation
order = client.orders.create_order(
    merchant_order_reference="order-001",
    order_amount=Amount(value=50000, currency="INR"),  # ₹500.00
    # ...see the API reference for the full schema
)
 
print(order)
```
 
## Environments
 
| Environment | Base URL                            |
| ----------- | ----------------------------------- |
| UAT         | `https://pluraluat.v2.pinepg.in`    |
| Production  | `https://api.pluralpay.in`          |
 
Pass the URL via `base_url`. The exported `PinelabsApiEnvironment` enum currently only contains `PRODUCTION`; for UAT, set `base_url` explicitly.
 
```python
from pinelabs import PinelabsApi
from pinelabs.environment import PinelabsApiEnvironment
 
client = PinelabsApi(
    token="<access-token>",
    environment=PinelabsApiEnvironment.PRODUCTION,
)
```
 
## Auto-refreshing token
 
For long-running services, cache the token and refresh it before it expires. Pass a callable instead of a string to `token` — it is invoked on every request. Use a separate **bootstrap** `auth` client (without a supplier) to fetch the token, so there is no risk of recursing into the supplier from within itself.
 
```python
import time
import threading
from typing import Optional
from pinelabs import PinelabsApi
 
base_url = "https://pluraluat.v2.pinepg.in"
 
# Bootstrap client used only to fetch tokens. It has no supplier, so calling
# generate_token() from inside get_token() will not recurse.
auth = PinelabsApi(base_url=base_url, token="")
 
_cached: Optional[dict] = None
_lock = threading.Lock()
 
 
def get_token() -> str:
    global _cached
    with _lock:
        # Refresh ~30s before expiry
        if _cached and time.time() < _cached["expires_at"] - 30:
            return _cached["value"]
        r = auth.authentication.generate_token(
            grant_type="client_credentials",
            client_id="<client-id>",
            client_secret="<client-secret>",
        )
        _cached = {
            "value": r.access_token,
            "expires_at": time.time() + r.expires_in,
        }
        return _cached["value"]
 
 
client = PinelabsApi(base_url=base_url, token=get_token)
 
# Every call now picks up a fresh token automatically.
client.orders.get_order_by_id(order_id="ord-123")
```
 
<Callout type="warning" title="Use a separate bootstrap client">
  Always create a dedicated `auth` client (with `token=""`) to fetch tokens. Calling `generate_token()` on a client that itself uses the supplier callable will recurse and hang.
</Callout>
 
## Async client
 
The SDK also exports an async variant for non-blocking use cases (FastAPI, asyncio workers). If you pass a custom `httpx_client`, use `httpx.AsyncClient()` instead of `httpx.Client()`.
 
```python
import asyncio
from pinelabs import Amount, AsyncPinelabsApi
 
client = AsyncPinelabsApi(token="<access-token>")
 
 
async def main() -> None:
    order = await client.orders.create_order(
        merchant_order_reference="order-001",
        order_amount=Amount(value=50000, currency="INR"),
    )
    print(order)
 
 
asyncio.run(main())
```
 
## Sub-clients
 
`PinelabsApi` exposes one sub-client per API tag:
 
| Sub-client                              | Purpose                                            |
| --------------------------------------- | -------------------------------------------------- |
| `client.authentication`                 | OAuth token generation                             |
| `client.orders`                         | Create, capture, cancel, and fetch orders          |
| `client.refunds`                        | Create and look up refunds                         |
| `client.settlements`                    | Settlement reports + UTR lookup                    |
| `client.checkout`                       | Hosted-checkout related operations                 |
| `client.payment_links`                  | Single + bulk payment links                        |
| `client.card_payments`                  | Direct card payment + OTP flow                     |
| `client.bnpl`                           | Buy-Now-Pay-Later eligibility and flows            |
| `client.convenience_fee`                | Convenience-fee config and computation             |
| `client.e_challans`                     | Government e-challan integration                   |
| `client.apple_pay`                      | Apple Pay session + decryption                     |
| `client.international_payments`         | Cross-border (DCC / MCC) payments                  |
| `client.customers`                      | Customer profile management                        |
| `client.tokenization`                   | Card / network tokenization                        |
| `client.payouts`                        | Payouts: balance, create, cancel, list             |
| `client.subscriptions_plans`            | Recurring-billing plans                            |
| `client.subscriptions_subscriptions`    | Subscription lifecycle                             |
| `client.subscriptions_presentations`    | Subscription debit presentations                   |
| `client.pay_by_points`                  | Loyalty / points-based payments                    |
| `client.affordability_suite`            | EMI / offer eligibility                            |
| `client.split_settlements`              | Split settlements between sub-merchants            |
 
For the full operation list and request/response schemas, see the [API reference](/docs/api-reference).
 
## Recipes
 
### Create a payment link
 
```python
from pinelabs import Amount, PinelabsApi
 
client = PinelabsApi(token="<access-token>")
 
link = client.payment_links.create_payment_link(
    merchant_payment_link_reference="link-001",
    amount=Amount(value=50000, currency="INR"),
    description="Order #001",
)
print(link)
```
 
### Fetch an order
 
```python
order = client.orders.get_order_by_id(order_id="v1-241010055924-aa-AHbN0s")
print(order)
```
 
### Refund an order
 
```python
from pinelabs.refunds import CreateRefundRequestOrderAmount
 
refund = client.refunds.create_refund(
    order_id="v1-241010055924-aa-AHbN0s",
    merchant_order_reference="refund-001",
    order_amount=CreateRefundRequestOrderAmount(value=400, currency="INR"),
)
print(refund)
```
 
## Error handling
 
All HTTP errors derive from `pinelabs.core.api_error.ApiError`. The SDK also exposes typed subclasses per status code so you can handle specific failures cleanly.
 
```python
from pinelabs import PinelabsApi
from pinelabs.core.api_error import ApiError
from pinelabs.errors import (
    BadRequestError,
    UnauthorizedError,
    ForbiddenError,
    NotFoundError,
    ConflictError,
    UnprocessableEntityError,
    InternalServerError,
    ServiceUnavailableError,
)
 
client = PinelabsApi(token="<access-token>")
 
try:
    client.orders.get_order_by_id(order_id="missing")
except NotFoundError as e:
    print("order does not exist:", e.body)
except UnauthorizedError:
    print("token expired or invalid")
except ApiError as e:
    print("HTTP", e.status_code, e.body)
```
 
`ApiError` exposes `status_code`, `body` (parsed JSON when the server returns JSON), and `headers`.
 
## Per-request options
 
Every operation accepts a `request_options` keyword argument for ad-hoc overrides:
 
```python
client.orders.create_order(
    merchant_order_reference="order-002",
    order_amount=Amount(value=1000, currency="INR"),
    request_options={
        "timeout_in_seconds": 10,
        "max_retries": 0,
        "additional_headers": {"x-correlation-id": "req-abc"},
    },
)
```
 
Useful for per-call timeouts, propagating trace IDs, or disabling retries on idempotency-sensitive flows.
 
### Retries
 
The SDK retries with exponential backoff (default: **2** retries) on `408`, `429`, and `5xx` responses. Override per request via `max_retries`, or globally via the `request_options` argument on any call.
 
### Timeouts
 
The default timeout is **60 seconds**. Override at the client or per-request level:
 
```python
client = PinelabsApi(token="<access-token>", timeout=20.0)
```
 
### Custom HTTP client
 
```python
import httpx
from pinelabs import PinelabsApi
 
client = PinelabsApi(
    token="<access-token>",
    httpx_client=httpx.Client(
        proxy="http://my.test.proxy.example.com",
        transport=httpx.HTTPTransport(local_address="0.0.0.0"),
    ),
)
```
 
## Type hints
 
The package ships full PEP 561 type information. Request/response models live under `pinelabs.types`; resource-specific request models live under each sub-package (e.g. `pinelabs.refunds.CreateRefundRequestOrderAmount`). The codebase passes `mypy --strict`.
 
## Source & support
 
- PyPI: [pinelabs-python](https://pypi.org/project/pinelabs-python/)
- Issues: [github.com/plural-pinelabs/Pinelabs-Agentic-SDK/issues](https://github.com/plural-pinelabs/Pinelabs-Agentic-SDK/issues)
- API reference: [/docs/api-reference](/docs/api-reference)
 
For the agent-toolkit (LangChain, Vercel AI SDK, OpenAI Agents, Anthropic, Claude Agent SDK), see [`pinelabs-agent-toolkit`](https://www.npmjs.com/package/pinelabs-agent-toolkit). For the command-line interface, see [`pinelabs-cli`](https://www.npmjs.com/package/pinelabs-cli).

 
