Skip to main content

Importing Shipments via File Upload

This tutorial guides you through the process of importing multiple shipments into the Visiwise platform at once by uploading a CSV, XLS, or XLSX file. This is the most efficient method for bulk operations.

The import process involves five main steps:

  1. Get your Visiwise API Key.
  2. Authenticate your API requests.
  3. Prepare Your File.
  4. Upload the File.
  5. Monitor the Import Status.
  6. Review the Results.

Step 1: Get Your Visiwise API Key

To begin, you need to get your Visiwise API Key from your account dashboard by following the instructions in the Authentication section.

Step 2: Authenticate Your API Requests

The Visiwise API uses token-based authentication. To authenticate your requests, you must include your API Key in the Authorization header as a Bearer token.

Here is an example of an authenticated request using cURL:

curl -X GET "https://www.visiwise.co/api/v1/shipments/" \
-H "Authorization: Bearer YOUR_VISIWISE_API_KEY"

Step 3: Prepare Your File

Next, you need to create a CSV, XLS, or XLSX file containing your shipment data. The first row of your file must be a header row with column names that exactly match the ones specified below.

File Headers

Header (Case-sensitive)RequiredExampleDescription
tracking_numberYesMSDU8525700The Bill of Lading, booking, or container number.
tracking_typeYescontainerThe type of number provided. Must be one of container, bill_of_lading, or booking.
referenceNoref-C006Your unique identifier for this shipment. If this reference exists, the existing shipment will be updated.
carrierNoMSCUThe SCAC code for the shipping carrier (e.g., MSCU for MSC). If tracking_type is container, you can set this to "AUTO" to let Visiwise detect the carrier automatically.
nameNoShipment of Coconuts & ApplesA friendly name for the shipment.
viewersNo[email protected], [email protected]A comma-separated list of email addresses for users you want to share this shipment with (view-only).
tagsNoPriority, PerishableA comma-separated list of tags to categorize the shipment.
assigneesNo[email protected], [email protected]A comma-separated list of email addresses for team members you want to assign to this shipment.
cf_wdgpuzcr89NoPO-12345To set a custom field value, use the format cf_{customfieldslug}. The custom field must already exist.
See the API Reference for more details.

Example CSV File Content

Here is an example of a correctly formatted CSV file.

tracking_number,tracking_type,reference,carrier,name,viewers,tags,assignees,cf_wdgpuzcr89
MSDU8525700,container,ref-C006,MSCU,Coconuts & Apples,"[email protected], [email protected]","Goods, Import","[email protected], [email protected]",PO-111
MSNU5702988,container,ref-C007,MSCU,Electronics Shipment,"[email protected], [email protected]",Electronics,"[email protected], [email protected]",PO-222

Step 4: Upload the File

Now you can upload your file to the API. You send a POST request to the /v1/shipments/imports/file endpoint. The platform processes your file in the background (asynchronously).

The API will respond immediately with a ShipmentRequestBatch object, which includes a unique id. You will use this id in the next step to check the status of your import.

Reference uniqueness

Each reference must be unique within your workspace. If a reference already exists, the shipment will be updated rather than created.

import requests
import json

BASE_URL = "https://www.visiwise.co"
API_KEY = "YOUR_VISIWISE_API_KEY"

# Headers for application/json requests.
headers = {
"Authorization": f"Bearer {API_KEY}",
"Content-Type": "application/json"
}

# The endpoint for file uploads
upload_url = f"{BASE_URL}/api/v1/shipments/imports/file"

# Headers for multipart/form-data requests.
# Note: The 'requests' library sets the 'Content-Type' with the correct boundary automatically.
upload_headers = {
"Authorization": f"Bearer {API_KEY}"
}

# Prepare the file for uploading
with open('shipments_import.csv', 'rb') as file:
files = {'file': ('shipments_import.csv', file, 'text/csv')}

response = requests.post(upload_url, headers=upload_headers, files=files)

# The API responds with the initial batch object
batch_object = response.json()
batch_id = batch_object["id"]

