# UPS Package Tracking Scraper (`muhammadafzal/ups-package-tracking-scraper`) Actor

Track current public UPS package status in a browser and return normalized tracking status, latest update, delivery estimate, and delivery details for up to 25 authorized tracking numbers.

- **URL**: https://apify.com/muhammadafzal/ups-package-tracking-scraper.md
- **Developed by:** [Muhammad Afzal](https://apify.com/muhammadafzal) (community)
- **Categories:** E-commerce, Automation, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $20.00 / 1,000 ups tracking results

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## UPS Package Tracking Scraper

Track public UPS package status in a real browser and return normalized, agent-ready JSON for operations teams, ecommerce support, shipment monitoring, and workflow automation.

This Apify Actor opens UPS’s public tracking flow with each supplied tracking number. It uses a coherent browser and proxy session because ordinary HTTP requests to UPS are frequently rejected by Akamai before the tracking application loads. The Actor does not require a UPS developer account, does not solve CAPTCHAs, and does not request proof-of-delivery images or signatures.

### What it extracts

Each tracking number with an active public UPS result produces one dataset item.

| Field | Meaning |
| --- | --- |
| `trackingNumber` | Normalized UPS package or InfoNotice number |
| `status` | Stable category such as `in_transit`, `out_for_delivery`, or `delivered` |
| `statusText` | Current status or latest update shown by UPS |
| `terminal` | Whether the shipment is delivered or returned |
| `delivered` / `delayed` | Automation-friendly state flags |
| `estimatedDelivery` | UPS delivery estimate when publicly displayed |
| `lastEvent` | Latest visible update plus location when available |
| `deliveredTo` | Delivery location text when UPS exposes it |
| `receivedBy` | Recipient text when UPS exposes it; may contain personal data |
| `sourceUrl` / `scrapedAt` | Source page and UTC extraction time |

Invalid, inactive, challenged, or failed lookups are not fabricated as shipment records. They are written as masked diagnostics in the `OUTPUT` key-value-store record, where `outcome` is one of `DATA`, `EMPTY`, `BLOCKED`, `REJECTED`, or `FAILED`.

### When to use it

Use this Actor when you need current public UPS tracking status for up to 25 numbers per run, especially from Apify schedules, API calls, Make, Zapier, n8n, or an AI agent. It is suitable for customer-support triage, delivery exception monitoring, order operations, and periodic status checks.

Do not use it for creating shipments, rating, address validation, claims, delivery changes, proof-of-delivery signatures, or high-volume carrier integration. Those workflows belong on UPS’s authenticated developer APIs. UPS states that tracking data is normally retained for a limited period, so old valid numbers can truthfully return no result.

### Input

| Field | Required | Default | Notes |
| --- | --- | --- | --- |
| `trackingNumbers` | Yes | Public inactive sample | 1–25 values; whitespace, hyphens, and duplicates are removed |
| `locale` | No | `en_US` | UPS language/country locale such as `en_US` |
| `requestTimeoutSecs` | No | `45` | 20–90 seconds per tracking number |
| `proxyConfiguration` | No | Apify Residential, US | Recommended because UPS rejects many datacenter/TLS clients |

The prefilled number is a widely published inactive sample and normally exercises the truthful `EMPTY` path without producing or charging for a result. Replace it with a current number you are authorized to track for live data.

#### Console input

```json
{
  "trackingNumbers": [
    "1Z999AA10123456784",
    "YOUR_SECOND_UPS_NUMBER"
  ],
  "locale": "en_US",
  "requestTimeoutSecs": 45,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": ["RESIDENTIAL"],
    "apifyProxyCountry": "US"
  }
}
```

#### API call

```bash
curl -X POST \
  "https://api.apify.com/v2/acts/YOUR_USERNAME~ups-package-tracking-scraper/runs?token=$APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"trackingNumbers":["YOUR_UPS_TRACKING_NUMBER"],"locale":"en_US"}'
```

Avoid putting API tokens or tracking numbers into public logs. For production integrations, send the Apify token in an authorization header or secret store rather than embedding it in source code.

### Output example

```json
{
  "trackingNumber": "1Z999AA10123456784",
  "carrier": "UPS",
  "status": "delivered",
  "statusText": "Delivered",
  "terminal": true,
  "delivered": true,
  "delayed": false,
  "estimatedDelivery": null,
  "lastEvent": {
    "text": "Delivered",
    "location": "Austin, TX"
  },
  "deliveredTo": "Austin, TX",
  "receivedBy": "FRONT DESK",
  "sourceUrl": "https://www.ups.com/track?loc=en_US&tracknum=1Z999AA10123456784&requester=ST%2Ftrackdetails",
  "scrapedAt": "2026-08-27T05:00:00.000Z"
}
```

Fields that UPS does not expose are explicitly `null`; their absence is never replaced with guessed data.

### Pay per event pricing

| Event | Price | Trigger |
| --- | ---: | --- |
| Actor start | $0.005 | Once when a run starts |
| UPS tracking result | $0.020 | Each schema-valid dataset record |

A run that checks one inactive number costs only the start event. One found package costs about `$0.025`; ten found packages cost about `$0.205`. These examples describe event charges and exclude any platform usage passed through by the account’s pricing configuration. The Actor checks the run’s maximum charge before crawling additional results and does not manually charge either synthetic event.

### Reliability and failure behavior

The Actor uses the public UPS page as its source. It maintains one consistent browser fingerprint, cookie jar, locale, proxy geography, and residential session. Images, fonts, media, and common analytics requests are blocked to reduce bandwidth without removing the scripts required by tracking. Requests run sequentially to avoid hammering UPS.

If UPS presents an Akamai or CAPTCHA challenge, the Actor retires the session and makes one bounded retry with a fresh proxy session. It never solves or bypasses the challenge. A repeated challenge becomes a `BLOCKED` diagnostic with zero result events. Temporary HTTP or rendering problems become `FAILED`; invalid/inactive numbers become `EMPTY`; malformed numbers become `REJECTED`. Valid partial results are preserved even when another lookup fails.

UPS can change its public markup, restrict traffic, remove old tracking data, or hide some details based on shipment type and destination. The Actor therefore promises only fields visible on the public result page. For business-critical or very high-volume use, integrate the official OAuth-protected UPS Tracking API instead.

### Privacy, legal use, and compliance

Tracking numbers and delivery details can be sensitive. Run this Actor only for shipments you own, administer, or are authorized to monitor. Keep runs, datasets, and integrations private; limit retention; and do not publish recipient information. The Actor masks tracking numbers in warnings, does not log them intentionally, and excludes proof-of-delivery images and signatures. You remain responsible for complying with UPS terms, privacy law, contractual duties, and applicable data-retention rules.

This Actor is unofficial and is not affiliated with, endorsed by, or sponsored by UPS. UPS is a trademark of United Parcel Service of America, Inc., used descriptively to identify the public source.

### Support

When reporting a problem, share the Apify run ID, outcome, and timestamp. Do not post a full tracking number or recipient data in a public issue. A redacted number with the first two and last four characters is enough for most diagnostics.

# Actor input Schema

## `trackingNumbers` (type: `array`):

Use this when you have UPS package or InfoNotice numbers to check. Enter 1–25 values, one per row; each value must contain 7–34 letters or digits, for example 1Z12345E0205271688. Duplicates and whitespace are removed. Do not enter order numbers or addresses.

## `locale` (type: `string`):

Use this to choose the language and country version of the UPS tracker. Provide a UPS locale in language\_COUNTRY form, for example en\_US. The default is en\_US; this is not a proxy-country setting.

## `requestTimeoutSecs` (type: `integer`):

Use this to control how long the browser waits for one UPS result. Accepted range is 20–90 seconds; default 45. This does not change the overall Apify run timeout.

## `proxyConfiguration` (type: `object`):

Use this to route the browser through an Apify Proxy session when UPS blocks datacenter traffic. Residential US proxy is the tested default. Disable it only for local debugging; this is not a third-party provider key.

## Actor input object example

```json
{
  "trackingNumbers": [
    "1Z12345E0205271688"
  ],
  "locale": "en_US",
  "requestTimeoutSecs": 45,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "US"
  }
}
```

# Actor output Schema

## `results` (type: `string`):

One record for each tracking number whose current UPS status was found.

## `summary` (type: `string`):

Counts, truthful outcome classification, and masked warnings for this run.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "trackingNumbers": [
        "1Z12345E0205271688"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("muhammadafzal/ups-package-tracking-scraper").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = { "trackingNumbers": ["1Z12345E0205271688"] }

# Run the Actor and wait for it to finish
run = client.actor("muhammadafzal/ups-package-tracking-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "trackingNumbers": [
    "1Z12345E0205271688"
  ]
}' |
apify call muhammadafzal/ups-package-tracking-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,muhammadafzal/ups-package-tracking-scraper"
        }
    }
}

```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/uKd3OvfkcPbjhiBXt/builds/4ocLsXjbHFfe3UZEA/openapi.json
