1. Containers
  2. Retrieve Container

Containers

Retrieve Container

Retrieve detailed information about a specific container, including its current status, tracking history, and related data using the id of the container.

GET
`/v1/containers/:container`
js
        fetch(`https://api.packagex.io/v1/containers/${container_id}`, {
  method: "GET",
  headers: {
    "PX-API-KEY": process.env.PX_API_KEY,
    "Content-Type": "application/json",
  },
});

      

Error Handling

If the container doesn't exist or belongs to a different organization:

js
        {
  "error": {
    "message": "Container not found",
    "status": 404
  }
}

      

List containers

Retrieve a paginated list of containers with comprehensive filtering, sorting, and search capabilities. The endpoint returns containers along with their associated data.

Basic Usage

GET
`/v1/containers`

Fetch a list of containers with default pagination and sorting:

js
        const response = await fetch(`https://api.packagex.io/v1/containers`, {
  method: "GET",
  headers: {
    "PX-API-KEY": process.env.PX_API_KEY,
    "Content-Type": "application/json"
  }
}).then((res) => res.json());

const containers = res.data;

      

Pagination

The API uses page-based pagination to manage large sets of results. Each response includes a pagination object with the following information:

        {
  "pagination": {
    "page": number,         // Current page number
    "limit": number,        // Items per page
    "has_more": boolean,    // Whether more items exist
    "total_count": number   // Total number of items
  }
}

      

Default pagination settings:

  • Page: 1
  • Limit: 10 items per page

Pass limit=all to request the largest available page size, which is 1000. This is a page size, not an unlimited response - if more than 1000 containers match, you still need to page through the results using page. Note that when a search value is also supplied, any limit above 100 is reduced to 100. See the Query Parameter Constraints table below for the accepted page and limit values.

Example request for the second page:

js
        const response = await fetch(`https://api.packagex.io/v1/containers?page=2&limit=25`, {
  method: "GET",
  headers: {
    "PX-API-KEY": process.env.PX_API_KEY,
    "Content-Type": "application/json"
  }
}).then((res) => res.json());

const containers = response.data;
const pagination = response.pagination;

      

Filtering Options

The API supports multiple filtering parameters to help narrow down results:

  1. Location Filtering

Filter containers by their physical location:

  • location_id: Location identifier
  • layout_id: Specific layout within a location
  • layout_path: Filter by layout hierarchy path
js
        const response = await fetch(`https://api.packagex.io/v1/containers?location_id=loc_1zwyxYCKZ15NqirEaqmZoK&layout_id=lay_1zwyxYCKZ15NqirEaqmZoK`, {
  method: "GET",
  headers: {
    "PX-API-KEY": process.env.PX_API_KEY,
    "Content-Type": "application/json"
  }
}).then((res) => res.json());

const containers = response.data;
const pagination = response.pagination;

      