print(f"File upload submitted successfully! Batch ID: {batch_id}")
print(f"Initial Batch Response: {json.dumps(batch_object, indent=2)}")

Sample Response

The API will return a batch object with a status of processing.

{
"id": "556ec6ce-fa3b-4175-aa93-85e4871291b9",
"status": "processing",
"created_at": "2025-09-16T08:02:25.959Z",
"results": [
{
"id": "af3bb60c-68c8-48f3-99fa-dfffd15e79e4",
"reference": "ref-C006",
"tracking_type": "container",
"tracking_number": "MSDU8525700",
"carrier_scac": "MSCU",
"creation_status": "created",
"tracking_status": "processing",
"shipment_id": "6a3471d5-297d-4399-9962-6a0c2c554b23",
"name": "Coconuts & Apples",
"errors": [],
"warnings": [],
...
},
{
"id": "3f140007-fbb1-40dc-92d9-b8a96f31e8f6",
"reference": "ref-C007",
"tracking_type": "container",
"tracking_number": "MSNU5702988",
"carrier_scac": "MSCU",
"creation_status": "created",
"tracking_status": "processing",
"shipment_id": "a1c06601-5985-4a23-9467-fa9018b8d8a3",
"name": "Electronics Shipment",
"errors": [],
"warnings": [],
...
}
]
}
See the API Reference for the complete response schema.

Step 5: Monitor the Import Status

Because the file is processed asynchronously, you need to poll the import status endpoint to know when it's finished. You can make GET requests to /v1/shipments/imports/{batch_id}.

The status field in the response will change from processing to completed or failed.

Why Poll the API?

Asynchronous processing allows the API to accept your request immediately and handle the file in the background. This is much more reliable for large files. Polling is how your application can ask, "Are you done yet?"

status_url = f"{BASE_URL}/api/v1/shipments/imports/{batch_id}"

while True:
print("Checking import status...")
response = requests.get(status_url, headers=headers)
batch_result = response.json()
status = batch_result.get("status")

print(f"Current Status: {status}")

# Exit the loop if the process is no longer running
if status in ["completed", "failed"]:
print("Import process finished!")
print(f"Final Batch Status: {json.dumps(batch_result, indent=2)}")
break

# Wait 10 seconds before checking again to avoid rate limiting
time.sleep(10)

Sample Response

Once all requests are processed, the status will update to completed.

{
"id": "556ec6ce-fa3b-4175-aa93-85e4871291b9",
"status": "completed",
"created_at": "2025-09-16T08:02:25.959Z",
"results": [
{
"id": "af3bb60c-68c8-48f3-99fa-dfffd15e79e4",
"reference": "ref-C006",
"tracking_type": "container",
"tracking_number": "MSDU8525700",
"carrier_scac": "MSCU",
"creation_status": "created",
"tracking_status": "succeeded",
"shipment_id": "6a3471d5-297d-4399-9962-6a0c2c554b23",
"name": "Coconuts & Apples",
"errors": [],
"warnings": [
{
"code": "invalid_input",
"message": "[email protected] is not a valid member in this workspace",
"target": "assignees"
},
...
],
...
},
{
"id": "3f140007-fbb1-40dc-92d9-b8a96f31e8f6",
"reference": "ref-C007",
"tracking_type": "container",
"tracking_number": "MSNU5702988",
"carrier_scac": "MSCU",
"creation_status": "created",
"tracking_status": "succeeded",
"shipment_id": "a1c06601-5985-4a23-9467-fa9018b8d8a3",
"name": "Electronics Shipment",
"errors": [],
"warnings": [
{
"code": "invalid_input",
"message": "[email protected] is not a valid member in this workspace",
"target": "assignees"
},
...
],
...
}
]
}
See the API Reference for the complete response schema.

Step 6: Review the Results

Once the batch status is completed, you can see which shipments were created successfully.

  • To get a list of all shipments created from this batch, you can use the GET /v1/shipments endpoint and filter by the batch_id.
  • To review any rows that failed, check the errors array in the final batch status object.
# Use the batch_id to query for the newly created shipments
shipments_url = f"{BASE_URL}/api/v1/shipments"
params = {
"batch_id": batch_id
}

