> ## Documentation Index
> Fetch the complete documentation index at: https://developers.openborder.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Batch tracking lookup

> Codes that resolve come back in `results` in the order sent, once each. The rest are listed in `not_found`, and a lookup where nothing resolves is still a 200. The call counts once against the rate limit, whatever the number of codes.



## OpenAPI

````yaml /api-reference/tracking.json post /api/v1/tracking/lookup
openapi: 3.0.0
info:
  title: OpenBorder Tracking API
  description: >-
    Tracking events for shipments whose label OpenBorder generated.


    ## Authentication


    Send your API key in the `X-OpenBorder-Api-Key` header on every request. A
    key covers one or more brands, and the

    tracking endpoints need the `tracking:read` scope. `GET /api/v1/me` works
    with any valid key and tells you

    which brands and scopes it has.


    ## Brands


    You only ever see shipments of the brands on your key. A code that belongs
    to another brand gets the same 404 as a code

    that doesn't exist.


    ## Rate limits


    Each key gets 120 requests per minute in a fixed window. A batch lookup

    counts once, however many codes it holds. Every response past authentication
    carries `RateLimit-Limit`,

    `RateLimit-Remaining` and `RateLimit-Reset` (plus the `X-RateLimit-*`
    equivalents), and going over returns 429.

    Requests rejected with 401 or 403 don't count.


    ## Reading events


    Order events by `sequence`, not `timestamp`. Carriers send events late and
    out of order, and some send only a date.

    `status` is one of a fixed set of milestones. Anything we can't map to one
    is `Unknown`, and `message`

    keeps the carrier's own wording.
  version: v1
  contact: {}
servers:
  - url: https://api.openborder.com
    description: Production
