Python
Pine Labs Server SDK for Python.
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-pythonon PyPI - Runtime: Python ≥ 3.8
- Type hints: Full PEP 561 type information ships with the package
Install
pip install pinelabs-python
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.
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.
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.
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")
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.
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().
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.
Recipes
Create a payment link
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
order = client.orders.get_order_by_id(order_id="v1-241010055924-aa-AHbN0s")
print(order)
Refund an order
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.
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:
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:
client = PinelabsApi(token="<access-token>", timeout=20.0)
Custom HTTP client
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
- Issues: github.com/plural-pinelabs/Pinelabs-Agentic-SDK/issues
- API reference: /docs/api-reference
For the agent-toolkit (LangChain, Vercel AI SDK, OpenAI Agents, Anthropic, Claude Agent SDK), see pinelabs-agent-toolkit. For the command-line interface, see pinelabs-cli.
