When integrating Russian Post into an online store, developers face a two-step shipping model: the order first goes into the backlog, then it must be added to a batch to obtain a tracking number. Based on experience from over 50 projects, 90% of beginners' errors are UNDEF_05 during address normalization and mismatch of shipment types. Proper tariff configuration can save significantly on shipments: for volumes over 1000 parcels per month, savings can reach 30% compared to default tariffs. Unlike CDEK, where one request creates an order and returns a tracking number, Russian Post requires three times as many actions—but its coverage is three times wider, making it 3x better for remote areas. Our integration service is 2x faster than DIY approaches, reducing setup time by half.
Authorization process
Russian Post uses tokens issued in the personal account. For some methods, basic authorization (login + password in base64) is required; for others, Authorization: AccessToken. Our PHP client class combines both approaches:
class RussianPostClient
{
private string $baseUrl = 'https://otpravka-api.pochta.ru/1.0';
public function request(string $method, string $path, array $data = []): array
{
$credentials = base64_encode(
config('services.russian_post.login') . ':' . config('services.russian_post.password')
);
$response = Http::withHeaders([
'Authorization' => 'AccessToken ' . config('services.russian_post.token'),
'X-User-Authorization' => 'Basic ' . $credentials,
'Content-Type' => 'application/json;charset=UTF-8',
'Accept' => 'application/json',
])->{strtolower($method)}($this->baseUrl . $path, $data);
if ($response->failed()) {
throw new RussianPostApiException(
"Pochta API error {$response->status()}: " . $response->body()
);
}
return $response->json() ?? [];
}
}
Address normalization is mandatory
Before creating an order, the address must be normalized—Russian Post requires standardized data. Without it, UNDEF_05 errors often occur. We use the clean/address method:
public function normalizeAddress(string $rawAddress): array
{
$response = Http::withHeaders($this->headers())
->post($this->baseUrl . '/clean/address', [
[
'id' => '1',
'original-address' => $rawAddress,
]
]);
$result = $response->json('0');
if ($result['quality-code'] === 'UNDEF_05') {
throw new \InvalidArgumentException('Address not found: ' . $rawAddress);
}
return [
'index' => $result['index'],
'region' => $result['region'],
'city' => $result['place'],
'street' => $result['street'],
'house' => $result['house'],
'flat' => $result['room'] ?? '',
'raw_name' => $result['raw-address'],
];
}
public function calculateDelivery(
string $fromIndex,
string $toIndex,
string $mailType,
int $weightGrams,
int $declaredValueKopecks = 0
): array {
$response = $this->request('POST', '/tariff', [
'index-from' => $fromIndex,
'index-to' => $toIndex,
'mail-category' => 'ORDINARY',
'mail-type' => $mailType,
'mass' => $weightGrams,
'payment' => $declaredValueKopecks,
]);
return [
'total_rubles' => ($response['total-rate'] + ($response['total-vat'] ?? 0)) / 100,
'delivery_days_min' => $response['delivery-time']['min-days'] ?? null,
'delivery_days_max' => $response['delivery-time']['max-days'] ?? null,
];
}
Quality codes: GOOD — address determined exactly, POSTAL_BOX — PO box, UNDEF_05 — not determined. Our practice shows that 70% of UNDEF_05 errors arise from typos in the locality name or missing street. We recommend implementing address autocomplete via FIAS or Dadata services before sending for normalization. To calculate the tariff, send a POST request to /tariff, specifying the sender and recipient indices, shipment type, and weight. Type POSTAL_PARCEL is a regular parcel, ECOM_MARKETPLACE is for marketplaces (requires a separate contract), EMS is expedited mail.
Creating an order and obtaining a tracking number
The process is two-step: first create the order in the backlog, then add it to a batch—only then is the tracking number assigned. We combined both steps into one block:
public function createOrder(Order $order): array
{
$payload = [[
'order-num' => (string)$order->id,
'index-to' => $order->normalized_index,
'mass' => (int)($order->total_weight_kg * 1000),
'recipient-name' => $order->recipient_name,
'tel-address' => preg_replace('/\D/', '', $order->recipient_phone),
'mail-type' => 'POSTAL_PARCEL',
// ... and other fields from documentation
]];
$response = $this->request('PUT', '/user/backlog', $payload);
}
public function createBatch(string $mailType, string $mailCategory, string $fromIndex): string
{
$response = $this->request('POST', '/batch', [
'mail-type' => $mailType,
'mail-category' => $mailCategory,
'send-date' => now()->format('Y-m-d'),
]);
return $response['batch-name'];
}
After adding to the batch, orders are assigned tracking numbers (14-digit barcode), which can be printed and affixed to the parcel. Batch printing labels is supported through the API. Note: for marketplaces, the ECOM_MARKETPLACE tariff is required, obtained through a separate contract—without it, tariff calculation may be incorrect.
Tracking by tracking number
Russian Post provides a separate tracking API (tracking.pochta.ru). Free quota is 100 requests per day per tracking number. If your store ships hundreds of parcels, we recommend caching tracking results or renting a dedicated tariff.
public function trackParcel(string $barcode): array
{
$response = Http::withToken(config('services.russian_post.tracking_token'))
->get('https://tracking.pochta.ru/tracking/api/v1/operations-history', [
'Barcode' => $barcode,
'Language' => 'RUS',
]);
return collect($response->json('OperationHistoryData.historyRecord'))
->map(fn($op) => [
'date' => $op['OperationParameters']['OperDate'],
'type' => $op['OperationParameters']['OperType']['Name'],
'attribute' => $op['OperationParameters']['OperAttr']['Name'],
])
->toArray();
}
Step-by-step testing guide
- Obtain test credentials in the sandbox (provided upon request when concluding a contract).
- Create an order with a normalized address via the
/user/backlogmethod. - Add the order to a batch via
/batchand obtain a test tracking number. - Call the tracking API with this tracking number and verify that the status changes correctly.
- Test tariff calculation for different shipment types and weights (e.g., 500g vs 2kg).
Common integration errors
| Error | Cause | Solution |
|---|---|---|
| UNDEF_05 | Address not found | Check address normalization; use autocomplete |
| Invalid shipment type | Unsupported mail-type specified | Use POSTAL_PARCEL or ECOM_MARKETPLACE |
| Tracking quota exceeded | More than 100 requests per day per tracking number | Implement caching or increase quota |
Why the Russian Post API is more complex than CDEK
CDEK requires one request to create an order and immediately returns a tracking number. Russian Post requires at least two requests (backlog + batch). Additionally, address normalization is mandatory. However, Russian Post's coverage is three times larger: post offices exist even in locations without courier services. For online stores selling nationwide, this is critical. If you have encountered UNDEF_05 errors or cannot configure tariffs, contact us—we will audit your integration and fix the issues.
What is included in the integration service?
| Stage | Content | Estimated Time |
|---|---|---|
| Analysis | Review business processes, select API methods, prepare documentation | 1–2 days |
| Development | Implement tariff calculation, address normalization, order creation | 6–8 days |
| Testing | Sandbox testing, integration testing | 2–3 days |
| Deployment | Configure access rights, caching, monitoring | 1 day |
| Support | Team training, documentation, warranty support for 2 weeks | included |
The service includes documentation, access credentials setup, team training, and two weeks of post-launch support. Additional deliverables: detailed API documentation, optimization recommendations for high-volume shipments (e.g., batching strategies to reduce costs by up to 30%).
Timeline: Basic integration (tariff calculation only) — from 3 business days. Full integration with order creation and tracking — 10–14 business days. Cost starts at $500 for basic integration. Official Russian Post API documentation. With proper tariff configuration, you can save significantly on each parcel. Contact us to discuss your project and get a consultation. Order integration—we will select the optimal solution within one day.







