Imagine: a customer adds a lightweight product to the cart, your store calculator shows one amount, but on the Belpochta website it's different. That difference drives the client to a competitor. The snag lies in weight category boundaries: your code rounded up to the wrong category. Integrating Belpochta into an online store is no trivial task: immature API, lack of unified tariffs, manual calculation. We've been solving this for several years and have completed over 50 projects with postal services in the CIS. In this article, we'll break down the pitfalls and present working solutions.
Belpochta tariffs depend on weight, distance, and volume. For international shipments, volumetric weight (length × width × height / 5000) applies, which is often ignored. This can cause significant errors in cost. Calculation via the corporate API is much more accurate than the table method for non-standard parcel dimensions. The API allows creating orders with cash on delivery; a commission applies.
Why Is Belpochta Integration Harder Than It Seems?
Belpochta is the national postal operator of Belarus, but its API is less mature than those of Russian services. Some functionality is implemented through custom calculations based on official tariff tables. Without a contract with Belpochta, you are limited to the table method, which requires manual updates and does not account for corporate customer discounts. According to Belpochta's documentation, the corporate API can automate most of the shipment creation processes, reducing manual labor.
Table method vs API: comparison
| Parameter | Tables (no contract) | Corporate API |
|---|---|---|
| Accuracy | Lower for non-standard dimensions | 100% accuracy |
| Automation | Requires manual updates | Full automation |
| Order creation | Manual | Via API |
| Tracking | Only public page | Built-in tracking |
| Requirements | None | Contract and API key |
What the Work Includes
We offer a turnkey comprehensive integration:
- Audit of current cart and delivery methods
- Implementation of a Belpochta branch selection widget
- Shipping cost calculator (tables or API)
- Order creation via corporate API
- Tracking page for the customer
- Documentation and manager training
- Post-launch support
How We Do It
Calculation Using Tariff Tables
Belpochta tariffs are structured by weight categories and delivery zones (within Minsk, within Belarus, international). Example tariffs for domestic parcels are available upon request. The calculator implementation reads from a configurable table.
class BelpochtaTariffCalculator
{
// Tariff data is loaded from an external source or configuration
private array $domesticParcels = [];
private float $courierSurcharge = 0; // Set from config
public function calculateDeclaredValueFee(float $value): float
{
// Fee calculated based on a percentage with a minimum
return max(0, $value * 0.005); // Example percentage
}
public function calculate(
float $weightKg,
bool $toDoor = false,
float $declaredValue = 0,
string $type = 'parcel'
): array {
$basePrice = null;
foreach ($this->domesticParcels as $maxWeight => $price) {
if ($weightKg <= $maxWeight) {
$basePrice = $price;
break;
}
}
if ($basePrice === null) {
throw new \InvalidArgumentException('Weight exceeds the maximum allowed');
}
$total = $basePrice;
if ($toDoor) $total += $this->courierSurcharge;
if ($declaredValue > 0) $total += $this->calculateDeclaredValueFee($declaredValue);
return [
'base' => $basePrice,
'courier_fee' => $toDoor ? $this->courierSurcharge : 0,
'declared_fee' => $declaredValue > 0 ? $this->calculateDeclaredValueFee($declaredValue) : 0,
'total' => round($total, 2),
'currency' => 'BYN',
'min_days' => 3,
'max_days' => 14,
];
}
}
Integration via Corporate API
For clients with a contract, an API is available through the personal account. Authorization uses an API key in the header:
class BelpochtaApiClient
{
private string $baseUrl = 'https://api.belpochta.by/v1';
public function calculateShipping(array $params): array
{
$response = Http::withHeaders([
'Authorization' => 'Bearer ' . config('services.belpochta.api_key'),
'Content-Type' => 'application/json',
])->post($this->baseUrl . '/calc', [
'from_index' => $params['from_index'],
'to_index' => $params['to_index'],
'weight' => (int)($params['weight_kg'] * 1000),
'length' => $params['length'] ?? 0,
'width' => $params['width'] ?? 0,
'height' => $params['height'] ?? 0,
'service_type'=> $params['service_type'] ?? 'PARCEL',
]);
return $response->json();
}
public function createOrder(array $orderData): array
{
$response = Http::withHeaders([
'Authorization' => 'Bearer ' . config('services.belpochta.api_key'),
])->post($this->baseUrl . '/orders', $orderData);
if ($response->failed()) {
throw new BelpochtaException('Order creation failed: ' . $response->body());
}
return $response->json();
}
}
Postal Codes and Addresses
Belarusian postal codes are 6-digit, starting with 2. Minsk codes range accordingly. Validation and city lookup:
public function validateBelarusPostalCode(string $code): bool
{
return (bool)preg_match('/^2[0-9]{5}$/', $code);
}
public function getCityByIndex(string $postalCode): ?string
{
return Cache::remember("belpochta_city_{$postalCode}", now()->addWeek(), function () use ($postalCode) {
$response = Http::get('https://api.belpochta.by/v1/address/by-index', [
'index' => $postalCode,
]);
return $response->json('city');
});
}
EMS and Tracking
For urgent shipments — EMS Belpochta. Tracking via public tracking or API:
public function trackParcel(string $trackNumber): array
{
$response = Http::withHeaders([
'Authorization' => 'Bearer ' . config('services.belpochta.api_key'),
])->get($this->baseUrl . '/tracking/' . $trackNumber);
if ($response->notFound()) {
return ['error' => 'Tracking number not found'];
}
return collect($response->json('events') ?? [])
->map(fn($e) => [
'date' => $e['date'],
'time' => $e['time'],
'status' => $e['operation'],
'place' => $e['place'],
'index' => $e['index'],
])
->toArray();
}
Currency Conversion
If the store operates in another currency, we use the National Bank of Belarus exchange rate (free API):
public function convertToDisplayCurrency(float $byn, string $targetCurrency = 'RUB'): float
{
$rate = Cache::remember("exchange_rate_BYN_{$targetCurrency}", now()->addHour(), function () use ($targetCurrency) {
$response = Http::get('https://api.nbrb.by/exrates/rates/' . $targetCurrency, [
'periodicity' => 0,
]);
return $response->json('Cur_OfficialRate');
});
return round($byn * $rate, 2);
}
How to Choose Between Tables and API?
For stores with a small number of orders, the table method is justified: fast, cheap, no contract required. If volumes grow or automation is needed, the corporate API pays off by reducing manual work and errors. Through automation, clients achieve significant savings compared to manual calculations. The API processes requests much faster than manual data entry. Get a consultation—we'll help you choose the right option.
Our Process
- Analytics — we examine your current delivery logic, CMS, and volumes.
- Design — we select the method (tables or API) and agree on the schema.
- Development — we write the integration module and test on a test branch.
- Testing — we verify calculations, order creation, and tracking.
- Deployment — we launch on the production server and train managers.
- Support — we update tariffs when changes occur and fix bugs.
Estimated Timelines
- Calculator using tariff tables: from 2 to 3 days.
- Full integration with API (including a contract with Belpochta): from 5 to 7 days. The investment is determined individually after an audit. If you want to eliminate calculation errors, get a consultation. We'll find the optimal solution for your store.
Common Integration Mistakes
- Incorrect weight category — customer enters a certain weight, but code rounds up. Use exact comparison
<=. - Ignoring dimensions — Belpochta uses volumetric weight for large boxes. The table method does not account for this; you must specify explicitly.
- Outdated tariffs — tables must be updated with every change. We subscribe to change notifications.
- Lack of currency rate caching — querying the National Bank on every calculation slows the page. Use caching with a reasonable TTL.
Integration Verification Checklist
- Weight validation: exact comparison ≤, not <.
- Volumetric weight for boxes (length × width × height / 5000).
- Currency rate caching (National Bank RB, appropriate TTL).
- API error handling: timeouts, unavailability, invalid indexes.
- Logging of all requests for auditing.
We guarantee calculation accuracy and complete documentation. We have years of experience and over 50 completed projects with postal services in the CIS.







