Introduction

Bob Go API

The Bob Go API lets you rate, book and track shipments across every courier Bob Go supports, using a single integration. Everything the dashboard does is available over the same HTTP API.

What you can build with it:

  • Rates at checkout: show your buyers the shipping options you have configured in Bob Go, priced by your own rules, while they are still in the cart.
  • Order synchronisation: push orders into Bob Go as they are placed, and keep them in step as they change.
  • Shipment submission: quote couriers, book the one you want, and pull the waybill.
  • Tracking: read a shipment's tracking events, or have Bob Go push them to you as they happen.

Base URLs

Environment Base URL
Sandbox https://api.sandbox.bobgo.co.za/v2
Production https://api.bobgo.co.za/v2

Sandbox is a full copy of the platform wired to test courier accounts. Nothing you create there is collected or billed, and the account is pre-loaded with credit so you can create as many test shipments as you like. Sign up at sandbox.bobgo.co.za and create a key under Settings → API keys.

Going live is a two-line change: swap the base URL and swap the API key. The request and response shapes are identical.

In this Bruno collection the environment sets BobGoHost to the host, and each request carries the /v2 prefix in its path.

Conventions

  • All requests and responses are JSON. Send content-type: application/json on any request with a body.
  • Paths are prefixed with /v2, e.g. POST /v2/rates, POST /v2/shipments.
  • Field names are snake_case.
  • Money is a number in ZAR (rands), e.g. 152.35. Amounts are VAT-inclusive unless the field name says otherwise (rate_amount vs rate_amount_excl_vat).
  • Dimensions are centimetres (_cm), weights are kilograms (_kg).
  • Timestamps are RFC 3339, e.g. 2026-09-05T08:30:00Z.
  • An array in the query string is written as a JSON array with no spaces, e.g. ?status=["in-transit","delivered"]. A space after a comma is not rejected, but the value then matches nothing: a list comes back empty, and a waybill request finds no shipments to print.
  • Every request acts on the account your API key belongs to. Bob Go reads the account from the key, so you do not send account_id, and a value you do send is replaced rather than checked.

Objects that appear in more than one endpoint, addresses and parcels, always have the same shape wherever they appear. See Important concepts.

Listing and pagination

List endpoints accept limit and offset, plus order_by and order (asc / desc). order only applies together with order_by: send both, and the sort is ascending unless order is desc. Without order_by the endpoint's default sort is used and order is ignored. Responses wrap the page in an object with a count of the whole result set, not of the page. This is GET /v2/shipments?limit=2 on an account with 348 shipments, trimmed to two fields a shipment:

{
  "shipments": [
    { "id": 4455123, "tracking_reference": "UATCG4X7Q2" },
    { "id": 4455122, "tracking_reference": "UATCG4X7PZ" }
  ],
  "count": 348
}

Defaults and caps are per endpoint, so read the page for the one you are calling. GET /v2/orders and GET /v2/shipments both default to 100 and cap at 100. GET /v2/packages has no default and no cap: it returns every package on the account unless you set limit yourself.

Errors

Failures return an HTTP status code and a single message field:

{
  "message": "Collection contact name is required."
}
Status Meaning
400 The request failed validation. The message says what.
401 The API key was not recognised. See Authentication.
403 No API key was sent, the key may not make this call, or your plan does not include API access.
404 No such path, or no such record on your account. Some endpoints report a missing record as a 400 or an empty list instead.
423 The account has been closed.
429 Too many requests on this key. Back off and retry.
500 Something failed on our side. A few validation failures also come back as 500, so read the message before retrying.
503 The request ran past the gateway's limit of about 30 seconds, usually while waiting on a courier. The body is {"message": "Service Unavailable"}. Retry later.

Treat the message as human-readable text for your logs and support team, not as a stable machine-readable code. Branch on the status code instead.