A malformed location_id or layout_id (one that isn't shaped like a short id at all) returns a 400 error. A well-formed but non-existent or wrong-prefix id (for example a bare UUID) passes validation and simply matches no containers, returning a 200 with an empty data array.

  1. Status Filtering

Filter containers by their current status:

  • status: Comma-separated list of tracking statuses (for example delivered, picked_up, in_transit)
  • filter: Predefined status group filter. Allowed values are:
    • completed
    • outstanding
    • exception
js
        const response = await fetch(`https://api.packagex.io/v1/containers?status=delivered,picked_up,in_transit&filter=outstanding`, {
  method: "GET",
  headers: {
    "PX-API-KEY": process.env.PX_API_KEY,
    "Content-Type": "application/json"
  }
}).then((res) => res.json());

const containers = response.data;
const pagination = response.pagination;

      

Any filter value outside the three listed above is rejected with a 400 error (see List Endpoint Error Handling below).

  1. ID Filtering

Filter containers by their id using a comma-separated list. This lets you batch-retrieve several containers in a single call instead of calling GET /v1/containers/:container once per id:

js
        const response = await fetch(`https://api.packagex.io/v1/containers?id=ctnr_1zwyxYCKZ15NqirEaqmZoK,ctnr_w5vJVUcVguySGSM7MbBiB4`, {
  method: "GET",
  headers: {
    "PX-API-KEY": process.env.PX_API_KEY,
    "Content-Type": "application/json"
  }
}).then((res) => res.json());

const containers = response.data;
const pagination = response.pagination;

      

A malformed id value (one that isn't shaped like a short id at all) returns a 400 error. However, a well-formed id that is never a ctnr_-prefixed one - for example a bare UUID - passes validation, since the validator only checks the shape and not the prefix in that case. It simply matches no containers, returning a 200 with an empty data array rather than a 400. The same is true of location_id and layout_id.

  1. Date Filtering

Filter containers by their updated_at timestamp. updated_at accepts an optional operator prefix followed by a colon and one or more comma-separated values:

Operator Meaning Values required
eq Equal to 1
!eq Not equal to 1
gt Greater than 1
gte Greater than or equal to 1
lt Less than 1
lte Less than or equal to 1
bwe Between, exclusive of bounds 2 (comma-separated)
bwi Between, inclusive of bounds 2 (comma-separated)
!bwe Not between, exclusive 2 (comma-separated)
!bwi Not between, inclusive 2 (comma-separated)
in In a list of values 1 or more (comma-separated)
!in Not in a list of values 1 or more (comma-separated)

The date-filter grammar also defines null and !null tokens (in place of an operator/value pair, for example updated_at=null) to filter on the presence or absence of a value. updated_at on containers is always set, so these two tokens are not useful on this particular field.

Use a colon to separate the operator from its value(s), and a comma to separate multiple values:

js
        // Containers updated on or after a given instant
const response = await fetch(`https://api.packagex.io/v1/containers?updated_at=gte:1780000000`, {
  method: "GET",
  headers: {
    "PX-API-KEY": process.env.PX_API_KEY,
    "Content-Type": "application/json"
  }
}).then((res) => res.json());

// Containers updated within a window (inclusive of both bounds)
const windowed = await fetch(`https://api.packagex.io/v1/containers?updated_at=bwi:1780000000,1780100000`, {
  method: "GET",
  headers: {
    "PX-API-KEY": process.env.PX_API_KEY,
    "Content-Type": "application/json"
  }
}).then((res) => res.json());

// Containers NOT updated within a window
const excluded = await fetch(`https://api.packagex.io/v1/containers?updated_at=!bwi:1780000000,1780100000`, {
  method: "GET",
  headers: {
    "PX-API-KEY": process.env.PX_API_KEY,
    "Content-Type": "application/json"
  }
}).then((res) => res.json());

      

Accepted value formats:

  • Epoch seconds, for example 1780000000
  • Epoch milliseconds (13 or more digits), for example 1780000000000
  • A full ISO datetime with a Z suffix, for example 2026-06-01T00:00:00Z or 2026-06-01T00:00:00.123Z
  • RFC 3339 with a UTC offset, for example 2026-06-01T00:00:00+05:00 or 2026-06-01T00:00:00.123+05:00

The fractional-seconds part of an ISO/RFC 3339 value, if present, must have exactly 3 digits (milliseconds) - 2026-06-01T00:00:00.123Z is accepted, but 2026-06-01T00:00:00.1Z, 2026-06-01T00:00:00.12Z and 2026-06-01T00:00:00.123456Z are all rejected with a 400 error. Omitting the fractional part entirely (2026-06-01T00:00:00Z) is also accepted.

A date-only value such as 2026-06-01 is rejected with a 400 param.invalid error. This is the most common integration mistake for this filter - always send a full ISO datetime or an epoch value, never a bare date.

A bare updated_at value with no operator prefix (for example ?updated_at=1780000000) is not the same as eq:1780000000: the bare form matches only the exact stored instant, while eq: matches the entire second (that is, [1780000000.000, 1780000000.999]). When in doubt, use an explicit operator.

  1. Metadata Filtering

Filter containers based on metadata fields using the metadata query parameter:

js
        const response = await fetch(`https://api.packagex.io/v1/containers?metadata.route_number=RT-123`, {
  method: "GET",
  headers: {
    "PX-API-KEY": process.env.PX_API_KEY,
    "Content-Type": "application/json"
  }
}).then((res) => res.json());

const containers = response.data;
const pagination = response.pagination;

      

Sorting

Control the order of returned results using sort parameters:

Parameter Type Default Description
order_by string created_at Field to sort by, supported values: created_at, updated_at, estimated_delivery_at
direction string desc Sort direction, supported values: asc, desc
js
        const response = await fetch(`https://api.packagex.io/v1/containers?order_by=updated_at&direction=desc`, {
  method: "GET",
  headers: {
    "PX-API-KEY": process.env.PX_API_KEY,
    "Content-Type": "application/json"
  }
}).then((res) => res.json());

const containers = response.data;
const pagination = response.pagination;

      

The API provides advanced full-text search capabilities through the search parameter, allowing you to search containers using fuzzy, typo-tolerant matching across multiple fields. The search functionality examines key container properties including:

  • tracking_number
  • metadata
js
        const response = await fetch(`https://api.packagex.io/v1/containers?search=CTNR-N3J70JS-G07WFSJ`, {
  method: "GET",
  headers: {
    "PX-API-KEY": process.env.PX_API_KEY,
    "Content-Type": "application/json"
  }
}).then((res) => res.json());

const containers = response.data;
const pagination = response.pagination;

      

Each container in the search results includes a special _search object that provides detailed information about the match:

js
        "_search": {
  "tracking_number": "<mark>CTNR-N3J70JS-G07WFSJ</mark>",
  "relevance_score": 1,
  "query": "CTNR-N3J70JS-G07WFSJ"
}

      

The _search object includes highlighted matches using <mark> tags, making it easy to emphasize matching text in user interfaces. It also provides a relevance score between 0 and 1, where higher values indicate better matches. By default, search results are ordered by relevance score to show the best matches first. However, you can override this by specifying an order_by parameter to sort by other fields like created_at, updated_at, estimated_delivery_at. The search functionality can be combined with other filter parameters to refine your results further.

Search strategy

When search contains multiple words, search_strategy controls whether a container must match ANY of those words or ALL of them:

Value Behavior
match_some (default) Matches containers with ANY of the search terms
match_all Matches containers with ALL of the search terms
js
        // Only return containers that match every word in the search string
const response = await fetch(`https://api.packagex.io/v1/containers?search=CTNR-N3J70JS+warehouse&search_strategy=match_all`, {
  method: "GET",
  headers: {
    "PX-API-KEY": process.env.PX_API_KEY,
    "Content-Type": "application/json"
  }
}).then((res) => res.json());

      

Because the default is match_some, a multi-word search value widens your result set (and pagination.total_count) compared to requiring every term to match. Pass search_strategy=match_all to restore ALL-terms matching.

Query Parameter Constraints

The following constraints are enforced on list requests. A request that violates any of them returns a 400 error - see List Endpoint Error Handling below.

Parameter Constraint
page Must be an integer >= 1. page=0, page=abc, and page=1.5 are all rejected
limit Must be an integer >= 1, or the literal string all (which means a page size of 1000, not an unlimited response). Values above 1000 are reduced to 1000, and any value above 100 is reduced to 100 when a search value is supplied. limit=none is not supported
search Maximum 255 characters
layout_path Maximum 511 characters
location_id Must be shaped like a short id. A well-formed id that isn't loc_-prefixed (for example a bare UUID) is accepted but matches nothing
metadata Must be supplied as key:value (a bare key with no :value is rejected)

List Endpoint Error Handling

Invalid list requests return HTTP 400 with one of two body shapes, depending on where the request fails:

Schema validation errors - for example an unlisted filter value, a non-integer page, or a search string over 255 characters:

js
        {
  "code": "validation.failed",
  "message": "Request failed validation",
  "errors": ["query.filter"],
  "data": {
    "query.filter": { ... }
  }
}

      

errors is an array of the field paths that failed validation, and data contains a per-field issue dictionary with more detail.

Parameter-helper errors - for example a date-only updated_at value, or a bwi/bwe operator missing its second value:

js
        {
  "code": "param.invalid",
  "message": "Invalid datetime '2026-06-01'. Provide a full ISO datetime (2026-06-01T00:00:00Z), epoch seconds, or epoch milliseconds",
  "errors": []
}

      

On this path, message echoes the offending value and errors is always an empty array - inspect message rather than errors to see what was wrong.