API overview
The Transdirect API is a JSON REST API for quoting, booking, labelling and tracking shipments with Australian couriers. This page covers the rules every endpoint shares. New to the API? Start with the quickstart.
Base URL and versions
Section titled “Base URL and versions”https://api.transdirect.com.au/api/v5- Every path starts with
/api/v5. The major version is in the path and only changes for a breaking change; see SDKs and versioning. - HTTPS only.
- The machine-readable contract is the OpenAPI spec at
https://api.transdirect.com.au/openapi.json. The API Explorer is built from it.
Authentication
Section titled “Authentication”Send your key in the api-key header. Most endpoints need it; public tracking
(GET /api/v5/tracking/{reference}), the location lookups (except
validate-route), couriers and the public quote endpoint do not. See
authentication.
curl https://api.transdirect.com.au/api/v5/member \ -H "api-key: $TD_API_KEY"Requests
Section titled “Requests”- Send request bodies as JSON with
content-type: application/json. Some endpoints also accept form-encoded bodies for older plugins, but JSON is the supported format. - Path ids such as
{id}are the numeric Transdirect booking id, which you get from the quote response. Your own order or consignment number goes incustomer_referenceinstead. - Weights are in kilograms and dimensions in centimetres.
- Unknown fields in a quote body are dropped without an error. Some other bodies,
such as booking updates and items, are strict and reject unknown fields with a
400. Check field names against the reference page or the spec.
Responses
Section titled “Responses”- Responses are usually JSON. Labels and other documents are
application/pdf, the streamed quote endpoint istext/event-stream, a failedtest_api_key_settingsreturns plain text, and a document that is not ready yet, requested withAccept: text/html, returns an HTML page instead of the JSON error. - Money is in Australian dollars, as numbers such as
12.93. Quotetotalincludes GST, andapplied_gstshows the GST part. - Timestamps are ISO 8601 with an offset. Quote responses use Sydney time
with an offset that has no colon, such as
2026-10-16T15:32:38+1100. Booking responses use UTC, such as2026-10-16T04:32:38.000Z. Always parse the offset rather than assuming a timezone. One exception: a booking’spickup_windowholds the pickup’s local wall-clock times even though the values end inZ, so read them as local times, not UTC. - Pickup dates are
YYYY-MM-DD. Pickup times areHH:MMin the pickup address’s local time. - On a booking,
statusis a display label such as “Confirmed” that may be reworded. Write your code againststatus_code, the stable lowercase code such asconfirmed. See the booking lifecycle.
Request ids
Section titled “Request ids”Every response carries a cf-ray header that identifies the request. Some
error bodies also include a requestId. Log both, and quote them when you
contact support with the booking id and the request and
response.
Errors
Section titled “Errors”Check the HTTP status first. The body then explains the problem, but its shape depends on where the error came from, so do not assume one envelope.
| Status | When |
|---|---|
200, 201 | Success. POST /api/v5/quotes returns 201. POST /api/v5/orders returns 201 for a new order and 200 for an existing one. |
204 | Success with no body, for example a confirm or a delete. |
304 | GET /api/v5/bookings with an If-None-Match that matches its ETag. |
400 | The request is invalid. The body says why (see the shapes below). |
401 | The api-key header is missing, or the key is wrong or deactivated. |
402 | The payment was declined or failed at confirm. |
403 | The booking cannot be paid for (for example NO_PAYMENT_METHOD), or a batch includes another account’s booking. |
404 | The booking, item or resource does not exist or is not yours, or the path is wrong. |
409 | A document is not ready yet, or the booking can no longer be booked. Retry later or re-quote. |
422 | The chosen courier cannot provide the tail lift this booking needs. |
429 | Rate limited. Wait for the retry-after header. |
500 | Something failed on our side, or a booking that is already paid was deleted. |
502, 503 | A service we depend on is unavailable. Retry with backoff. |
Error body shapes
Section titled “Error body shapes”Simple error. Most endpoint errors are a single code:
{ "error": "booking_not_found" }Switch on error. Examples are missing_api_key, invalid_api_key,
booking_not_found, item_not_found, invalid_since, invalid_sort,
rate_limited and label_not_ready. Document errors also include a message.
Field errors. Quote, booking and item rules report problems by field:
{ "errors": { "receiver": { "country": ["Invalid Country"] } } }errors can be an object keyed by field, a list of messages, or a single
string, for example {"errors":["Cannot update. Booking has been processed."]}.
Show the messages to whoever can fix the data.
Validation errors. A body or parameter that does not match the schema
returns one of two shapes. Both have error.name set to ZodError:
{ "error": { "name": "ZodError", "issues": [ { "path": ["payment_method"], "code": "invalid_format", "message": "payment_method must be \"demo\", \"credit\", nab_<id>, or paypal_<id>" } ] }, "requestId": "917e1baf-9e14-417e-b577-326ae42d1e7c"}{ "success": false, "error": { "name": "ZodError", "message": "[{\"code\":\"too_small\",\"path\":[\"reference\"],\"message\":\"Too small: expected string to have >=3 characters\"}]" } }In the second shape, message is a string that contains a JSON list of issues.
Parse it to get the field paths.
Payment and access errors at confirm:
{ "errors": { "payment": ["..."] }, "payment_error": { "code": "...", "user_message": "...", "log_id": "...", "method": "card" } }{ "error": "Add a payment method or set up a credit account to make a live booking.", "access_error": { "code": "NO_PAYMENT_METHOD", "user_message": "..." } }Read payment_error.code or access_error.code in code and show
user_message to the person booking. See how payments work.
Wrong path. A path under /api/v5 that does not exist returns:
{ "error": "legacy_public_route_not_mounted", "path": "/api/v5/unknown-endpoint" }Check the method and path against the reference. A literal placeholder such as
{id} or [Number] left in the URL is a common cause.
Empty bodies. A few responses have no body: 401 on a HEAD request,
404 from GET /api/v5/bookings when you have no bookings yet, 404 from a
booking’s item list when it has no items, and 400 from the sender or receiver
endpoint when that address is missing.
Unexpected errors return 500 {"error":"internal_error","requestId":"..."}.
Rate limits
Section titled “Rate limits”| Who | Limit |
|---|---|
Requests with an api-key (except POST /api/v5/bookings/public, where an API key does not lift the per-IP limit) | 600 per minute per key |
POST /api/v5/bookings and POST /api/v5/bookings/stream without a key | 10 per hour per IP address |
POST /api/v5/bookings/public | 30 per minute and 150 per 5 minutes per IP address |
- Over the limit, you get
429and aretry-afterheader with the number of seconds to wait. There are noX-RateLimit-*headers, so count your own requests if you need to stay under the limit. - A keyed request returns
{"error":"rate_limited"}. An anonymous quote returns{"error":"rate_limited_anonymous_quotes", ...}withlimitandwindow_seconds. Quote with your API key (POST /api/v5/quotes) to get the keyed limit. - Back off and retry on
429,502and503. Do not retry4xxerrors unchanged.
Polling, not webhooks
Section titled “Polling, not webhooks”The API does not send webhooks. Poll for changes:
- Labels: after a confirm,
HEAD /api/v5/bookings/{id}/labelreturns200once the label is ready. Poll every few seconds, thenGETit. - Tracking: poll
GET /api/v5/tracking/{reference}for bookings that are in transit. A tracking request can fetch fresh scans from the courier, so keep to a modest interval per booking (for example every 30 minutes). Stop polling once a booking is delivered. - Bookings:
GET /api/v5/bookingsreturns anETag. Send it back inIf-None-Matchto get a304when nothing has changed.
Lists and paging
Section titled “Lists and paging”List endpoints return a capped list, not pages with cursors:
GET /api/v5/bookingsreturns at most 100 bookings, newest first, and leaves out unconfirmed quotes. To walk through more, sort oldest first and movesinceforward, for example?sort=booking_time&since=2026-10-01T00:00:00Z. If you use an offset such as+10:00, URL-encode the+as%2B. This is a capped list, not guaranteed pagination: more than 100 bookings with the samecreated_atcannot be stepped past.GET /api/v5/ordersreturns at most 50 orders, newest first.GET /api/v5/locationsis paged with/api/v5/locations/page/{page}and alimitof up to 5000, and returns anext_pagelink while there are more.
API map
Section titled “API map”| Area | What it does | Reference |
|---|---|---|
| Quotes | Price a shipment with every courier and create a draft booking | Get quotes, pickup availability |
| Bookings | Confirm, read, update, list and delete bookings | Create, confirm, get, list, update, delete, addresses, customs declaration |
| Booking items | Read and change the parcels on a draft booking | List, get, create, update, delete |
| Labels and documents | Labels, A6 labels, manifests, invoices, shareable links and batches | Get a label, related documents |
| Tracking | Where a shipment is | Track a booking, tracking concepts |
| Members | Your account and saved payment methods | Get member, payment methods |
| Locations | Suburbs, postcodes, states and route checks | List, search, by postcode, states, validate a route |
| Couriers | Courier names and ids | List couriers |
| Collection points | Parcel lockers and drop-off points | Collection points |
| Orders | Store orders imported for booking | List, get, create, update |
| Account configuration | Settings for a key, boxing and frequent rates | API key configuration, order boxing, frequent rates |