response = requests.get(shipments_url, headers=headers, params=params)
response = requests.get(shipments_url, headers=headers, params=params)
shipments = response.json().get("data")

print(f"Retrieved {len(shipments)} shipments for batch {batch_id}:")
print(json.dumps(shipments, indent=2))

Sample Response

The response will be a paginated list of Shipment objects, each containing comprehensive tracking data from all available sources.

{
"data": [
{
"id": "6a3471d5-297d-4399-9962-6a0c2c554b23",
"reference": "ref-C006"
"tracking_number": "MSDU8525700",
"tracking_type": "container",
"carrier_scac": "MSCU",
"last_process_status": "track_succeeded",
"last_process_failure_reason": null,
"data_status": "success",
"container_stage": null,
"custom_fields": {
"wdgpuzcr89": "PO-111"
},
"tracking": {
"tracking_freshness": "ok",
"pre": null,
"pol": null,
"pod": {
...
},
"pde": {
...
},
"containers_uri": "/api/v1/shipment/6a3471d5-297d-4399-9962-6a0c2c554b23/containers",
...
},
},
{
"id": "a1c06601-5985-4a23-9467-fa9018b8d8a3",
"reference": "ref-C007"
"tracking_number": "MSNU5702988",
"tracking_type": "container",
"carrier_scac": "MSCU",
"last_process_status": "track_succeeded",
"last_process_failure_reason": null,
"data_status": "success",
"container_stage": null,
"custom_fields": {
"wdgpuzcr89": "PO-222"
},
"tracking": {
"tracking_freshness": "ok",
"pre": {
...
},
"pol": {
...
},
"pod": {
...
},
"pde": {
...
},
"containers_uri": "/api/v1/shipment/a1c06601-5985-4a23-9467-fa9018b8d8a3/containers",
...
},
}
],
"pagination": {
...
}
}
See the API Reference for the complete response schema.

Final Example Code

import requests
import json

BASE_URL = "https://www.visiwise.co"
API_KEY = "YOUR_VISIWISE_API_KEY"

# Headers for application/json requests.
headers = {
"Authorization": f"Bearer {API_KEY}",
"Content-Type": "application/json"
}

# The endpoint for file uploads
upload_url = f"{BASE_URL}/api/v1/shipments/imports/file"

# Headers for multipart/form-data requests.
# Note: The 'requests' library sets the 'Content-Type' with the correct boundary automatically.
upload_headers = {
"Authorization": f"Bearer {API_KEY}"
}

# Prepare the file for uploading
with open('shipments_import.csv', 'rb') as file:
files = {'file': ('shipments_import.csv', file, 'text/csv')}

response = requests.post(upload_url, headers=upload_headers, files=files)

# The API responds with the initial batch object
batch_object = response.json()
batch_id = batch_object["id"]

print(f"File upload submitted successfully! Batch ID: {batch_id}")
print(f"Initial Batch Response: {json.dumps(batch_object, indent=2)}")

status_url = f"{BASE_URL}/api/v1/shipments/imports/{batch_id}"

while True:
print("Checking import status...")
response = requests.get(status_url, headers=headers)
batch_result = response.json()
status = batch_result.get("status")

print(f"Current Status: {status}")

# Exit the loop if the process is no longer running
if status in ["completed", "failed"]:
print("Import process finished!")
print(f"Final Batch Status: {json.dumps(batch_result, indent=2)}")
break

# Wait 10 seconds before checking again to avoid rate limiting
time.sleep(10)

# Use the batch_id to query for the newly created shipments
shipments_url = f"{BASE_URL}/api/v1/shipments"
params = {
"batch_id": batch_id
}

response = requests.get(shipments_url, headers=headers, params=params)
shipments = response.json().get("data")

print(f"Retrieved {len(shipments)} shipments for batch {batch_id}:")
print(json.dumps(shipments, indent=2))

Wrap-up

Congratulations! You have successfully imported multiple shipments into the Visiwise platform by uploading a file. You can now integrate this workflow into your applications to automate shipment tracking at scale.