API overview: the merchant and company APIs
The platform's two public APIs: the merchant API at /api/v1 (sk_ key) and the company API at /api/company/v1 (ck_ key), who can use them, how versions work and the JSON envelope every response uses.
Last updated 22 October 2026 · Last verified 22 October 2026
Purpose#
The platform has two public REST APIs for connecting your own systems (an ERP, an app, a custom shop). Both exchange JSON, identify you with a Bearer API key and answer in the same envelope. Endpoint details are in the Merchant API reference and the Company API reference.
The two public APIs#
| Merchant API | Company API | |
|---|---|---|
| Base path | /api/v1 |
/api/company/v1 |
| Who it is for | A merchant: price, create, list, track and cancel parcels, read the statement, manage webhook endpoints | A delivery company: read its merchants and couriers, list and create parcels, assign a courier, update a parcel's status |
| Key prefix | sk_ |
ck_ |
| Where you get the key | Merchant panel: Integration, then Webhook and API | Company panel: Integrations and API |
The full address is your platform's domain plus the base path. Each of those pages has an API documentation button, and the panel shows the exact Base URL (at the bottom of the API key card for companies, in the Quick start section of the documentation page for merchants). Other endpoints under /api are not documented here: the mobile app API and store webhooks are internal, and a key-based endpoint (POST /api/parcel/create-with-security, with X-Platform) serves store and custom platforms. Use /api/v1 or /api/company/v1 for new integrations.
Who can use it#
- Merchant API: platform merchants. A merchant who belongs to a delivery company cannot use it: the panel page shows a notice and returns to the dashboard, and the API answers
403withapi_not_allowed. A merchant who owns an internal delivery company is still a platform merchant. - Company API: the company admin creates the key. The company needs an active subscription and a plan that includes the API feature; platform-owned and merchant-owned companies are exempt from both.
- Plans: when merchant plans are in use, the merchant needs an active plan with the API feature. A new merchant plan starts with the API off until the super admin turns it on; a new company plan starts with it on; an old plan with unconfigured features allows everything.
How versions work#
- The version is part of the path. Both APIs are at
v1; there is no version header and no other version. - The merchant in-panel documentation says new fields can be added without notice (ignore fields you do not know) and a breaking change would be a new version; the company documentation only says new fields may be added and unknown ones ignored.
- Build your logic on the parcel
stage(a simplified, fixed set of states). Usestatus_codeandstatus_labelfor the exact status and for display, and branch onerror.coderather than onerror.message.
The response envelope#
Every response from these APIs uses one of two shapes.
{ "success": true, "data": { "id": 1 }, "meta": { "page": 1, "per_page": 20, "total": 132, "last_page": 7 } }
{ "success": false, "error": { "code": "validation_failed", "message": "...", "fields": { "customer_phone": ["..."] } } }
metaappears only on paginated lists (page,per_page,total,last_page): merchantGET /parcelsandGET /statement, companyGET /merchantsandGET /parcels. Other lists are plain arrays indata. Nolinks.error.codeis a short snake_case code: program against it, not againsterror.message. The message is Arabic by default and English withAccept-Language: en.fieldsappears on most validation errors (a few company API ones carry onlymessage),retry_afteronrate_limited,referenceon server errors.- Timestamps on parcels, events, statement lines and webhook endpoints are ISO 8601 with a UTC offset. The plan expiry in
GET /meis a plain string:YYYY-MM-DDfor merchants andYYYY-MM-DD HH:MM:SSfor companies. Money amounts inside parcel objects are in US dollars. - Always send
Accept: application/json. Errors raised before the API code runs (unknown path, wrong HTTP method, the general and route request limits) use a different format,{"message": "..."}, or an HTML page without the header.
Quick start#
Confirm that your plan includes the API
Merchants: the "Plan and subscription" page lists each plan's features, including the API entry and its limits. Companies: the "Company plan" card on the "Integrations and API" page lists your plan's features.
Generate an API key
Follow Authentication and API keys.
Call the account endpoint
GET /mereturns your account details and proves that the key works. For the company API use/api/company/v1/me.curl https://YOUR_DOMAIN/api/v1/me \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Accept: application/json"Create and follow parcels
See the two API references.
- Your current API keys (shown masked)
- The name of the platform or system you connect
- Generate a new key
- Open the API documentation
Common mistakes#
- Wrong key type. A
ck_key on/api/v1, or ask_key created for a store platform, gives401andinvalid_key. - Key outside the header. A key in the query string or body is ignored:
401andunauthenticated. - Planning around 120 or 300 requests per minute. The general 60 per minute per IP address applies too.
- Putting the key in a mobile app or browser code. Keep it on your server.
Limits and permissions#
- Rate limits. Three layers apply: a general limit of 60 requests per minute per IP address on every
/apirequest; route limits (merchant 120 per minute, 60 forPOST /parcels, 30 forGET /statement; company 300 per minute, 120 forPOST /parcels), counted per IP address on one shared counter, so aPOST /parcelsrequest counts against both its group limit and its own and these are not separate quotas (because each one is counted twice, one IP address can in practice create only about 30 parcels per minute with the merchant API and about 60 with the company API, and fewer if it also makes other calls in the same minute); and plan limits per account (per minute and per day). Plan limits return429withrate_limited,retry_afterin seconds and aRetry-Afterheader; the general and route limits return429with{"message": "Too Many Attempts."}. Plan for at most 60 requests per minute per IP address. - Webhooks. Notifications to your system are configured separately, with the merchant API's
/webhooksendpoints or in the panel; the company API has no webhook endpoints (use the panel).
API
GET /api/v1/meGET /api/company/v1/me