Ekart Logistics is Flipkart's in house supply chain arm, and the largest captive logistics network in India. It moves the overwhelming majority of Flipkart's own parcels, and it also sells capacity to third party sellers and aggregators as a standalone carrier. For a commerce database it is a carrier connector: no orders, no listings, no stock. What it gives us is a tracking id and a label when we book, a tracking history, a shipment update mechanism that doubles as the NDR action path, and return to origin and reverse pickup flows. Ekart publishes nothing about its API. The API itself, however, is live, self describing at the edge and can be mapped precisely by probing, which is what this page does.
At a glance
HTTP_X_MERCHANT_CODE is a real header name, not a transcription error. It looks like a PHP $_SERVER superglobal key (HTTP_ prefix plus the uppercased header name) that was adopted as the literal header, which is a fingerprint of an older PHP gateway somewhere in Ekart's stack. Send it exactly as written.
What it is
Ekart was founded in 2009 as Flipkart's internal delivery arm, at a point when no Indian third party carrier could reliably do cash on delivery at e-commerce scale. It grew into the country's largest logistics network by parcel volume, with its own sortation hubs, line haul, last mile fleet and a large kirana and franchise based delivery layer. It is owned by Flipkart, which is majority owned by Walmart.
Two things follow for anyone building a connector. First, Ekart is not a neutral carrier: its first duty is Flipkart's own volume, and third party capacity is sold selectively. A D2C brand does not simply sign up. Second, most sellers meet Ekart through an intermediary rather than directly, either as the carrier assigned to a Flipkart marketplace order under Flipkart Advantage, or as a courier option inside an aggregator. Shiprocket exposes it as courier_company_id 48 (Ekart Logistics) and 54 (Ekart Logistics Surface), for example. In both of those cases the connector you want is the Flipkart one or the aggregator one, not this one.
A direct Ekart integration means a contracted third party shipper relationship with a merchant code.
API access
There is no console, no documentation portal and no signup. Access comes from a commercial conversation with Ekart, after which you receive a three character merchant code, an API credential and a documentation pack.
Hosts, verified live on 2026-09-22:
The API surface, established by probing each path with each verb. The gateway is precise: an unknown path returns 404, a known path with the wrong verb returns 405, and a known path with the right verb but no credential returns 401. That distinction is what makes the table below verifiable rather than guessed.
Two things that surface immediately from that map. There is no serviceability or pincode endpoint, so a pre booking check against Ekart is not possible over this API; you need a serviceability file from Ekart or you rely on the creation call failing. And there is no pickup endpoint, so pickups are a standing arrangement rather than a per shipment request.
There are no official SDKs, and no published version policy. The presence of /v3/shipments/track alongside /v2/shipments/track, with no /v3 equivalent for creation, suggests tracking was versioned independently.
Authentication
Two headers, no exchange step and nothing to refresh.
Authorization: <token>. Note: the production integrations send the token bare, with noBearerprefix. The token's origin is not publicly documented; it is issued by Ekart rather than obtained from a login endpoint, and no login path exists anywhere on the API surface probed.HTTP_X_MERCHANT_CODE: <three-character-merchant-code>.Content-Type: application/json.
curl -X POST 'https://api.ekartlogistics.com/v2/shipments/track' \
-H 'Content-Type: application/json' \
-H 'Authorization: <ekart-token>' \
-H 'HTTP_X_MERCHANT_CODE: ABC' \
-d '{"request_details": [{"tracking_id": "ABCC1234567890"}]}'
Anything missing or wrong returns HTTP 401 with {"unauthorised":"invalid_user_credential"}, verified live. Supplying HTTP Basic credentials or a merchant code without a valid token produces the same body, so the error message does not distinguish which of the two headers is at fault.
Multi account is one merchant code per Ekart shipper. Because the merchant code is also the prefix of every tracking id (see below), a platform shipping for several brands under one Ekart contract cannot separate them by tracking id alone.
Objects we can read
Shipments and tracking
POST /v2/shipments/track, with /v3/shipments/track also deployed. The request carries a request_details array of tracking_id values, matching the shape used by the other operations on this API.
There is no pagination, no date window and no "changed since" filter: a lookup by key. The response shape could not be verified without credentials. What can be established from a production integration is that Ekart responses are envelopes with a response array positionally parallel to the request array, each element carrying at least a tracking_id and a status_code, where status_code: 200 means the operation succeeded for that element. Expect tracking to follow the same pattern.
No proof of delivery artefact is exposed by any endpoint on the probed surface.
Orders, listings, inventory, serviceability, returns, settlements
None. The API surface is shipments only. Reverse flows exist as operations rather than as readable objects. COD remittance and freight invoicing are handled outside this API.
Writing back: listings, price and stock
Not applicable, this is a carrier. There are no listings, no prices and no stock. The write path is the shipment lifecycle, and it is the richest part of this API.
Create a shipment
POST /v2/shipments/create. The body is deeply nested, unlike every other Indian carrier in this set, and is organised around services and service legs rather than around a flat shipment:
{
"client_name": "ABC",
"services": [
{
"service_code": "Regular",
"service_details": [
{
"service_leg": "FORWARD",
"service_data": {
"vendor_name": "Ekart",
"delivery_type": "SMALL",
"amount_to_collect": 999,
"source": {
"address": {
"first_name": "Main Warehouse",
"address_line1": "Plot 7, Industrial Area",
"city": "Delhi", "state": "Delhi", "pincode": "110030",
"primary_contact_number": "9811111111",
"email_id": "warehouse@example.com"
}
},
"destination": {
"address": {
"first_name": "Ramesh Kumar",
"address_line1": "12, MG Road",
"city": "Pune", "state": "Maharashtra", "pincode": "411001",
"primary_contact_number": "9876543210"
}
},
"return_location": {
"address": {
"first_name": "Main Warehouse",
"address_line1": "Plot 7, Industrial Area",
"city": "Delhi", "state": "Delhi", "pincode": "110030",
"primary_contact_number": "9811111111",
"email_id": "warehouse@example.com"
}
}
},
"shipment": {
"tracking_id": "ABCC1234567890",
"shipment_value": 999,
"shipment_dimensions": {
"length": { "value": 20 },
"breadth": { "value": 15 },
"height": { "value": 5 },
"weight": { "value": 0.5 }
},
"shipment_items": [
{
"quantity": 1,
"seller_details": { "seller_reg_name": "Example Brand" },
"item_attributes": [
{ "name": "order_id", "value": "ORD-10231" },
{ "name": "invoice_id", "value": "INV-10231" }
]
}
]
}
}
]
}
]
}
The single most important thing on this page: you generate the tracking id yourself. Ekart does not assign it. The format observed in a production integration is the three character merchant code, then a single character C for cash on delivery or P for prepaid, then ten digits. So a merchant code of ABC on a COD shipment produces a tracking id like ABCC1234567890. That has real consequences: uniqueness is your problem, the payment mode is baked into the AWB and cannot be changed after the fact, and you must never reuse an id.
Other notes:
client_namerepeats the merchant code that is already in the header.amount_to_collectis the COD amount, zero for prepaid.delivery_typetakes size band values such asSMALL.service_legisFORWARDhere; reverse flows use the dedicated endpoints below rather than a different leg on this one.item_attributesis a free form name and value list, and it is the only place to record your own order id and invoice number. Nothing in the request has a dedicated field for them.- Dimensions are wrapped one level deeper than necessary, as
{"value": n}objects. Units are not published; the production integration passes centimetres for dimensions and kilograms for weight.
The response carries response[0].tracking_id, echoing back the id you supplied, and a status_code.
Update a shipment, including NDR actions
PUT /v2/shipments/update_shipment is the workhorse. There is no separate NDR endpoint on this API: failed delivery actions are expressed as shipment updates.
{
"update_request_type": "RESCHEDULE_DELIVERY_DATE",
"update_request_details": { "updated_delivery_date": "2026-09-23" },
"tracking_id": "ABCC1234567890"
}
The update_request_type values observed in a production integration:
A practical NDR re-attempt is therefore two calls: correct the address or phone with CUSTOMER_DETAILS or CUSTOMER_CONTACT, then reschedule with RESCHEDULE_DELIVERY_DATE. There is no "re-attempt as instructed" action and no deferred delivery slot.
PUT /v2/shipments/update also exists on the probed surface and is distinct from update_shipment. Its purpose is unverified.
Return to origin
PUT /v2/shipments/rto/create with {"request_details": [{"tracking_id": "ABCC1234567890"}]}. Batched: request_details is an array. The response is a parallel response array where status_code: 200 per element means the RTO was accepted.
This is also how you cancel: there is no /v2/shipments/cancel path (it returns 404), so sending a shipment back is the cancellation mechanism once it is in the network.
Reverse pickup cancellation
PUT /v2/shipments/rvp/cancel with the same request_details array shape. Note that rvp/create returns 404, so reverse pickups are presumably created through /v2/shipments/create with a different service_leg or service_code. That mapping is unverified.
The verbs matter and the public integration is inconsistent with the live gateway. A production integration POSTs to /v2/shipments/rto/create and /v2/shipments/update_shipment in one place and PUTs to them in another. Probing on 2026-09-22 shows POST returning 405 and PUT returning 401 on both, so PUT is the verb the gateway accepts. Use PUT for update, update_shipment, rto/create and rvp/cancel, and POST for create and track.
Webhooks and notifications
Not published, and there is no subscription endpoint anywhere on the probed surface. Ekart does push status to large partners under separate arrangements; that is an account conversation.
Poll POST /v2/shipments/track. Because the request takes a request_details array, batch your tracking ids rather than calling per shipment. With no published rate limit, poll in flight shipments every 60 to 120 minutes in batches, and stop at a terminal status.
Rate limits and pagination
Neither is published, and neither could be measured without credentials.
- Pagination: none. Tracking, RTO and reverse pickup cancellation all take arrays and return parallel arrays; there is no cursor and no page.
- Rate limits: not published, and no quota headers were observed on the 401 responses. Implement your own limiter and back off on any non-200.
One structural note on batching: because responses are positionally parallel to requests, a partial response or a reordering would silently mis-attribute results. Match on tracking_id in the response element, not on array index.
Mapping to the unified model
Ekart contributes shipments only.
ship_to_address maps to service_data.destination.address, which is PII. locations maps to service_data.source.address and return_location.address, both sent per shipment rather than registered once, so there is no warehouse registry to read. The payment method is recoverable from the tracking id's fourth character, which is the only place it survives.
Gaps and open questions
- No public documentation at all. Ekart publishes nothing. This page is a map of the live gateway plus one production integration's request bodies.
- Every response shape is unverified. Tracking in particular: the field names for status and scan history are unknown. Budget a discovery round against a live tracking id.
- The token's origin is unknown. No login or token endpoint exists on the probed surface, so the
Authorizationvalue is presumably a long lived credential issued by Ekart. Its lifetime and rotation procedure are unpublished. - No serviceability, no rates, no pickup. All three are absent from the API. Serviceability has to come from a file, pricing from your contract, and pickups from a standing arrangement. This is the most operationally significant gap.
- Reverse pickup creation is unmapped.
rvp/cancelexists butrvp/createreturns 404, so reverse shipments are created some other way, presumably throughcreatewith a differentservice_leg. Unverified. /v2/shipments/updateversus/v2/shipments/update_shipment. Both exist, both take PUT, and only the second appears in any public integration. The difference is unknown./v3/shipments/trackexists with no/v3sibling for creation. Whether v3 is a drop in replacement, and whether v2 tracking is deprecated, is unpublished.- Tracking id generation is the caller's responsibility, with no published format specification and no collision check documented. The merchant code plus payment character plus ten digit format comes from one integration. Confirm your allocated range with Ekart before generating anything, and treat a duplicate id as a serious defect.
- No published rate limits and no quota headers.
- Units unpublished for dimensions and weight.
Sources
Verified live on 2026-09-22 by direct request against https://api.ekartlogistics.com:
POST /v2/shipments/create,POST /v2/shipments/track,POST /v3/shipments/track,PUT /v2/shipments/update,PUT /v2/shipments/update_shipment,PUT /v2/shipments/rto/create,PUT /v2/shipments/rvp/cancel: all return HTTP 401{"unauthorised":"invalid_user_credential"}GET /v2/shipments/track,GETandPOSTon the four PUT paths,PATCHandDELETEon/v2/shipments/update: HTTP 405{"code":405,"message":"HTTP 405 Method Not Allowed"}/v2/shipments,/v2/shipments/cancel,/v2/shipments/label,/v2/shipments/status,/v2/shipments/reverse,/v2/shipments/rvp/create,/v2/shipments/track/bulk,/v2/serviceability,/v2/pincodes,/v2/pincode/serviceability,/v2/pickup/create,/v2/ndr,/v1/shipments/track,/v1/shipments/create,/v3/shipments/create,/v3/shipments/cancel: all HTTP 404{"code":404,"message":"HTTP 404 Not Found"}https://staging.ekartlogistics.com/v2/shipments/track: HTTP 401 with the same envelope, confirming the staging host- Supplying HTTP Basic credentials or an
HTTP_X_MERCHANT_CODEheader without a valid token returns the same 401 body - DNS:
ekartlogistics.comresolves to163.53.77.41,api.ekartlogistics.comto163.53.76.23,staging.ekartlogistics.comto163.53.76.0.developer.ekartlogistics.comdoes not resolve https://ekartlogistics.com/redirects to/ekartlogistics-weband serves an internal application titled "EKL Grocery Dashboards", whose error reporting points atsentry.flipkart.com, confirming Flipkart ownership. It is not a developer or marketing site
Secondary, one production integration read for request bodies:
- Avry123/node-worker,
src/actions/deliveryPartner/ekart.ts: theAuthorizationandHTTP_X_MERCHANT_CODEheaders, the tracking id generation format, the full/v2/shipments/createbody withservices,service_details,service_leg,service_data,shipmentandshipment_items, theupdate_shipmentrequest with its threeupdate_request_typevalues, therto/createandrvp/cancelrequest_detailsarray shape, and theresponse[].tracking_idandresponse[].status_codeenvelope