security: []
tags: []
paths:
  /api/v1/tracking/lookup:
    post:
      tags:
        - Tracking
      summary: Get the tracking events of several shipments in one call
      description: >-
        Codes that resolve come back in `results` in the order sent, once each.
        The rest are listed in `not_found`, and a lookup where nothing resolves
        is still a 200. The call counts once against the rate limit, whatever
        the number of codes.
      operationId: TrackingController_getTrackingBatch
      parameters: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TrackingBatchRequest'
      responses:
        '200':
          description: The shipments that resolved, and the codes that did not
          headers:
            RateLimit-Limit:
              description: Requests this key may make per minute
              schema:
                type: integer
                example: 120
            RateLimit-Remaining:
              description: Requests left in the current window
              schema:
                type: integer
                example: 117
            RateLimit-Reset:
              description: Seconds until the window resets
              schema:
                type: integer
                example: 42
            X-RateLimit-Limit:
              description: Same as RateLimit-Limit
              schema:
                type: integer
                example: 120
            X-RateLimit-Remaining:
              description: Same as RateLimit-Remaining
              schema:
                type: integer
                example: 117
            X-RateLimit-Reset:
              description: When the window resets, in Unix epoch seconds
              schema:
                type: integer
                example: 1790000000
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TrackingResponse'
        '400':
          description: >-
            The body is not JSON, or `tracking_codes` is missing, empty, holds
            something other than non-empty strings, or has too many codes
          headers:
            RateLimit-Limit:
              description: Requests this key may make per minute
              schema:
                type: integer
                example: 120
            RateLimit-Remaining:
              description: Requests left in the current window
              schema:
                type: integer
                example: 117
            RateLimit-Reset:
              description: Seconds until the window resets
              schema:
                type: integer
                example: 42
            X-RateLimit-Limit:
              description: Same as RateLimit-Limit
              schema:
                type: integer
                example: 120
            X-RateLimit-Remaining:
              description: Same as RateLimit-Remaining
              schema:
                type: integer
                example: 117
            X-RateLimit-Reset:
              description: When the window resets, in Unix epoch seconds
              schema:
                type: integer
                example: 1790000000
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/ValidationError'
                example:
                  statusCode: 400
                  message:
                    - tracking_codes must contain no more than 50 elements
                  error: Bad Request
        '401':
          description: >-
            The API key is missing ("API key required in header:
            X-OpenBorder-Api-Key") or unknown, expired or revoked ("Invalid or
            expired API key")
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/ApiError'
                example:
                  statusCode: 401
                  message: Invalid or expired API key
                  error: Unauthorized
        '403':
          description: >-
            The key lacks the tracking:read scope ("Insufficient scopes"),
            covers no active brand ("API key covers no active brand"), or was
            refused ("API key was refused")
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/ApiError'
                example:
                  statusCode: 403
                  message: Insufficient scopes
                  error: Forbidden
        '429':
          description: Over the per key rate limit. Wait RateLimit-Reset seconds.
          headers:
            RateLimit-Limit:
              description: Requests this key may make per minute
              schema:
                type: integer
                example: 120
            RateLimit-Remaining:
              description: Requests left in the current window
              schema:
                type: integer
                example: 117
            RateLimit-Reset:
              description: Seconds until the window resets
              schema:
                type: integer
                example: 42
            X-RateLimit-Limit:
              description: Same as RateLimit-Limit
              schema:
                type: integer
                example: 120
            X-RateLimit-Remaining:
              description: Same as RateLimit-Remaining
              schema:
                type: integer
                example: 117
            X-RateLimit-Reset:
              description: When the window resets, in Unix epoch seconds
              schema:
                type: integer
                example: 1790000000
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/ApiError'
                example:
                  statusCode: 429
                  message: Rate limit exceeded
        '503':
          description: >-
            We couldn't validate a key we haven't seen recently. Keys validated
            in the last few minutes keep working. Retry with backoff.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/ApiError'
                example:
                  statusCode: 503
                  message: Unable to validate API key
                  error: Service Unavailable
      security:
        - api_key: []
components:
  schemas:
    TrackingBatchRequest:
      type: object
      properties:
        tracking_codes:
          description: >-
            Tracking codes to look up, OpenBorder label or carrier. At most 50.
            Duplicates are collapsed.
          example:
            - OB_LABEL_MNM631582
            - LA123456789NL
          type: array
          items:
            type: string
      required:
        - tracking_codes
    TrackingResponse:
      type: object
      properties:
        results:
          type: array
          items:
            $ref: '#/components/schemas/TrackingResult'
        not_found:
          description: >-
            Codes that returned nothing. Always present, empty when every code
            resolved.
          example: []
          type: array
          items:
            type: string
      required:
        - results
        - not_found
    ValidationError:
      type: object
      properties:
        statusCode:
          type: number
          example: 400
        message:
          oneOf:
            - type: array
              items:
                type: string
            - type: string
          description: >-
            One message per invalid field. A single string instead when the body
            is not valid JSON.
          example:
            - tracking_codes should not be empty
        error:
          type: string
          example: Bad Request
      required:
        - statusCode
        - message
        - error
    ApiError:
      type: object
      properties:
        statusCode:
          type: number
          description: The HTTP status code, repeated
          example: 404
        message:
          type: string
          description: What went wrong. Safe to log, not meant for end customers.
          example: Tracking code not found
        error:
          type: string
          description: The HTTP reason phrase. Absent on 429.
          example: Not Found
      required:
        - statusCode
        - message
    TrackingResult:
      type: object
      properties:
        tracking_code:
          type: string
          description: The code as sent in the request
          example: OB_LABEL_MNM631582
        brand_code:
          type: string
          description: >-
            The brand the shipment belongs to, always one of the brands on the
            calling key
          example: acme
        order_reference:
          type: string
          nullable: true
          description: The merchant's order reference as it reached us
          example: '#L1234'
        latest_tracking_status:
          $ref: '#/components/schemas/TrackingStatus'
          description: Status of the highest sequence event
          example: Delivered
        tracking_updates:
          description: Every event we hold for the shipment, highest sequence first
          type: array
          items:
            $ref: '#/components/schemas/TrackingUpdate'
      required:
        - tracking_code
        - brand_code
        - order_reference
        - latest_tracking_status
        - tracking_updates
    TrackingStatus:
      type: string
      enum:
        - InfoReceived
        - InTransit
        - OutForDelivery
        - AttemptFail
        - AvailableForPickup
        - Delivered
        - Exception
        - Undeliverable
        - Unknown
    TrackingUpdate:
      type: object
      properties:
        sequence:
          type: number
          description: >-
            Order events by this. Allocated when we ingest the event and
            strictly increasing within a shipment. Opaque and gapped: a jump
            between values does not mean events are missing.
          example: 918342207
        status:
          $ref: '#/components/schemas/TrackingStatus'
          example: Delivered
        message:
          type: string
          description: The carrier's own description, unmodified
          example: Delivered to recipient
        timestamp:
          type: string
          nullable: true
          description: >-
            When the carrier says the event happened, ISO-8601 in UTC. Display
            this, do not sort on it.
          example: '2026-09-21T17:22:00.000Z'
        timestamp_precision:
          $ref: '#/components/schemas/TimestampPrecision'
          description: >-
            day means the carrier sent only a date and the time component is
            ours
          example: second
        location:
          $ref: '#/components/schemas/TrackingLocation'
      required:
        - sequence
        - status
        - message
        - timestamp
        - timestamp_precision
        - location
    TimestampPrecision:
      type: string
      enum:
        - second
        - day
        - unknown
    TrackingLocation:
      type: object
      properties:
        city:
          type: string
          nullable: true
          example: Toronto
        province:
          type: string
          nullable: true
          example: Ontario
        country:
          type: string
          nullable: true
          description: ISO-3166 alpha-2
          example: CA
        raw:
          type: string
          nullable: true
          description: The carrier's location string, verbatim
          example: TORONTO, ON M5V
      required:
        - city
        - province
        - country
        - raw
  securitySchemes:
    api_key:
      type: apiKey
      in: header
      name: X-OpenBorder-Api-Key

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.