Build on the MG Ship API
Pull real-time shipment visibility into your own systems with a simple, versioned REST API.
Overview
The MG Ship API is a REST API that returns JSON over HTTPS. It gives your systems programmatic access to shipment visibility data — listings, full shipment records, and tracking timelines — so you can pull milestones into your own TMS, ERP, or customer portal.
Every endpoint lives under the versioned base path below. The API is versioned in the URL path (v1). Breaking changes ship under a new path (v2) with a 12-month deprecation window for the previous version, so an integration built against v1 keeps working.
Base URLs
Pick the base URL for the environment you are integrating against. Every endpoint path below is relative to it. Staging and Test (VPS) are for development and testing; use Production only with live keys.
| Environment | Base URL |
|---|---|
| Production | https://api.mglobalship.com/api/v1/external |
| Staging | https://api.staging.mglobalship.com/api/v1/external |
| Test (VPS) | https://mgs.aipipis.com/api/v1/external |
Authentication
Authenticate every request with an API key sent as a Bearer token in the Authorization header. Keys are issued per tenant from the API Portal, are shown only once at creation, carry explicit scopes (for example shipments.read), and can be rotated or revoked at any time.
Keep keys server-side — never ship them in browser or mobile code. If a key is exposed, rotate it from the API Portal and the old key stops working immediately.
curl https://api.staging.mglobalship.com/api/v1/external/health \
-H "Authorization: Bearer mgs_live_xxxxxxxxxxxxxxxxxxxxxxxx"Conventions
Pagination
List endpoints are cursor-paginated. Pass limit (1–100, default 20) to size a page. When more results exist the response sets has_more to true and returns next_cursor; pass it back as the cursor query parameter to fetch the next page. Cursors are opaque — treat them as a token, do not construct them yourself.
Rate limits
Requests are rate-limited per minute based on your account tier. Every response includes the headers X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset (seconds until the window resets). Exceeding the limit returns 429 with a retry_after_seconds value in the body. Individual keys can be granted a higher ceiling on request.
| Tier | Requests / min |
|---|---|
| Bronze | 60 |
| Silver | 300 |
| Gold | 1,000 |
| Platinum | 5,000 |
| Enterprise | Unlimited |
Idempotency
Write endpoints (such as POST /shipments) require an Idempotency-Key header. Retrying a request with the same key returns the original result instead of creating a duplicate, so a network retry is always safe. Read-only (GET) endpoints do not need it.
Endpoints
/healthCheck that your key works and see your current tier and rate-limit status. A lightweight call to verify connectivity.
curl https://api.staging.mglobalship.com/api/v1/external/health \
-H "Authorization: Bearer mgs_live_..."{
"status": "ok",
"tenant_id": 123,
"tier": "silver",
"rate_limit": {
"limit": 300,
"remaining": 299,
"reset_seconds": 59
}
}/shipmentsRegister a shipment so the platform starts tracking it. Requires the shipments.write scope and a unique Idempotency-Key header. The shipment counts against your plan's monthly quota.
| Parameter | Type | Description |
|---|---|---|
Idempotency-Key* | string (header) | Unique per request; retrying with the same value returns the original shipment instead of creating a duplicate. |
tracking_number* | string | The AWB / B-L / tracking number. |
carrier_code* | string | Carrier or airline code (PCS code, IATA airline code, or AfterShip courier slug). |
mode* | string | Transport mode: sea, air, or courier. |
curl -X POST https://api.staging.mglobalship.com/api/v1/external/shipments \
-H "Authorization: Bearer mgs_live_..." \
-H "Idempotency-Key: a1b2c3d4-e5f6-7890" \
-H "Content-Type: application/json" \
-d '{
"tracking_number": "160-12345675",
"carrier_code": "pcs",
"mode": "air"
}'{
"object": "shipment",
"id": 12346,
"tracking_number": "160-12345675",
"carrier_code": "pcs",
"mode": "air",
"status": "pending",
"awb_number": "160-12345675",
"_links": {
"self": "/api/v1/external/shipments/12346",
"tracking": "/api/v1/external/shipments/12346/tracking"
}
}/shipmentsList your shipments, newest first, with cursor pagination and optional filters.
| Parameter | Type | Description |
|---|---|---|
limit | integer | 1–100, default 20. |
cursor | string | Opaque cursor from a previous page. |
status | string | Filter by shipment status, e.g. in_transit. |
q | string | Full-text search across shipment fields. |
mode | string | air, sea, rail, or road. |
carrier_code | string | Filter by carrier, e.g. maersk. |
po | string | Filter by purchase-order number. |
curl "https://api.staging.mglobalship.com/api/v1/external/shipments?status=in_transit&limit=20" \
-H "Authorization: Bearer mgs_live_..."{
"object": "list",
"data": [
{
"object": "shipment",
"id": 12345,
"tracking_number": "MAEU1234567",
"carrier_code": "maersk",
"mode": "sea",
"status": "in_transit",
"origin": { "country": "CN", "city": "Shanghai", "port_code": "CNSHA" },
"destination": { "country": "MY", "city": "Port Klang", "port_code": "MYPKG" },
"po_number": "PO-2026-001",
"estimated_arrival": "2026-04-30T14:00:00+08:00",
"_links": {
"self": "/api/v1/external/shipments/12345",
"tracking": "/api/v1/external/shipments/12345/tracking"
}
}
],
"has_more": false,
"next_cursor": null,
"_links": { "self": "/api/v1/external/shipments" }
}/shipments/{id}Retrieve a single shipment with full cargo details and its milestone history.
| Parameter | Type | Description |
|---|---|---|
id* | integer | The shipment id (path parameter). |
curl https://api.staging.mglobalship.com/api/v1/external/shipments/12345 \
-H "Authorization: Bearer mgs_live_..."{
"object": "shipment",
"id": 12345,
"tracking_number": "MAEU1234567",
"carrier_code": "maersk",
"mode": "sea",
"status": "in_transit",
"origin": { "country": "CN", "city": "Shanghai", "port_code": "CNSHA" },
"destination": { "country": "MY", "city": "Port Klang", "port_code": "MYPKG" },
"po_number": "PO-2026-001",
"container_no": "MAEU123456789",
"estimated_arrival": "2026-04-30T14:00:00+08:00",
"vessel_name": "MSC GULSUN",
"voyage_number": "2026W0105",
"cargo": {
"weight_kg": 18000.5,
"volume_cbm": 45.25,
"pieces": 120,
"description": "Electronics",
"hs_code": "8471.30",
"container_size": "40HQ",
"declared_value": { "amount": 499900, "currency": "USD" }
},
"milestones": [
{
"object": "milestone",
"status_code": "PICKED_UP",
"status_text": "Picked up",
"location": "Shanghai",
"occurred_at": "2026-04-22T08:15:00+08:00",
"source": "maersk_direct_api"
}
],
"_links": {
"self": "/api/v1/external/shipments/12345",
"tracking": "/api/v1/external/shipments/12345/tracking"
}
}/shipments/{id}/weatherGet current/forecast weather (Open-Meteo) at the shipment's origin and destination. On-demand enrichment, cached ~30 min; a leg is null when its position cannot be resolved.
| Parameter | Type | Description |
|---|---|---|
id* | integer | The shipment id (path parameter). |
curl https://api.staging.mglobalship.com/api/v1/external/shipments/12345/weather \
-H "Authorization: Bearer mgs_live_..."{
"object": "weather",
"shipment_id": 12345,
"origin": {
"label": "Singapore, SG",
"lat": 1.29,
"lng": 103.85,
"observed_at": "2026-06-25T12:00:00+00:00",
"temperature_c": 29.4,
"wind_mps": 2.7,
"condition_id": 803,
"condition_main": "Clouds",
"severity": "low",
"summary": "Clouds",
"alerts_count": 0
},
"destination": {
"label": "New York, US",
"lat": 40.71,
"lng": -74.0,
"observed_at": "2026-06-25T12:00:00+00:00",
"temperature_c": 17.1,
"wind_mps": 0.5,
"condition_id": 803,
"condition_main": "Clouds",
"severity": "low",
"summary": "Clouds",
"alerts_count": 0
},
"_links": {
"self": "/api/v1/external/shipments/12345/weather",
"shipment": "/api/v1/external/shipments/12345"
}
}/shipments/{id}/riskGet conflict / civil-unrest events near the shipment's origin and destination (within 100 km over the last 7 days). On-demand enrichment with cached provider results.
| Parameter | Type | Description |
|---|---|---|
id* | integer | The shipment id (path parameter). |
curl https://api.staging.mglobalship.com/api/v1/external/shipments/12345/risk \
-H "Authorization: Bearer mgs_live_..."{
"object": "geopolitical_risk",
"shipment_id": 12345,
"origin": {
"label": "Singapore, SG",
"lat": 1.29,
"lng": 103.85,
"observed_at": "2026-06-25T12:00:00+00:00",
"severity": "high",
"radius_km": 100,
"since_days": 7,
"summary": "High: 1 violence_civilians event",
"events_count": 1,
"top_events": [
{
"occurred_on": "2026-06-21",
"type": "violence_civilians",
"sub_type": "Attack",
"location": "Singapore",
"fatalities": 0,
"distance_km": 12.0,
"description": "High: 1 violence_civilians event"
}
]
},
"destination": null,
"_links": {
"self": "/api/v1/external/shipments/12345/risk",
"shipment": "/api/v1/external/shipments/12345"
}
}/shipments/{id}/trackingGet the compact tracking timeline (ordered milestone events) for a shipment.
| Parameter | Type | Description |
|---|---|---|
id* | integer | The shipment id (path parameter). |
curl https://api.staging.mglobalship.com/api/v1/external/shipments/12345/tracking \
-H "Authorization: Bearer mgs_live_..."{
"object": "tracking",
"shipment_id": 12345,
"status": "in_transit",
"estimated_arrival": "2026-04-30T14:00:00+08:00",
"last_event_at": "2026-04-23T06:00:00+08:00",
"events": [
{
"object": "milestone",
"status_code": "PICKED_UP",
"status_text": "Picked up at origin",
"location": "Shanghai",
"occurred_at": "2026-04-22T08:15:00+08:00",
"source": "maersk_direct_api"
},
{
"object": "milestone",
"status_code": "DEPARTED_ORIGIN",
"status_text": "Departed origin port",
"location": "Shanghai",
"occurred_at": "2026-04-23T06:00:00+08:00",
"source": "maersk_direct_api"
}
],
"_links": {
"self": "/api/v1/external/shipments/12345/tracking",
"shipment": "/api/v1/external/shipments/12345"
}
}Webhooks
Instead of polling, subscribe to webhooks to be notified when something happens — for example when a shipment hits an exception. You configure endpoint URLs and select event types in the API Portal, and we POST a JSON payload to your URL for each event.
Every delivery is signed so you can verify it came from us. We send the signature in the X-MGS-Signature header, the event type in X-MGS-Event, and a unique delivery id in X-MGS-Delivery-Id (reuse it to make your handler idempotent). Deliveries are retried with exponential backoff over 24 hours.
X-MGS-Signature has the form t=<timestamp>,v1=<signature>, where signature is the hex HMAC-SHA256 of the string "<timestamp>.<raw-request-body>" keyed with your endpoint secret. To verify: split out t and v1, recompute the HMAC over the timestamp, a literal dot, and the exact raw body, and compare. Reject if it does not match or if the timestamp is too old.
X-MGS-Event: shipment.exception
X-MGS-Delivery-Id: whd_8f3c2a1b
X-MGS-Signature: t=1715241600,v1=4e2c...hex...sha256Errors
Errors use standard HTTP status codes and a consistent JSON envelope. The error object always carries a stable code and a human-readable message; some errors add context fields. Every response includes a request_id — quote it when contacting support.
| Status | Code | When |
|---|---|---|
| 400 | validation_error | The request body is missing or invalid (or the Idempotency-Key header is absent). |
| 401 | auth.token_invalid | Missing or invalid API key. |
| 402 | shipment.overage_payment_required | Shipment quota exhausted; settle outstanding overage first. |
| 403 | auth.insufficient_scope | Key lacks the required scope, e.g. shipments.read. |
| 403 | auth.tenant_suspended | The tenant account is not active. |
| 404 | shipment.not_found | The requested resource does not exist. |
| 409 | shipment.duplicate | A shipment with this tracking number and carrier already exists. |
| 429 | rate_limit_exceeded | Rate limit hit; see retry_after_seconds. |
{
"error": {
"code": "auth.token_invalid",
"message": "Token invalid."
},
"request_id": "req_9a8b7c6d"
}{
"error": {
"code": "rate_limit_exceeded",
"message": "Rate limit exceeded for Silver tier (300 req/min). Wait 23s.",
"retry_after_seconds": 23
},
"request_id": "req_1f2e3d4c"
}