API reference · https://api.postpin.in/v1

Postpin developer docs

Get from zero to an accurate INR shipping rate in a single call. REST, JSON, India-first — keys, pincodes and zones included.

Getting started

Quickstart

Send your first POST /rates request. Use your test or live key in the Authorization header — the example below prices Jaipur (302001) → Guwahati (781001).

    1

    Grab a key

    Create a project and copy your pp_live_… key.

    2

    Send pincodes + parcel

    POST origin, destination, weight & optional size.

    3

    Receive itemised INR

    Get zone, billable weight and a GST breakdown.

POSThttps://api.postpin.in/v1/rates/calculate
curl 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

Authentication

Postpin uses bearer API keys. Pass your key in the Authorization header on every request. Keep live keys server-side only.

Authorization header
bash
Authorization: Bearer pp_live_3kQ9xR2pLmZ

Key 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

Rate API

Calculate an itemised shipping charge between two Indian pincodes. Returns the zone, billable weight (max of actual & volumetric) and a GST-inclusive breakdown.

POSThttps://api.postpin.in/v1/rates/calculate

Request parameters

ParameterTypeRequiredDescription
originstringRequiredPickup pincode (6 digits).
destinationstringRequiredDelivery pincode (6 digits).
weightintegerRequiredActual parcel weight in grams (max 100000).
lengthnumberOptionalParcel length in cm. With width & height, drives volumetric weight = L×W×H/5000.
widthnumberOptionalParcel width in cm.
heightnumberOptionalParcel height in cm.
serviceenumOptionalsurface (default), express, or same_day.
codbooleanOptionalSet true for cash-on-delivery to add COD handling.
declared_valueintegerOptionalDeclared parcel value in INR. Drives the 1.5% COD fee.

Example request body

POST /v1/rates
json
{
  "origin": "302001",
  "destination": "781001",
  "weight": 400,
  "length": 30,
  "width": 25,
  "height": 8,
  "service": "surface",
  "cod": true,
  "declared_value": 1499
}

Example response

200 OK
json
{
  "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 }
}

Response schema

FieldTypeDescription
zonestringwithin_city · within_state · metro · roi · ne_jk.
zoneLabelstringLocal · Regional · Metro · National · Special / Remote.
servicestringThe resolved service level.
serviceLabelstringHuman-readable service name.
chargeableWeightGramsintegermax(actual, volumetric) in grams.
volumetricWeightGramsintegerVolumetric weight in grams.
etaDays[int, int]Estimated delivery window, low–high.
currencystringAlways INR.
breakdownarrayItemised charge lines (label, amount, hint?).
totalnumberGrand total in INR, GST inclusive.
totalPaiseintegerGrand total in paise (integer, no rounding drift).
originobject{ pincode, city, state } of the pickup.
destinationobject{ pincode, city, state } of the delivery.
serviceablebooleanFalse when a pincode is unknown or off-network.

Core

Serviceability

Check whether a single pincode is on-network before you quote. Returns city, state, zone, COD availability and an ETA window.

GEThttps://api.postpin.in/v1/serviceability/:pincode

Request

bash
curl https://api.postpin.in/v1/serviceability/781001 \
  -H "Authorization: Bearer pp_live_3kQ9xR2pLmZ"

Response

200 OK
json
{
  "data": {
    "pincode": "781001",
    "serviceable": true,
    "found": true,
    "city": "Guwahati",
    "state": "Assam"
  },
  "meta": { "request_id": "req_7Yh2mKp", "api_version": "v1", "cached": false }
}

Reference

Pincodes

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.

GEThttps://api.postpin.in/v1/pincodes

Request

bash
curl "https://api.postpin.in/v1/pincodes?q=jaipur&limit=5" \
  -H "Authorization: Bearer pp_live_3kQ9xR2pLmZ"

Response

200 OK
json
{
  "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

Webhooks

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 types

EventTriggered when
rate.calculatedA rate was successfully computed via the API.
key.createdA new API key was issued for the workspace.
key.revokedAn API key was revoked.
subscription.updatedPlan, status or limits changed for the workspace.
invoice.paidA usage / subscription invoice was paid.
sync.completedAn India Post pincode sync run finished.
sync.failedA pincode sync run failed and needs attention.

Signed payload

rate.calculated
http
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"
  }
}

Verify the signature

verify.js
javascript
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

Errors

Postpin uses conventional HTTP status codes and a stable error envelope: { error: { code, message, request_id } }. Quote the request_id when contacting support.

StatusCodeMeaning
400validation_errorA field is missing or malformed. details lists the offending paths.
401invalid_keyMissing, invalid or expired API key (also missing_key, key_expired).
402quota_exceededMonthly included-call quota exhausted. Upgrade your plan.
403no_subscriptionThe key's workspace has no active subscription.
404not_foundPincode or resource is unknown to the master.
429rate_limitedPlan RPM exceeded. Inspect Retry-After.
500internal_errorUnexpected error. Safe to retry with backoff.
429 Too Many Requests
json
{
  "error": {
    "code": "rate_limited",
    "message": "Plan limit of 300 requests/min exceeded.",
    "request_id": "req_7Yh2mKp"
  }
}

Tooling

SDKs

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/node
npm
bash
npm install @postpin/node

Python

postpin
PyPI
bash
pip install postpin

PHP

its-pradeependra/postpin-php
Packagist
bash
composer require its-pradeependra/postpin-php

Go

github.com/its-pradeependra/postpin-go
pkg.go.dev
bash
go get github.com/its-pradeependra/postpin-go

Updates

Changelog

Every API change, newest first. The /v1 surface is stable; breaking changes ship behind a new version.

  1. v1.4.012 Jun 2026
    • Added GET /v1/serviceability/:pincode for single-call route checks.
    • Webhook signatures now include a replay-protection timestamp.
  2. v1.3.028 Apr 2026
    • same_day service level is now generally available on metro routes.
    • Pincode search endpoint added with fuzzy city matching.
  3. v1.2.003 Mar 2026
    • Per-customer rate cards exposed on Growth and above.
    • Nightly India Post sync moved to 00:30 IST.

Ready to ship accurate rates?

Create a key in seconds — 1,000 free calls every month.