Python SDK
StampMitra Developer API का आधिकारिक Python क्लाइंट: sync और async क्लाइंट, typed pydantic models और signed-webhook helpers के साथ identity verification और e-Stamp।
Pre-release 0.1.0 - package registry पर प्रकाशन जारी है
- Package
stampmitra- संस्करण
- 0.1.0
- आवश्यकता
- Python 3.9 या उससे नया। Runtime dependencies: httpx और pydantic 2। Sync और async क्लाइंट, typed (py.typed, mypy --strict पास)।
- लाइसेंस
- MIT
- Changelog
- CHANGELOG.md
- StampMitra और AsyncStampMitra क्लाइंट, context managers के रूप में उपयोगी।
- Typed pydantic v2 models: JSON camelCase में, attributes snake_case में, अज्ञात fields सुरक्षित।
- Jitter वाले backoff के साथ retries, और webhook replay को छोड़कर हर POST पर अपने आप Idempotency-Key।
- Flask और FastAPI के webhook उदाहरण, और iterator-आधारित pagination।
Package इंस्टॉल करें
pip install stampmitraप्रकाशित होने के बाद उपलब्ध। अभी PyPI पर प्रकाशित नहीं हुआ है। यह कमांड package के रिलीज़ होने के बाद काम करेगा; तब तक रिलीज़ के लिए GitHub repository देखते रहें।
pip install "git+https://github.com/Bani-Global-Industries-LLP/stampmitra-developer-python.git"तब तक GitHub से इंस्टॉल करें: यह repository एक मानक pyproject (hatchling, src layout) है, इसलिए pip इसे सीधे Git से build कर सकता है।
पाँच पंक्तियों में PAN verify करें
बनाते समय sandbox key (sk_test_...) इस्तेमाल करें। यह मुफ़्त है, आपके वॉलेट से कुछ नहीं कटता और sandbox डेटा पर चलती है।
from stampmitra import StampMitra
client = StampMitra() # reads STAMPMITRA_API_KEY
result = client.verify.pan("ABCDE1234F", name="Rahul Kumar")
print(result.valid, result.registered_name, result.reference_id)API keys और environments
Sandbox
Sandbox keys sk_test_ से शुरू होती हैं। ये मुफ़्त हैं और sandbox डेटा लौटाती हैं।
Live
Live keys sk_live_ से शुरू होती हैं। इनके लिए स्वीकृत production access request चाहिए और हर सफल कॉल पर शुल्क लगता है।
api_key="sk_test_..." दें या STAMPMITRA_API_KEY environment variable सेट करें। client.environment का मान "test" या "live" होता है। जो key ^sk_(test|live)_[A-Za-z0-9_-]{8,}$ से मेल नहीं खाती वह अस्वीकार होती है, जब तक आप allow_custom_key=True न दें।
from stampmitra import StampMitra, RequestOptions
client = StampMitra(
api_key="sk_test_...",
base_url="https://api-b2b.stampmitra.in",
timeout=30,
max_retries=2,
default_headers={},
app_info={"name": "my-app", "version": "1.0.0", "url": "https://example.com"},
debug=False,
validate_input=True,
allow_custom_key=False,
)
# Every method also takes per-request options:
client.verify.pan(
"ABCDE1234F",
options=RequestOptions(timeout=10, max_retries=1, idempotency_key="...", headers={}),
)हर resource एक जगह
JSON के field नाम camelCase और model attributes snake_case (registered_name) में होते हैं। अज्ञात fields सुरक्षित रहते हैं (model.model_extra, model.to_dict())। e-Stamp और ऑर्डर की request bodies सादे dicts हैं, ठीक वैसे जैसे API में दिए हैं। हर लौटाए गए model में status_code, headers, request_id और content वाला .response होता है। जिन endpoints का आकार spec में तय नहीं, वे ApiObject लौटाते हैं।
# verify (every call is billed, including negative results; HTTP 200 with valid=False)
client.verify.pan("ABCDE1234F", name="Rahul Kumar")
client.verify.gstin("07AAAAA1111A1Z1", business_name="Acme")
client.verify.rc("MH12AB1234")
client.verify.voter_id("ABC1234567")
client.verify.driving_license("MH1420110012345", "1990-01-01") # IP-allowlisted key
client.verify.passport("PA1079341954215", "1990-01-01") # IP-allowlisted key
client.verify.cin("U74999DL2015PTC284009")
client.verify.udyam("UDYAM-MH-12-1234567")
client.verify.pan_to_gstin("ABCDE1234F")
client.verify.pan_to_udyam("ABCDE1234F")
client.verify.bank_account("000123456789", "ICIC0001234", name="Jane Doe")
# services and e-Stamp
client.services.list()
client.services.get("estamp.instrument.rent_agreement")
client.estamp.get_schema(code)
client.estamp.list_states()
client.estamp.list_instruments("MH")
client.estamp.list_articles(instrument_code)
client.estamp.calculate_duty({...})
client.estamp.add_parties(order_id, {...})
# orders and payments
order = client.orders.create({"externalOrderId": "o1", "externalCustomerId": "c1", "serviceCode": code, "details": {}})
client.orders.get(id)
client.orders.get_status(id)
client.orders.get_payment(id)
client.orders.list_documents(id)
client.orders.get_document_download(id, document_id)
client.payments.intents.create({...})
client.payments.intents.get(intent_id)
# usage, logs, webhook events
client.usage.summary()
client.logs.list(from_="2026-10-01", to="2026-10-16", service="pan.verify", status="200", limit=50)
client.webhooks.events.list(limit=20)
client.webhooks.events.replay(event_id)Pagination
client.webhooks.events.iter_all() भी इसी तरह काम करता है (यह /v1/logs की तरह items / nextCursor आकार मानकर चलता है)।
for entry in client.logs.iter_all(service="pan.verify", limit=100): # follows nextCursor
print(entry.id)
async for entry in async_client.logs.iter_all(limit=100):
...Error classes
हर error StampMitraError से बना है (status, code, message, request_id, body)। ValidationError में .field और RateLimitError में .retry_after (सेकंड) होता है। Non-JSON error body भी status से map होती है। API की error body {"error": {"code", "message", "requestId", "field?"}} है।
| HTTP status | Class | टिप्पणी |
|---|---|---|
| 400 / 422 | ValidationError | गलत इनपुट। Validation helpers इसे request भेजने से पहले local रूप से भी उठाते हैं। |
| 401 | AuthenticationError | API key न मिली, गलत है या रद्द हो चुकी है। |
| 402 | PaymentRequiredError | वॉलेट में बैलेंस या क्रेडिट कम है। |
| 403 | PermissionDeniedError | सर्वर का संदेश जस का तस दिया जाता है, जैसे Passport और Driving Licence के लिए IP-allowlisted key चाहिए। |
| 404 | NotFoundError | ऑर्डर, सेवा या इवेंट की id मौजूद नहीं है। |
| 409 | ConflictError | Idempotency-Key किसी दूसरी body के साथ दोबारा इस्तेमाल हुई, या state में टकराव है। |
| 429 | RateLimitError | रुककर दोबारा कोशिश करें; सर्वर Retry-After भेजे तो वह मान उपलब्ध रहता है। |
| 5xx | UpstreamError | सर्वर या upstream की ओर की विफलता। 502, 503 और 504 पर अपने आप retry होता है। |
| network / timeout | APIConnectionError / APITimeoutError | कोई उपयोगी response नहीं मिला: कनेक्शन विफल, timeout या cancel। |
from stampmitra import StampMitraError, ValidationError, RateLimitError, PermissionDeniedError
try:
client.verify.pan("ABCDE1234F")
except ValidationError as e: # 400 (or local validation, e.status is None)
print(e.field, e.message)
except RateLimitError as e: # 429
print(e.retry_after)
except StampMitraError as e: # base class
print(e.status, e.code, e.request_id)Retries और idempotency
- डिफ़ॉल्ट max_retries 2 है, exponential backoff और full jitter के साथ (base 0.5 s, cap 8 s)। Retry-After का पालन होता है, अधिकतम 60 s तक।
- Retry होते हैं: 429, 502, 503, 504, connection errors और timeouts, GET के लिए और उन POST requests के लिए जिनमें Idempotency-Key हो (webhook replay को छोड़कर हर POST पर SDK यह key जोड़ता है)।
- Webhook replay को छोड़कर हर POST (सभी verify.* calls, estamp.calculate_duty, estamp.add_parties, orders.create और payments.intents.create) को random UUIDv4 Idempotency-Key मिलती है, जो अपने आप होने वाले retries में वही रहती है, जब तक आप options=RequestOptions(idempotency_key=...) न दें, जो हमेशा प्राथमिकता पाती है। Retry हुआ verification दोबारा शुल्क के बजाय सर्वर द्वारा replay होता है। हर अलग कॉल को नई key मिलती है।
- webhooks.events.replay में अपने आप key नहीं लगती और response मिलने के बाद उसे कभी retry नहीं किया जाता; वह सिर्फ़ तब retry होता है जब TCP कनेक्शन बन ही नहीं पाया। Key चाहिए तो स्पष्ट idempotency_key दें।
क्लाइंट-साइड validation
भेजने से पहले इनपुट normalise (trim, upper-case) होकर बैकएंड जैसे ही patterns से जाँचे जाते हैं। विफल होने पर ValidationError local रूप से उठता है, बिना network call और बिना शुल्क के। इसे StampMitra(validate_input=False) से बंद करें।
Webhook signatures जाँचें
हर डिलीवरी में X-Webhook-Signature, X-Webhook-Timestamp (Unix सेकंड) और X-Webhook-Event-Id होते हैं। Signature, timestamp, एक बिंदु और raw body का hex HMAC-SHA256 है, जो आपके endpoint secret से बनता है। जाँच हमेशा raw body पर करें, दोबारा serialise किए गए JSON पर नहीं।
webhooks module signature को constant-time में जाँचता है और उन timestamps को अस्वीकार करता है जो अभी से tolerance_seconds (डिफ़ॉल्ट 300) से ज़्यादा दूर हों। यही helpers क्लाइंट पर client.webhooks.verify_signature और construct_event के रूप में भी हैं।
from stampmitra import webhooks, WebhookSignatureError
event = webhooks.construct_event(
raw_body,
headers["X-Webhook-Timestamp"],
headers["X-Webhook-Signature"],
secret,
)
# webhooks.verify_signature(...) returns True or raises WebhookSignatureErrorWebhookSignatureError में एक code होता है: signature_mismatch, timestamp_outside_tolerance, invalid_timestamp या missing_secret।
Framework के उदाहरण
import os
from flask import Flask, request
from stampmitra import WebhookSignatureError, webhooks
app = Flask(__name__)
SECRET = os.environ["STAMPMITRA_WEBHOOK_SECRET"]
@app.post("/webhooks/stampmitra")
def receive() -> "tuple[str, int]":
try:
event = webhooks.construct_event(
request.get_data(), # the RAW body, not request.json
request.headers.get(webhooks.TIMESTAMP_HEADER, ""),
request.headers.get(webhooks.SIGNATURE_HEADER, ""),
SECRET,
)
except WebhookSignatureError as err:
return str(err), 400
print("event", event.core_order_id, event.core_event_type, event.occurred_at)
return "ok", 200Webhooks: इवेंट के प्रकार, payload और retry नीति Webhooks पेज पर हैं।
सुरक्षित logging
डिफ़ॉल्ट रूप से बंद। StampMitra(debug=True) stampmitra logger में log करता है (या logger= दें)। Log की गई bodies में Authorization और x-api-key redact रहते हैं और PAN, GSTIN, account numbers व नाम masked रहते हैं।
इंटीग्रेट करने के दूसरे तरीके
REST API किसी भी भाषा से चलती है, और बाकी SDKs भी उसी contract पर बने हैं।
API कॉल करने के लिए तैयार हैं?
मुफ़्त sandbox key लें और ऊपर का quickstart लगभग 5 मिनट में चलाएँ।