Talabat is Delivery Hero's brand in the Middle East and North Africa, covering food delivery, grocery and quick commerce across the Gulf, the Levant and Egypt. It matters to a merchant because in most of those markets it is the delivery platform, not one of several. Unlike the Indian marketplaces in this reference, Talabat has real public API documentation, and it has two of them: a Partner API at developer.talabat.com for retail and quick commerce merchants managing a catalogue and receiving orders, and Delivery Hero's POS integration middleware at integration.talabat.com for restaurant point of sale vendors. They are different products with different audiences and different endpoints, and picking the wrong one is the most common way to waste a week here.
At a glance
What it is
Talabat is a delivery platform, so the connector's job is different from a marketplace's. There is no shipment you create and no carrier you choose: the platform either sends a rider, or the vendor delivers themselves. There are no returns in the retail sense. And the most time critical write is not stock, it is whether the shop is open.
Delivery Hero's own quick commerce documentation lists Talabat across Bahrain, Egypt, Jordan, Kuwait, Oman, Qatar and the United Arab Emirates. Other markets exist commercially and may be served by the same platform, so confirm the country list with an account manager rather than from this page.
The vocabulary from the Partner API's own glossary:
- Vendor: the store, outlet or merchant listed on the platform, responsible for fulfilling orders and managing its catalogue. Identified by a short
vendor_idsuch asnaez. - Chain: a group of stores under a common brand, franchise or region, identified by a UUID
chain_id. One API key manages every outlet in that group, which is the key multi location fact. - Job: a background process for time consuming work such as product assignment or catalogue export.
- SKU and GTIN: the merchant's code and the global barcode.
Two fulfilment modes appear as transport_type. LOGISTICS_DELIVERY means a Talabat rider collects, so the vendor's terminal state is READY_FOR_PICKUP. VENDOR_DELIVERY means the vendor delivers, so the terminal state is DISPATCHED. This single field changes which status transitions are legal, so read it before writing anything.
API access
For the Partner API, the documented route is to contact an account manager or use the Partner Portal to request API keys. Client credentials are generated in the Partner Portal under the Shop Integrations Plugin section of All Settings. Sandbox credentials are separate and can be created through Partner Portal self service.
Hosts:
The version in force is v2.0.2, with /v2 in every path and a downloadable OpenAPI specification on the reference page. Some endpoints are explicitly marked early access: Add Products and Product Addition Status both carry a beta banner saying they may change without notice and should not be used in production without being enabled as a pilot partner. Add Products can also return 501 Not Implemented when the flow is not yet supported in a given market.
For the POS middleware, the audience is point of sale vendors rather than merchants. Delivery Hero splits it in two, and the split is the whole architecture:
- Integration Middleware API: endpoints called by POS plugins, maintained by Delivery Hero.
- Plugin API: endpoints implemented by the POS partner, so that platforms can push orders and updates into them.
That second half means a POS integration is not only a client, it is also a server. Access requires going through Delivery Hero's onboarding, configuring the integration, and getting IP addresses allowlisted, and the documentation includes a post production agreement step.
There is no official SDK for either surface.
Choose the surface by merchant type, not by preference. A grocery, pharmacy or retail merchant managing a product catalogue with prices and barcodes belongs on the Partner API. A restaurant with a point of sale system and a menu belongs on the POS middleware, and in that case the integration is usually built by the POS vendor rather than by the restaurant. The two share no endpoints.
Authentication
Partner API
OAuth 2.0 client credentials, form encoded.
curl -X POST 'https://talabat.partner.deliveryhero.io/v2/oauth/token' \ -H 'Content-Type: application/x-www-form-urlencoded' \ -d 'grant_type=client_credentials' \ -d 'client_id=<client-id>' \ -d 'client_secret=<client-secret>'
{
"access_token": "eyJhbGciOiJQUzI1NiIsInR5cCI6IkpXVCJ9eyJ1dWlkIjoiVEVTVCJ9",
"token_type": "Bearer",
"expires_in": 7200
}
Then send it on every call:
curl 'https://talabat.partner.deliveryhero.io/v2/chains/550e8400-e29b-41d4-a716-446655440000/vendors/naez/catalog?page_size=100' \ -H 'Authorization: Bearer <access_token>'
grant_type currently supports only client_credentials. The token is a short lived JWT, and the documentation is emphatic about caching it: use the expires_in value, 7200 seconds in the published sample, as the cache TTL rather than minting a token per request. The token endpoint is rate limited to 50 requests per minute per client id, which is a hard enough ceiling that an uncached client will start failing with 429 under any real load. Missing or invalid authentication returns 401.
There is no refresh token, because there does not need to be: re-run the client credentials call.
Multi account handling is by chain_id in the path. One credential pair covers every outlet in that group, and each call names the chain and, where relevant, the vendor. Sandbox credentials do not work in production and production credentials do not work in the sandbox.
Webhooks
Webhook notifications use a different scheme, named WebhookKeyAuth in the specification. The documentation states that a static token for webhook notifications can be added through the Vendor Portal. So the inbound direction is a shared static token you configure, not a signature you verify.
Objects we can read
Orders
Orders arrive by webhook and can be read back two ways.
GET /v2/chains/{chain_id}/orders/{order_id}
GET /v2/chains/{chain_id}/vendors/{vendor_id}/orders
The list call is explicitly positioned for reconciliation and analysis rather than for live operation. Filters are start_time and end_time, both ISO 8601, with a maximum of 60 days in the past, defaulting to today from midnight to now. Pagination is page and page_size, the latter bounded 1 to 500 with a default of 20. Two older parameters, start_created_at_datetime and end_created_at_datetime, are marked deprecated in favour of start_time and end_time.
Order fields, from the notification schema:
Three identifiers for one order is a design you have to respect: order_id is what you call the API with, external_order_id is the platform's number, and order_code is what a human rider reads off a bag.
Order items
Items live on the order. On the write side, an item is addressed by item.id or item.sku, and the documentation states that if both are supplied, item.id takes precedence over item.sku. That precedence rule is worth encoding rather than discovering.
Products and listings
GET /v2/chains/{chain_id}/vendors/{vendor_id}/catalog
PUT /v2/chains/{chain_id}/vendors/{vendor_id}/catalog
POST /v2/chains/{chain_id}/catalog
GET /v2/chains/{chain_id}/catalog/jobs/{job_id}
POST /v2/chains/{chain_id}/vendors/{vendor_id}/catalog/export
GET /v2/chains/{chain_id}/vendors/{vendor_id}/categories
The read takes query_term for a text search across title, SKU and barcode, locale such as en_GB to choose which language the search runs against, category_global_ids as a comma separated list of category UUIDs, is_active to filter active or inactive products, and page and page_size bounded 1 to 500 with a default of 20.
The product shape, from the official request sample:
{
"vendors": ["urd8"],
"products": [
{
"sku": "3671",
"title": { "en_SE": "Canned / Jarred / Instant Meals" },
"description": { "en_SE": "Organic Vegetable Broth, 300 g." },
"barcodes": ["6281100875093"],
"images": ["https://images.deliveryhero.io/image.jpg"],
"categories": ["18cad219-add0-459e-8bcf-6d6a48f57bea"],
"price": 39.75,
"is_sold_by_weight": true,
"weight_specifications": {
"base_weight": 3,
"base_weight_unit": "KG",
"average_weight_per_piece": 1,
"average_weight_per_piece_unit": "KG",
"minimum_starting_weight": 0.5,
"minimum_starting_weight_unit": "KG"
}
}
]
}
Titles and descriptions are locale keyed maps, not strings, which is a genuine difference from most marketplace APIs and needs modelling up front for bilingual Arabic and English markets. is_sold_by_weight with weight_specifications exists because grocery sells by weight, and a loose produce SKU behaves differently from a packaged one all the way through pricing and picking.
vendors: ["*"] assigns the products to every outlet in the chain.
Catalogue export is asynchronous: request it, then collect the result.
Inventory
There is no stock quantity object in the Partner API as published. What exists is is_active on a product, and on the POS middleware side an explicit Update Item Availability call with a matching Unavailable item read. Availability, not quantity, is the model. If you need true quantity tracking, it lives in your own system and you express it to Talabat as availability.
Shipments and tracking
Not applicable in the marketplace sense. The delivery is Talabat's when transport_type is LOGISTICS_DELIVERY and the vendor's when it is VENDOR_DELIVERY. There is no AWB, no carrier selection and no tracking event feed on the Partner API.
Returns and cancellations
There are no returns. Cancellation is a status with a required reason, carried in the cancellation object on the order and written through the update call.
Payments and settlements
No settlement, payout or commission endpoint appears in the Partner API. Reconciliation has to come from portal reports.
Customers
No customer object is exposed in the Partner API reference read here. Delivery address handling sits with the platform, since the platform arranges delivery in the LOGISTICS_DELIVERY case.
Locations and outlet status
GET /v2/chains/{chain_id}/vendors/{vendor_id}/status
PUT /v2/chains/{chain_id}/vendors/{vendor_id}/status
Outlet status is arguably the most operationally important endpoint on the platform, because a shop that is open and cannot fulfil loses orders and rating. Supported statuses:
CLOSED_TODAY, closed until the end of the dayCLOSED_UNTIL, closed until the date inclosed_untilOPEN, reopen a closed vendor if it is within scheduled opening hoursCHECKIN, acknowledge a required check in
The check in feature is off by default and enabled by an account manager. When on, the vendor confirms an upcoming opening up to 30 minutes before the scheduled hour, and if the partner fails to acknowledge, the shop stays closed even though the schedule says open. A connector that does not implement check in for a vendor who has it enabled will silently keep that shop shut. The shop can still be opened by calling the API with OPEN.
Writing back: listings, price and stock
Products and price. PUT /v2/chains/{chain_id}/vendors/{vendor_id}/catalog updates products for one outlet, and POST /v2/chains/{chain_id}/catalog adds products across many outlets at once. Price is a field on the product, so a price change is a catalogue update rather than a separate pricing call.
The add flow is asynchronous and returns a job:
{
"job_id": "a946a2c7-f4e7-46ac-ae63-8a5497cb0ad9",
"job_status": "QUEUED",
"duplicated_products": ["vendor1:11111"]
}
Results arrive either by polling GET /v2/chains/{chain_id}/catalog/jobs/{job_id} or by the add products results callback, which carries per item feedback:
{
"job_id": "a946a2c7-f4e7-46ac-ae63-8a5497cb0ad9",
"job_status": "IN_PROGRESS",
"created_at": "2024-09-30T10:00:36.947Z",
"updated_at": "2024-09-30T10:05:36.947Z",
"result": {
"duplicated_products": ["vendor1:11111"],
"item_level_feedback": [
{
"sku": "3451",
"barcodes": ["9783193610843"],
"vendor_id": "naez",
"status": "Product creation failed",
"rejection_reasons": [
{ "rejected_data": "images", "rejected_reason": "poor quality" }
]
}
]
}
}
rejection_reasons naming the offending field is more useful than most marketplaces manage, and a connector should surface it verbatim rather than collapsing it to "failed".
Order status. PUT /v2/chains/{chain_id}/orders/{order_id} is the main operational write. The status enum is CANCELLED, DISPATCHED, READY_FOR_PICKUP and UPDATE_CART, and the legal transitions depend on transport_type:
- For
VENDOR_DELIVERY:CANCELLEDorDISPATCHED. Once dispatched, the order cannot be cancelled, and the reverse also holds. - For
LOGISTICS_DELIVERY:CANCELLEDorREADY_FOR_PICKUP. - Cancelling requires a cancellation reason.
UPDATE_CART is how items are modified, for example when a grocery substitution or a short pick changes what is actually in the bag.
Promotions. PUT /v2/chains/{chain_id}/promotion manages promotions and GET /v2/chains/{chain_id}/promotion/jobs/{job_id} reports the outcome, so promotions are asynchronous too.
Outlet hours. The status call above.
Webhooks and notifications
Partner API
Order notifications are POSTed to a URL you develop and register, either with the platform or in the Partner Portal. The documentation is specific that the endpoint must handle the whole order lifecycle, not just creation: status changes and cancellations arrive on the same hook.
The statuses you receive depend on the picking device configured for the vendor:
RECEIVED is described as the initial status, meaning processing has not started at the partner's end.
Catalogue jobs have their own callback, the add products results webhook described above.
The inbound and outbound spellings of cancellation differ in Talabat's own documentation. The order notification webhook lists CANCELED with one L, while the Update Order status enum lists CANCELLED with two. Treat them as distinct string constants in code, one for reading and one for writing, and do not normalise one into the other without testing against the sandbox.
Authentication of inbound calls is the static WebhookKeyAuth token set in the Vendor Portal. No HMAC signature scheme is published, so also restrict the receiver by source and treat the token as a secret with a rotation plan.
No retry policy or ordering guarantee is published. Be idempotent on order_id and use updated_at to discard stale updates.
POS middleware
The restaurant side has its own set, split by who implements it.
Called by the POS plugin, maintained by Delivery Hero:
Implemented by the POS partner, called by Delivery Hero:
Note the 24 hour bound on the order identifier report: the POS middleware's reporting service is a short window safety net, not a history API. The Partner API's 60 day vendor order history is the one with reach.
Rate limits and pagination
The only published number is the token endpoint's 50 requests per minute per client id, returning 429 on breach. No per endpoint limits, quota headers or burst policy are published for the business endpoints, which makes token caching the single most important throttling behaviour to get right.
Pagination is page and size, with page_size bounded 1 to 500 and defaulting to 20 on both the catalogue read and the vendor order history. The order history is further bounded to 60 days in the past.
A workable cadence: take orders from the webhook, poll GET /v2/chains/{chain_id}/vendors/{vendor_id}/orders once an hour over an overlapping window purely as reconciliation, push outlet status changes immediately since they are time critical, submit catalogue updates in batches and follow the job, and cache the OAuth token for its full expires_in.
Mapping to the unified model
Gaps and open questions
- No settlement or commission API on either surface. Reconciling revenue means portal reports.
- No stock quantity model. Availability is boolean. If the business needs quantity driven availability, the logic lives in your system and you push a flag.
- Add Products and Product Addition Status are beta and explicitly not for production without pilot enrolment, and Add Products can return
501 Not Implementedby market. Confirm availability in each country before designing around it. - The exact country list for the Partner API is not stated on the reference page. Delivery Hero's quick commerce documentation lists Bahrain, Egypt, Jordan, Kuwait, Oman, Qatar and the United Arab Emirates for Talabat, which may not be the full commercial footprint.
- Webhook authentication is a static token with no published signature scheme, no retry policy and no ordering guarantee.
- The
CANCELEDandCANCELLEDspelling split between the inbound and outbound vocabularies is in the official documentation and is a live source of bugs. - Rate limits are published only for the token endpoint. Ask for the business endpoint limits before a bulk catalogue load.
- The relationship between the Partner API and the POS middleware is not documented anywhere read here. Whether a merchant can be on both, and what happens if both write to the same vendor, is unresolved.
- The POS middleware reference at
integration-middleware.stg.restaurant-partners.com/apidocs/pos-middleware-apiis a staging host. The production equivalent was not confirmed.
Sources
- Talabat Partner API v2.0.2 reference, official, source of the hosts, the OAuth flow and its 50 per minute limit, the sandbox description, every
/v2path quoted here, the product and job samples, the order notification schema, the status enums and theirtransport_typerules, the outlet statuses and the check in behaviour - Talabat POS Integration documentation, official, source of the Integration Middleware API and Plugin API split and the full POS endpoint list including the 24 hour order identifier bound
- Talabat POS Integration: how it works, official
- Delivery Hero Restaurant Integration API, official, background on the middleware architecture
- Delivery Hero Q-Commerce Partner API, official, source of the Talabat country list
- Integration Middleware Plugins API, staging, official, the middleware reference host