Get from zero to an accurate INR shipping rate in a single call. REST, JSON, India-first — keys, pincodes and zones included.
Getting started
Send your first POST /rates request. Use your test or live key in the Authorization header — the example below prices Jaipur (302001) → Guwahati (781001).
Grab a key
Create a project and copy your pp_live_… key.
Send pincodes + parcel
POST origin, destination, weight & optional size.
Receive itemised INR
Get zone, billable weight and a GST breakdown.
https://api.postpin.in/v1/rates/calculatecurl https://api.postpin.in/v1/rates/calculate \
-H "Authorization: Bearer pp_live_3kQ9xR2pLmZ" \
-H "Content-Type: application/json" \
-d '{
"origin": "302001",
"destination": "781001",
"weight": 400,
"length": 30,
"width": 25,
"height": 8,
"service": "surface",
"cod": true,
"declared_value": 1499
}'Security
Postpin uses bearer API keys. Pass your key in the Authorization header on every request. Keep live keys server-side only.
Authorization: Bearer pp_live_3kQ9xR2pLmZKey types
pp_test_… runs against the sandbox. pp_live_… bills real usage. Rotate keys anytime from the dashboard.
Rate limits & quota
Every keyed response carries x-ratelimit-remaining and x-quota-remaining. On a 429, back off using Retry-After.
Core
Calculate an itemised shipping charge between two Indian pincodes. Returns the zone, billable weight (max of actual & volumetric) and a GST-inclusive breakdown.
https://api.postpin.in/v1/rates/calculate| Parameter | Type | Required | Description |
|---|---|---|---|
| origin | string | Required | Pickup pincode (6 digits). |
| destination | string | Required | Delivery pincode (6 digits). |
| weight | integer | Required | Actual parcel weight in grams (max 100000). |
| length | number | Optional | Parcel length in cm. With width & height, drives volumetric weight = L×W×H/5000. |
| width | number | Optional | Parcel width in cm. |
| height | number | Optional | Parcel height in cm. |
| service | enum | Optional | surface (default), express, or same_day. |
| cod | boolean | Optional | Set true for cash-on-delivery to add COD handling. |
| declared_value | integer | Optional | Declared parcel value in INR. Drives the 1.5% COD fee. |
{
"origin": "302001",
"destination": "781001",
"weight": 400,
"length": 30,
"width": 25,
"height": 8,
"service": "surface",
"cod": true,
"declared_value": 1499
}{
"data": {
"zone": "ne_jk",
"zoneLabel": "Special / Remote",
"service": "surface",
"serviceLabel": "Surface",
"chargeableWeightGrams": 1200,
"volumetricWeightGrams": 1200,
"etaDays": [5, 9],
"currency": "INR",
"breakdown": [
{ "label": "Base charge", "amount": 95.00, "hint": "Special / Remote · Surface" },
{ "label": "Weight charge", "amount": 108.00, "hint": "1.50 kg chargeable" },
{ "label": "Fuel surcharge", "amount": 24.36, "hint": "12%" },
{ "label": "COD handling", "amount": 57.49, "hint": "₹35 + 1.5%" },
{ "label": "GST", "amount": 51.27, "hint": "18%" }
],
"total": 336.12,
"totalPaise": 33612,
"origin": { "pincode": "302001", "city": "Jaipur", "state": "Rajasthan" },
"destination": { "pincode": "781001", "city": "Guwahati", "state": "Assam" },
"serviceable": true
},
"meta": { "request_id": "req_7Yh2mKp", "api_version": "v1", "cached": false, "engine_ms": 11 }
}| Field | Type | Description |
|---|---|---|
| zone | string | within_city · within_state · metro · roi · ne_jk. |
| zoneLabel | string | Local · Regional · Metro · National · Special / Remote. |
| service | string | The resolved service level. |
| serviceLabel | string | Human-readable service name. |
| chargeableWeightGrams | integer | max(actual, volumetric) in grams. |
| volumetricWeightGrams | integer | Volumetric weight in grams. |
| etaDays | [int, int] | Estimated delivery window, low–high. |
| currency | string | Always INR. |
| breakdown | array | Itemised charge lines (label, amount, hint?). |
| total | number | Grand total in INR, GST inclusive. |
| totalPaise | integer | Grand total in paise (integer, no rounding drift). |
| origin | object | { pincode, city, state } of the pickup. |
| destination | object | { pincode, city, state } of the delivery. |
| serviceable | boolean | False when a pincode is unknown or off-network. |
Core
Check whether a single pincode is on-network before you quote. Returns city, state, zone, COD availability and an ETA window.
https://api.postpin.in/v1/serviceability/:pincodecurl https://api.postpin.in/v1/serviceability/781001 \
-H "Authorization: Bearer pp_live_3kQ9xR2pLmZ"{
"data": {
"pincode": "781001",
"serviceable": true,
"found": true,
"city": "Guwahati",
"state": "Assam"
},
"meta": { "request_id": "req_7Yh2mKp", "api_version": "v1", "cached": false }
}Reference
Search the India Post-synced pincode master by code or city name. Pass q (min 2 chars); a numeric q matches by pincode prefix, otherwise by city/district/state.
https://api.postpin.in/v1/pincodescurl "https://api.postpin.in/v1/pincodes?q=jaipur&limit=5" \
-H "Authorization: Bearer pp_live_3kQ9xR2pLmZ"{
"data": [
{ "pincode": "302001", "city": "Jaipur", "state": "Rajasthan", "metro": false, "serviceable": true },
{ "pincode": "302002", "city": "Jaipur", "state": "Rajasthan", "metro": false, "serviceable": true }
],
"meta": { "request_id": "req_9Kd4nQw", "api_version": "v1", "has_more": false }
}Results are capped by limit (1–10, default 5); meta.has_more tells you when more matches exist. The full master covers 19,000+ serviceable pincodes.
Reference
Subscribe to events and Postpin will POST a signed JSON payload to your endpoint. Always verify the X-Postpin-Signature header against the raw body before trusting a payload.
| Event | Triggered when |
|---|---|
rate.calculated | A rate was successfully computed via the API. |
key.created | A new API key was issued for the workspace. |
key.revoked | An API key was revoked. |
subscription.updated | Plan, status or limits changed for the workspace. |
invoice.paid | A usage / subscription invoice was paid. |
sync.completed | An India Post pincode sync run finished. |
sync.failed | A pincode sync run failed and needs attention. |
POST /your-endpoint HTTP/1.1
X-Postpin-Signature: t=1718900000,v1=5257a869e7ec...
X-Postpin-Event: rate.calculated
X-Postpin-Event-Id: evt_2nKp7Yh
Content-Type: application/json
User-Agent: Postpin-Webhooks/1.0
{
"id": "evt_2nKp7Yh",
"event": "rate.calculated",
"created": "2026-06-20T18:13:20.000Z",
"data": {
"origin": "302001",
"destination": "781001",
"zone": "ne_jk",
"total": 336.12,
"currency": "INR"
}
}import { Postpin } from "@postpin/node";
// Always verify against the RAW request body — never a re-serialized object.
app.post("/webhooks/postpin", express.raw({ type: "application/json" }), (req, res) => {
let event;
try {
event = Postpin.webhooks.constructEvent(
req.body, // raw Buffer
req.headers["x-postpin-signature"],
process.env.POSTPIN_WEBHOOK_SECRET,
);
} catch {
return res.sendStatus(400); // bad signature or stale timestamp
}
switch (event.event) {
case "rate.calculated":
// handle event.data
break;
}
res.sendStatus(200);
});Reference
Postpin uses conventional HTTP status codes and a stable error envelope: { error: { code, message, request_id } }. Quote the request_id when contacting support.
| Status | Code | Meaning |
|---|---|---|
| 400 | validation_error | A field is missing or malformed. details lists the offending paths. |
| 401 | invalid_key | Missing, invalid or expired API key (also missing_key, key_expired). |
| 402 | quota_exceeded | Monthly included-call quota exhausted. Upgrade your plan. |
| 403 | no_subscription | The key's workspace has no active subscription. |
| 404 | not_found | Pincode or resource is unknown to the master. |
| 429 | rate_limited | Plan RPM exceeded. Inspect Retry-After. |
| 500 | internal_error | Unexpected error. Safe to retry with backoff. |
{
"error": {
"code": "rate_limited",
"message": "Plan limit of 300 requests/min exceeded.",
"request_id": "req_7Yh2mKp"
}
}Tooling
Four official, typed SDKs wrap authentication, retries, idempotency and webhook verification. All are published — install your language and you're a few lines from a rate.
Node
@postpin/nodenpm install @postpin/nodePython
postpinpip install postpinPHP
its-pradeependra/postpin-phpcomposer require its-pradeependra/postpin-phpGo
github.com/its-pradeependra/postpin-gogo get github.com/its-pradeependra/postpin-goUpdates
Every API change, newest first. The /v1 surface is stable; breaking changes ship behind a new version.
Create a key in seconds — 1,000 free calls every month.