How quoting works
A quote asks every courier available to your API key to price one shipment, and gives you back a draft booking with all the prices on it. You then confirm the draft with the courier you choose.
The one request you need is:
POST https://api.transdirect.com.au/api/v5/quoteswith your key in the api-key header. See Create a quote for the full reference.
A quote is a draft booking
Section titled “A quote is a draft booking”Every quote request creates a booking with status new and returns its id. That id is the Transdirect booking id you use for every later call, such as confirm, update and labels. It is not your order number or a courier connote.
- Each request makes a new draft, even for the same shipment. Nothing is served from a cache, so
meta.cache.statusisBYPASS. - A draft is never charged and never sent to a courier. Delete drafts you will not use with
DELETE /api/v5/bookings/{id}, or leave them. - Drafts do not appear in List bookings, which skips status
new. Keep theidfrom the quote response. - Quote requests take no courier, payment or pickup time. You choose those when you confirm, so a quote never books or pays for anything.
Which quote endpoint to use
Section titled “Which quote endpoint to use”All four endpoints take the same request body and run the same pricing engine.
| Endpoint | Key | Success | Use it for |
|---|---|---|---|
POST /api/v5/quotes | Required | 201 | Integrations. This is the default. |
POST /api/v5/bookings | Optional | 200 | The same as /quotes when you send a key. Without a key it quotes publicly listed couriers at public prices, limited to 10 requests an hour per IP address. |
POST /api/v5/bookings/stream | Optional | 200, Server-Sent Events | Showing prices as each courier answers. Same limits as POST /api/v5/bookings. |
POST /api/v5/bookings/public | None | 200 | Transdirect’s own public quote form. Publicly listed couriers only, no member pricing, limited to 30 requests a minute and 150 every 5 minutes per IP address. Use /api/v5/quotes in an integration. |
Requests with a key share a limit of 600 a minute per key. Over a limit you get 429 with a retry-after header in seconds.
Call these endpoints from your server. Browsers on other websites cannot call the API directly, because it only allows Transdirect’s own sites as browser origins.
Streaming quotes
Section titled “Streaming quotes”POST /api/v5/bookings/stream answers with Content-Type: text/event-stream. Each event has an event: name and a JSON data: line:
| Event | When | Data |
|---|---|---|
ready | First | requestId, carrierCount (couriers being asked), anonymous |
booking | As soon as the draft is saved | bookingId, the draft booking id |
quote | Once per courier that priced the shipment | carrier (the courier id) and quote, one entry shaped like a value in quotes. These prices are provisional. |
carrier_error | Once per courier that did not | courier, serviceability, retryable, reason |
ping | Every 2 seconds while couriers are still answering | at, a timestamp. Ignore it. |
complete | Last | response, the full quote response, the same body POST /api/v5/bookings returns |
error | If the stream fails | error, message |
Your key’s module settings, such as Quote display and surcharges, are applied after every courier has answered, so a quote event can show a price that changes or disappears in complete. Treat complete as the final list, and take the draft booking id from it or from the booking event.
The stream ignores customer_reference. If you do not need prices to appear one by one, use POST /api/v5/quotes.
What to send
Section titled “What to send”A quote needs a sender, a receiver and at least one item. This body also carries the contact details you need to confirm it later:
{ "sender": { "name": "Sam Taylor", "company_name": "Example Books Pty Ltd", "email": "dispatch@example.com", "phone": "0291234567", "address": "1 Martin Place", "suburb": "SYDNEY", "postcode": "2000", "state": "NSW", "country": "AU", "type": "business" }, "receiver": { "name": "Jordan Lee", "email": "jordan@example.com", "phone": "0398765432", "address": "200 Collins Street", "suburb": "MELBOURNE", "postcode": "3000", "state": "VIC", "country": "AU", "type": "residential" }, "items": [ { "description": "Carton", "weight": 5, "length": 40, "width": 30, "height": 20, "quantity": 1 } ], "goods_description": "Books", "declared_value": 0, "customer_reference": "ORDER-1042"}- Addresses.
postcode,suburb,state,countryandtype(businessorresidential) are checked on both addresses. For Australian addresses the postcode must exist and the suburb must belong to it. Leave outstateand it is filled in from the suburb and postcode. Look up valid combinations with Search locations. - Residential or business.
typechanges which couriers can quote and what they charge, so send what the address really is. - Items.
descriptionis the package type, such asCartonorPallet, not what is inside. Weight is in kilograms and dimensions in centimetres. See Items and packaging. - Goods description. What is inside goes in the top-level
goods_description(ordescription). - Contact and street details (
name,company_name,email,phone,address,address2) are optional for a quote, but confirm needsname,email,phoneandaddresson both addresses, pluscompany_nameon abusinessaddress. Send them in the quote if you have them, or add them later with Update a booking.
Every field is listed in Create a quote.
Transit warranty: declared_value and include_warranty
Section titled “Transit warranty: declared_value and include_warranty”Transit warranty is Transdirect’s cover for loss or damage in transit. Two request fields control it:
| Field | Default | Effect |
|---|---|---|
declared_value | 0 | The value of the goods in AUD. It sets the warranty cover. 0, null or leaving it out means no cover. |
include_warranty | true | Set false to quote without warranty. This also sets declared_value to 0 for the booking. |
include_warranty defaults to true, so a non-zero declared_value on its own adds the warranty fee to every price.
If your key’s module settings have Include warranty on all requests via this module switched on, a request whose declared_value is missing or 0 is quoted with the module’s Transit warranty value instead. So the only sure way to quote without warranty is "include_warranty": false.
In each quote, price_insurance_ex is the price without warranty, fee is the warranty fee and insured_amount is the cover you get. Cover is capped per courier, so insured_amount can be lower than declared_value, and some couriers include cover with no fee. A booking with a declared value over $20,000 is held for a manual check after payment. Read the terms on the transit warranty page.
Reading the response
Section titled “Reading the response”The response is the draft booking. The prices are in quotes, an object keyed by courier id:
{ "id": 623630, "status": "new", "status_code": "new", "quotes": { "aramex": { "total": 12.93, "price_insurance_ex": 12.93, "fee": 0, "applied_gst": 1.18, "insured_amount": 0, "service": "road", "transit_time": "2 Business Days", "transit": { "text": "2 Business Days", "type": "business_days", "min_days": 2, "max_days": 2 }, "pickup_dates": ["2026-10-19", "2026-10-20", "2026-10-21"], "pickup_time": { "from": "09:00", "to": "16:00" }, "rate_card_code": "", "tiers": [ { "identifier": 2, "description": "Multipickup 3 Price (Minimum 3 consignments per collection)", "total": 10.18, "price_insurance_ex": 10.18, "fee": 0, "applied_gst": 0.93, "insured_amount": 0 } ] }, "couriers_please": { "total": 28.43, "service": "road", "transit_time": "1-2 Business Days" } }, "quote_errors": [ { "courier": "allied_pallet", "code": "no_service", "serviceability": "route.no_residential", "retryable": false } ]}This is shortened. Create a quote has a full response.
Prices
Section titled “Prices”| Field | Meaning |
|---|---|
total | What you pay, in AUD, including GST and any transit warranty fee. |
price_insurance_ex | The price without the transit warranty fee. |
fee | The transit warranty fee. 0 when there is no cover. |
applied_gst | The GST included in the price before the warranty fee, price_insurance_ex. |
insured_amount | The transit warranty cover for this courier, in AUD. |
tiers | Frequent-shipper prices, when your account can use them. See Frequent rates and tiers. |
Choosing a courier
Section titled “Choosing a courier”The key of each entry in quotes (aramex, couriers_please, tnt_road_express and so on) is the value you send as courier when you confirm. Use the key from the quote response, not a courier name.
The entries come back cheapest first, but quotes is a JSON object and many JSON libraries do not keep key order. Sort by total yourself before you show a list or pick the cheapest.
If your key’s Quote display setting is Display cheapest or Display cheapest and fastest, the response holds only that one or two quotes.
Transit and pickup
Section titled “Transit and pickup”| Field | Meaning |
|---|---|
service | The courier’s service, for example road. |
transit_time | Estimated delivery time as text, for example "1-2 Business Days". |
transit | The same estimate as data: type (business_days, hours, same_day, overnight, date_time or unknown) with min_days and max_days, or min_hours and max_hours. |
pickup_dates | The pickup dates you can choose at confirm, as YYYY-MM-DD. Today is left out once the courier’s same-day cutoff has passed. |
pickup_time | The courier’s standard pickup window, {"from": "HH:MM", "to": "HH:MM"}. |
pickup_window | The rules behind the window: operatingStart, operatingEnd, sameDayCutoff, minWindowMinutes, incrementMinutes and fixed. When fixed is true (for example Aramex and Couriers Please) the courier only takes its standard window. |
pickup_ready_from_times, pickup_last_available_times | For couriers with a flexible window, the “ready from” and “last available” times you can offer, as HH:MM. |
pickupAvailability | The same dates and windows in the shape a date and time picker needs. |
tail_lift | Present only when you asked for a tail lift and this courier cannot provide one for your items: {"available": false, "reason": "weight", "message": "..."}. The price stands, but confirming this courier with tail lift fails. |
To check pickup options again before you confirm, call Pickup availability.
Other fields
Section titled “Other fields”id,statusandstatus_code: the draft booking. Both statuses are"new". If the draft cannot be saved, an authenticated quote returns500booking_persist_failedinstead of201withid: null. Retry the quote request before you try to confirm.sender,receiver,items: what was saved on the draft, with their ids.customer_reference: present when you sent one.label: ignore it. Fetch labels withGET /api/v5/bookings/{id}/labelonce the booking is confirmed.service_notices: optional notices that a courier is temporarily unavailable, as{courier, kind, message}. Show themessageand do not offer that courier.meta: diagnostics such as timings. Do not build logic on it.
Not every courier answers: check quote_errors
Section titled “Not every courier answers: check quote_errors”A quote response shows the couriers that answered. It does not promise that every courier was asked and answered. Alongside quotes, every response has a quote_errors array naming each courier that did not produce a price:
{ "quote_errors": [ { "courier": "northline", "code": "no_service", "serviceability": "route.not_serviced", "retryable": false }, { "courier": "aramex", "code": "no_response", "serviceability": "carrier.unavailable", "retryable": true } ]}| Field | Meaning |
|---|---|
courier | The courier id that did not quote. |
code | A broad category: no_service, no_response, invalid_response or courier_error. |
serviceability | The reason. See the table below. |
retryable | true when the problem is temporary and quoting again may succeed. |
Serviceability codes
Section titled “Serviceability codes”serviceability | Meaning | A price is missing? |
|---|---|---|
route.not_serviced | The courier does not cover this lane, area or zone. | No |
route.restricted_postcode | The pickup or delivery postcode is outside the courier’s coverage. | No |
route.no_residential | The courier does not deliver to residential addresses on this lane. | No |
items.not_serviced | The courier does not take this package type or packaging. | No |
items.over_limit | An item is over the courier’s weight or size limits. | No |
service.unavailable | The courier has no service or rate for this shipment. | No |
carrier.unavailable | The courier’s system timed out or could not be reached. | Yes. Quote again. |
config.error | A credentials or configuration problem with the courier. | Yes. Quoting again will not help. |
courier.error | The courier sent a response that could not be read, or another failure. | Yes |
When quotes is empty
Section titled “When quotes is empty”An empty quotes object means no courier priced the shipment. You still get a draft booking id, and quote_errors says why each courier did not quote:
- All genuine “does not carry this” reasons: change the shipment. Common fixes are a different package type (heavy cartons often need to be a pallet, skid or crate), smaller or lighter items, or a correct suburb and postcode.
- Any missing-price reason, such as
carrier.unavailable: quote again shortly.
If a whole request fails before any courier is asked, you get an HTTP error instead, for example 400 for an invalid suburb. See Create a quote.
Frequent rates and tiers
Section titled “Frequent rates and tiers”Couriers Please and Aramex can return a frequent-shipper price in tiers. It is for accounts that send at least 3 consignments per collection. Send its identifier as tier when you confirm. See Frequent rates.
Why your prices can differ from the website
Section titled “Why your prices can differ from the website”A quote made with your API key is priced for your account:
- Your courier discounts. Discounts on your account apply to that courier’s price.
- Your couriers. Couriers switched off for your account are not quoted.
- Your key’s module settings. Surcharges, a fixed price when quoting fails, quote display and default warranty, set on the key’s page under Settings, then Integrations in the members area. See API key configuration.
- Your request. Residential or business addresses, tail lift, declared value and pickup date all change prices.
A quote on the public website, or one sent without a key, uses public prices and only the couriers listed publicly.
Testing quotes
Section titled “Testing quotes”In demo mode, quotes also include a demo courier with a test price, which you can confirm without paying or sending anything.