     [Blog](https://scrapfly.io/blog)   /  [python](https://scrapfly.io/blog/tag/python)   /  [How to Scrape Maersk Container Tracking and Shipping Schedule Data](https://scrapfly.io/blog/posts/how-to-scrape-maersk)   # How to Scrape Maersk Container Tracking and Shipping Schedule Data

 by [Mohab Yousry](https://scrapfly.io/blog/author/mohab-yousry-9396552a) Jul 21, 2026 20 min read [\#python](https://scrapfly.io/blog/tag/python) [\#scrapeguide](https://scrapfly.io/blog/tag/scrapeguide) 

 [  ](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fscrapfly.io%2Fblog%2Fposts%2Fhow-to-scrape-maersk "Share on LinkedIn") [  ](https://x.com/intent/tweet?url=https%3A%2F%2Fscrapfly.io%2Fblog%2Fposts%2Fhow-to-scrape-maersk&text=How%20to%20Scrape%20Maersk%20Container%20Tracking%20and%20Shipping%20Schedule%20Data "Share on X") [  ](https://www.facebook.com/sharer/sharer.php?u=https%3A%2F%2Fscrapfly.io%2Fblog%2Fposts%2Fhow-to-scrape-maersk "Share on Facebook")    

 

 

Summarize this article with

 [  ](https://chat.openai.com/?q=Summarize%20this%20article%20and%20explain%20how%20Scrapfly%20helps%20me%20scrape%20any%20website%20at%20scale%20and%20bypass%20anti-bot%20systems%20for%20my%20use%20case%3A%20https%3A%2F%2Fscrapfly.io%2Fblog%2Fposts%2Fhow-to-scrape-maersk) [  ](https://claude.ai/new?q=Summarize%20this%20article%20and%20explain%20how%20Scrapfly%20helps%20me%20scrape%20any%20website%20at%20scale%20and%20bypass%20anti-bot%20systems%20for%20my%20use%20case%3A%20https%3A%2F%2Fscrapfly.io%2Fblog%2Fposts%2Fhow-to-scrape-maersk) [  ](https://x.com/i/grok?text=Summarize%20this%20article%20and%20explain%20how%20Scrapfly%20helps%20me%20scrape%20any%20website%20at%20scale%20and%20bypass%20anti-bot%20systems%20for%20my%20use%20case%3A%20https%3A%2F%2Fscrapfly.io%2Fblog%2Fposts%2Fhow-to-scrape-maersk) [  ](https://www.perplexity.ai/search/new?q=Summarize%20this%20article%20and%20explain%20how%20Scrapfly%20helps%20me%20scrape%20any%20website%20at%20scale%20and%20bypass%20anti-bot%20systems%20for%20my%20use%20case%3A%20https%3A%2F%2Fscrapfly.io%2Fblog%2Fposts%2Fhow-to-scrape-maersk) [  ](https://www.google.com/search?udm=50&aep=11&q=Summarize%20this%20article%20and%20explain%20how%20Scrapfly%20helps%20me%20scrape%20any%20website%20at%20scale%20and%20bypass%20anti-bot%20systems%20for%20my%20use%20case%3A%20https%3A%2F%2Fscrapfly.io%2Fblog%2Fposts%2Fhow-to-scrape-maersk) 



         

   **Web Scraping API**Scrape any website with anti-bot bypass, proxy rotation, and JS rendering.

 

 [ Learn More  ](https://scrapfly.io/products/web-scraping-api) [  Docs ](https://scrapfly.io/docs/scrape-api/getting-started) 

 

 

Point a normal HTTP request at a Maersk tracking page and you get back an empty shell no status, no vessel, no ETA. The data loads after the page does, behind anti-bot defenses built for carrier sites. The tracking result is rendered client-side, and Maersk is served through Akamai infrastructure that sets Bot Manager cookies on automated requests.

What follows covers how to extract container tracking events and vessel schedule data from [maersk](https://www.maersk.com/) in Python, handling the JavaScript rendering and anti-bot layer that stops most scrapers before they see a single data field.



[**Maersk Scraper**github.com/scrapfly/scrapfly-scrapers/tree/main/maersk-scraper](https://github.com/scrapfly/scrapfly-scrapers/tree/main/maersk-scraper)

## Key Takeaways

Maersk tracking sits behind JavaScript rendering and anti-bot protection, but results live at a deterministic deep link URL keyed by the reference number, so you can request that URL directly instead of driving the search form.

- Use Scrapfly's Web Scraping API with `asp=True` and `render_js=True` to bypass carrier anti-bot defenses and receive the fully rendered tracking page HTML
- Request `https://www.maersk.com/tracking/<reference>` directly instead of submitting the tracking form
- The form accepts a container number, a Bill of Lading number, or a booking number as the reference
- Container numbers follow ISO 6346 format: a four-letter prefix such as MSKU or MRKU, a six-digit serial number, and a check digit
- Extract tracking events with date, location, facility, vessel, and voyage, plus the current status and origin and destination cities
- Maersk's frontend loads data from internal JSON endpoints after rendering. Wait for that call with `wait_for_selector="xhr:<pattern>"` and read its body off `browser_data.xhr_call`, no CSS selectors or HTML parsing needed
- For vessel schedules, build the search URL from a location ID and RKST code per port, then capture the `routing/routings-queries` XHR for sailing rows with departure, arrival, transit time, and vessel details
- Escalate to a Scrapfly Cloud Browser session for login-gated My Maersk portal flows that require interactive authentication

**Get web scraping tips in your inbox**Trusted by 100K+ developers and 30K+ enterprises. Unsubscribe anytime.







## Why Scrape Maersk Shipping Data?

Teams scrape Maersk to get container status, event timelines, and schedule data into their own systems at scale. The public site is built for one shipment at a time lookups, and the official data feed is gated behind approval and a subscription.

Monitoring a hundred active shipments every few hours is not a workflow the web interface supports, so teams without a suitable carrier feed need an automated data path.

The real demand comes from a few concrete operational jobs:

- **Supply chain visibility**: track container status and event history across many active shipments simultaneously, feeding a TMS or operations dashboard where no clean carrier feed is available
- **Exception and delay alerting**: detect new milestones and stale movement by comparing fresh tracking status and event timelines on a regular schedule
- **Schedule and routing intelligence**: pull point to point sailing options and vessel schedules to support booking decisions and transit time planning before a shipment is committed
- **Carrier data aggregation**: combine Maersk tracking data with feeds from other ocean lines into a single shipment view for customers or internal operations teams

For a broader look at logistics data extraction, including carrier coverage and example scrape patterns, see the [Scrapfly logistics use-case page](https://scrapfly.io/use-case/logistics-web-scraping).

The public interface is designed for individual lookups, so higher-volume workflows require either an approved API, a third-party carrier feed, or programmatic extraction.



## What Data Can You Extract from Maersk?

A tracking lookup gets you a shipment level origin and destination city, plus one or more containers, each with a current status, a last-known location, and an ordered event timeline (description, date, location, facility, and vessel/voyage when applicable).

A schedule search gets you sailing options with vessel name, voyage number, departure and arrival dates, and transit time. Those are the two data jobs this article covers.

**Container tracking record schema:**

| Field | Example | Notes |
|---|---|---|
| Tracking number | 269124324 | The container, BL, or booking reference used in the request |
| Origin | Shanghai | Shipment level origin city |
| Destination | Rotterdam | Shipment level destination city |
| Container number | MSKU1234567 | ISO 6346. Maersk prefixes include MSKU, MRKU, MAEU |
| Container type | 40' High Cube | Built from the container size and type fields on the record |
| Status | Vessel Departed | Latest milestone for this container |
| Current location | Shanghai | City of the most recent event |
| Status date | 2026-06-13T08:00:00Z | Timestamp of the most recent event |
| Last updated | 2026-06-25T08:30Z | Timestamp of the carrier's last data sync |
| Events | See below | Ordered list of milestones, each with location, facility, description, date, vessel, voyage, and an `is_current` flag |

The status-event vocabulary across a typical container journey:

- Gate Out Empty, Gate In Full, Loaded, Vessel Departed
- Transshipment, Vessel Arrived, Discharged
- Gate Out Full, Empty Return

Not every container hits every milestone, and a vessel at sea shows no new events until its next port call, so monitoring logic should expect gaps.

These aren't arbitrary field names. JSONCargo's resold Maersk feed documents a similar set: status, ports, last vessel/voyage, ETA, last-updated. Status and last-updated match the fields shown on Maersk's page. Ports, vessel, and voyage are nested per event rather than exposed as top-level fields. Maersk has no discrete ETA field, so a reseller's ETA is typically derived, not scraped verbatim.

Tracking responses may be delayed or incomplete, so production code should log timeouts and missing XHR calls instead of assuming a fixed completion time.

**Schedule output fields:**

From a point-to-point schedule search, the parser returns origin and destination facility codes, departure and arrival timestamps, Maersk's raw estimated-transit-time value, vessel name, voyage number, and service name. Maersk also exposes port call schedules for specific terminals and vessel schedules for specific ships by name.

Below is a representative extracted record:

Example Outputjson```json
{
  "tracking_number": "269124324",
  "origin": "Shanghai",
  "destination": "Rotterdam",
  "containers": [
    {
      "container_number": "MSKU1234567",
      "container_type": "40' High Cube",
      "last_updated": "2026-06-25T08:30:00Z",
      "status": "Vessel Departed",
      "current_location": "Shanghai",
      "status_date": "2026-06-13T08:00:00Z",
      "events": [
        {
          "location": "Shanghai",
          "facility": "Yangshan Deep Water Port",
          "description": "Gate In Full",
          "date": "2026-06-10T14:00:00Z",
          "vessel": null,
          "voyage": null,
          "is_current": false
        },
        {
          "location": "Shanghai",
          "facility": "Yangshan Deep Water Port",
          "description": "Loaded",
          "date": "2026-06-12T03:00:00Z",
          "vessel": "Maersk Essen",
          "voyage": "123W",
          "is_current": false
        },
        {
          "location": "Shanghai",
          "facility": "Yangshan Deep Water Port",
          "description": "Vessel Departed",
          "date": "2026-06-13T08:00:00Z",
          "vessel": "Maersk Essen",
          "voyage": "123W",
          "is_current": true
        }
      ]
    }
  ]
}
```



With the output shape clear, the next section explains how the tracking system works and why a plain HTTP request cannot reach any of that data.



## How Does Maersk Container Tracking Work?

The Maersk tracking form at [maersk.com/tracking](https://www.maersk.com/tracking/) accepts a container number, Bill of Lading, or booking reference and renders the result client-side. That is why a plain HTTP request returns an empty shell with no tracking data.

### What Inputs Does Maersk Tracking Accept?

The Maersk tracking form accepts three types of reference numbers:

- **Container number**: the physical equipment identifier in ISO 6346 format. The most common lookup for in transit monitoring.
- **Bill of Lading number**: the shipment document identifier. Available once the BL is released by the carrier, which happens closer to vessel departure.
- **Booking number**: Assigned when the booking is confirmed, before a container is even nominated to a sailing.

Booking numbers give the earliest visibility in the shipment lifecycle. Container and BL numbers are the most commonly used for ongoing tracking because they remain stable through the full journey.

### What Does a Maersk Container Number Look Like?

Maersk container numbers follow ISO 6346: a three-letter owner code plus a one-letter equipment category (U for freight), a six-digit serial number, and a check digit, eleven characters total, for example MSKU1234567. Maersk Line's SCAC code is MAEU, and its common prefixes are MSKU, MRKU, and MAEU.

The check digit is computed from the preceding characters, so validating it before a lookup catches a malformed reference before it wastes a request, since a bad reference returns the same not-found state as a genuinely missing container.

The tracking result is rendered by JavaScript after the page loads, while Akamai's anti-bot layer can challenge automated clients before the data request completes. Those two barriers are what the setup below addresses.



## How to Set Up Your Maersk Scraper with Python

The setup is a single install and a client initialization. Scrapfly handles rendering, residential proxy routing, and anti-bot bypass server-side. The same API is available in Python, TypeScript for Node, Deno, Bun, Go, and Rust, plus a Scrapy extension, so the approach works from whichever stack your pipeline uses.

shell```shell
pip install scrapfly-sdk
```



python```python
from scrapfly import ScrapeConfig, ScrapflyClient

SCRAPFLY = ScrapflyClient(key="YOUR_SCRAPFLY_API_KEY")

BASE_CONFIG = {
    "asp": True,             
    "render_js": True,       
    "country": "US",         
    "rendering_wait": 5000,  
}
```



Get your API key from [scrapfly.io/docs/sdk/python](https://scrapfly.io/docs/sdk/python). The `SCRAPFLY` client and `BASE_CONFIG` dictionary above are shared across every scraper function in this guide.

The `rendering_wait` gives the page time to fire its internal API calls before Scrapfly hands back control, which matters because the next section reads those calls directly instead of the rendered HTML. With those in place, the tracking and schedule requests follow the same pattern.



## How to Scrape Maersk Container Tracking Events

To get tracking data, request the tracking deep-link URL directly with [Scrapfly's Web Scraping API](https://scrapfly.io/products/web-scraping-api), with JavaScript rendering and anti-bot bypass enabled. The URL is deterministic `https://www.maersk.com/tracking/<reference>` and accepts a container number, BL number, or booking number, so no form interaction is needed.

Rather than fighting CSS selectors on a page Maersk rebuilds periodically, target the JSON call the page itself makes. The tracking view fetches an internal `synergy/tracking` endpoint and paints the timeline from that response. Scrapfly's `wait_for_selector="xhr:synergy/tracking"` waits for that call, then the parsed body sits on `browser_data.xhr_call`, no HTML parsing needed.

python```python
import json
from typing import Dict

from scrapfly import ScrapeApiResponse, ScrapeConfig, ScrapflyClient

SCRAPFLY = ScrapflyClient(key="YOUR_SCRAPFLY_API_KEY")


def _get_xhr_data(response: ScrapeApiResponse, url_pattern: str) -> Dict:
    """Pull the JSON body of the first background XHR call whose URL contains url_pattern"""
    xhr_calls = response.scrape_result.get("browser_data", {}).get("xhr_call", [])
    call = next((c for c in xhr_calls if url_pattern in c.get("url", "")), None)
    if not call:
        raise RuntimeError(f"XHR call matching '{url_pattern}' not found, try raising rendering_wait")
    return json.loads(call["response"]["body"])


def parse_tracking(data: Dict, tracking_number: str) -> Dict:
    """Parse Maersk's synergy tracking API JSON into structured tracking data"""
    containers = []
    for c in data.get("containers", []):
        locations = c.get("locations") or []
        all_events = [
            (loc, ev)
            for loc in locations
            for ev in (loc.get("events") or [])
        ]
        all_events.sort(key=lambda item: item[1].get("event_time") or "")
        events = [
            {
                "location": loc.get("city"),
                "facility": loc.get("terminal"),
                "description": ev.get("activity", ""),
                "date": ev.get("event_time"),
                "vessel": ev.get("vessel_name"),
                "voyage": ev.get("voyage_num"),
                "is_current": i == len(all_events) - 1,
            }
            for i, (loc, ev) in enumerate(all_events)
        ]
        last_loc, last_event = all_events[-1] if all_events else ({}, {})
        size = c.get("container_size")
        kind = c.get("container_type")
        container_type = f"{size}' {kind}" if size and kind else size or kind

        containers.append({
            "container_number": c.get("container_num"),
            "container_type": container_type,
            "last_updated": c.get("last_update_time"),
            "status": c.get("status"),
            "current_location": last_loc.get("city"),
            "status_date": last_event.get("event_time"),
            "events": events,
        })

    return {
        "tracking_number": tracking_number,
        "origin": data.get("origin", {}).get("city"),
        "destination": data.get("destination", {}).get("city"),
        "containers": containers,
    }


async def scrape_tracking(tracking_number: str) -> Dict:
    """Scrape a container, BL, or booking number from maersk.com's tracking widget"""
    url = f"https://www.maersk.com/tracking/{tracking_number}"
    response = await SCRAPFLY.async_scrape(
        ScrapeConfig(
            url,
            **BASE_CONFIG,
            wait_for_selector="xhr:synergy/tracking",
        )
    )
    data = _get_xhr_data(response, "synergy/tracking")
    return parse_tracking(data, tracking_number)
```



Tracking results are not always complete: containers not yet at a terminal return a not-found state, and vessels at sea show no new events until the next port call. Treat every field as optional, and log the raw XHR payload when fields are missing so you can see what actually changed.

The parser already returns clean, ordered events with an `is_current` flag on the latest milestone, so pulling the current status is just a filter over that flag:

python```python
def current_event(tracking: dict, container_number: str) -> dict | None:
    container = next(
        (c for c in tracking["containers"] if c["container_number"] == container_number),
        None,
    )
    if not container:
        return None
    return next((e for e in container["events"] if e["is_current"]), None)
```



With container tracking covered, the next flow is vessel schedules, which almost no existing Maersk guide documents for developers.



Scrapfly

#### Scale your web scraping effortlessly

Scrapfly handles proxies, browsers, and anti-bot bypass — so you can focus on data.

[Try Free →](https://scrapfly.io/register)## How to Scrape Maersk Vessel Schedules

The Maersk schedule flow at [maersk.com/schedules](https://www.maersk.com/schedules/) takes an origin port, a destination port, and a departure date window and returns sailing options. Each row includes:

- Vessel name and voyage number
- Departure and arrival dates
- Transit time in days
- Service string identifying the trade lane

Most competitor guides stop at tracking and skip schedules entirely.

The three schedule modes available are:

- **Point-to-point**: all sailings between two named ports in a given date range, the most common planning lookup
- **Port calls**: the call schedule for a specific port, useful for terminal and drayage planning
- **Vessel schedule**: the full port call sequence for a specific vessel by name, used for tracking expected port arrivals

The example below covers point-to-point, the most common of the three. Port calls and vessel schedules follow the same XHR-capture principle, but each renders from its own endpoint, so you would need to inspect the network calls on those pages the same way before wiring them up.

The approach mirrors tracking, via the same [Web Scraping API](https://scrapfly.io/products/web-scraping-api) request. A point-to-point search fires a `routing/routings-queries` XHR, so wait for it with `wait_for_selector="xhr:routings-queries"` and read the body off `browser_data.xhr_call`.

Each lookup needs an origin and destination location ID plus an RKST code per port, both pulled from Maersk's location-search autocomplete rather than a plain port name.

python```python
from datetime import datetime, timedelta
from urllib.parse import urlencode


def _parse_port_call(port_call: dict) -> dict:
    facility = port_call.get("location", {}).get("facility", {})
    service = port_call.get("departureService") or port_call.get("arrivalService") or {}
    return {
        "facility_code": facility.get("facilityCode", ""),
        "estimated_arrival": port_call.get("estimatedTimeOfArrival"),
        "estimated_departure": port_call.get("estimatedTimeOfDeparture"),
        "voyage_number": port_call.get("departureVoyageNumber") or port_call.get("arrivalVoyageNumber"),
        "service_name": service.get("serviceName"),
    }


def _parse_routing(routing: dict) -> dict | None:
    """Flatten one routing option's legs into a single summary row"""
    legs = []
    for leg in routing.get("routingLegs", []):
        carriage = leg.get("carriage", {})
        start = carriage.get("vesselPortCallStart")
        end = carriage.get("vesselPortCallEnd")
        if not start or not end:
            continue
        vessel = carriage.get("vessel") or {}
        legs.append({
            "vessel_name": vessel.get("vesselName"),
            "origin": _parse_port_call(start),
            "destination": _parse_port_call(end),
            "transit_time": leg.get("estimatedTransitTime"),
        })

    if not legs:
        return None

    first_leg, last_leg = legs[0], legs[-1]
    return {
        "route_id": routing.get("routeId", ""),
        "estimated_transit_time": routing.get("estimatedTransitTime"),
        "vessel_name": first_leg["vessel_name"],
        "origin_facility": first_leg["origin"]["facility_code"] or None,
        "destination_facility": last_leg["destination"]["facility_code"] or None,
        "departure_time": first_leg["origin"]["estimated_departure"],
        "arrival_time": last_leg["destination"]["estimated_arrival"],
        "voyage_number": first_leg["origin"]["voyage_number"],
        "service_name": first_leg["origin"]["service_name"],
        "legs": legs,
    }


async def scrape_schedule_search(
    from_location: str,
    to_location: str,
    from_rkst_code: str,
    to_rkst_code: str,
    departure_date: str,
) -> dict:
    """Search maersk.com point-to-point schedules"""
    date_to = (datetime.strptime(departure_date, "%Y-%m-%d") + timedelta(weeks=4)).strftime("%Y-%m-%d")
    url = "https://www.maersk.com/schedules/pointToPoint?" + urlencode({
        "from": from_location,
        "to": to_location,
        "fromRkstCode": from_rkst_code,
        "toRkstCode": to_rkst_code,
        "date": departure_date,
        "dateTo": date_to,
    })

    response = await SCRAPFLY.async_scrape(
        ScrapeConfig(url, **BASE_CONFIG, wait_for_selector="xhr:routings-queries")
    )
    data = _get_xhr_data(response, "routing/routings-queries")
    routes = [r for r in (_parse_routing(routing) for routing in data.get("routings", [])) if r]

    return {
        "from_rkst_code": from_rkst_code,
        "to_rkst_code": to_rkst_code,
        "departure_date": departure_date,
        "route_count": len(routes),
        "routes": routes,
    }
```



Maersk defaults the search window to four weeks past `departure_date`, so `dateTo` is computed rather than passed in by the caller. A lookup for Shanghai to Rotterdam, for example, needs both cities' location IDs (`2IW9P6J7XAW72` and `1JUKNJGWHQBNJ`) and their RKST codes (`CNSGH` and `NLROT`), which read like UN/LOCODEs but are a Maersk-specific identifier, not a drop-in substitute for one.



## Does Maersk Have an Official Tracking API?

Yes. Maersk offers a subscription based Visibility and Tracking API through its developer portal at [developer.maersk.com](https://integration.maersk.com/). Access is not automatic. The process requires submitting a request, going through an approval step, and entering a volume-based subscription.

The API covers neutralized public ocean track and trace milestones equivalent to what appears on maersk.com, delivered in a RESTful JSON format with both direct customer and third-party access paths.

The choice between the official API and public-page extraction depends on access, coverage, onboarding time, and operating cost:

| Path | Best when |
|---|---|
| Official Visibility and Tracking API | You qualify, need only ocean milestones, and can absorb the approval timeline and subscription cost |
| Scraping the public pages | You need schedule data, faster onboarding, cross-carrier coverage, or you are aggregating across multiple lines |

Third-party tracking APIs like Vizion, JSONCargo, and Terminal49 are themselves reselling aggregated carrier data. Scraping Maersk directly is the self-hosted equivalent, with full control over refresh frequency, fields, and cost.

If you only need ocean milestones for a moderate shipment volume and can accept the approval wait, the official API is the cleaner path. When the official API's coverage or onboarding does not fit, compare public-page extraction with third-party carrier APIs before choosing an implementation.



## How to Bypass Maersk Blocking and Scale Tracking Lookups with Scrapfly

Maersk serves its tracking and schedule applications through Akamai and loads the target data after JavaScript execution. A proxy alone does not provide browser execution or manage the full request fingerprint. The `asp=True` plus `render_js=True` configuration combines anti-bot handling, proxy routing, and browser rendering for these flows.

Scrapfly's [logistics use-case page](https://scrapfly.io/use-case/logistics-web-scraping) describes proxy, browser-rendering, and anti-bot requirements for collecting data from carrier websites.

At volume, keep these in mind:

- **Rate limiting**: space repeated lookups rather than polling continuously, and record response latency so the polling interval can be tuned from observed behavior.
- **Region and locale**: fix `country="US"` in your base config so responses stay consistent across runs.
- **Partial data**: treat every missing field as an expected state, not a failure, and log the raw result for inspection.
- **Caching**: cache the last known state per container and re-scrape only when the ETA window is close or a status change is expected.



ScrapFly's [Web Scraping API](https://scrapfly.io/products/web-scraping-api) is a single HTTP endpoint for collecting web data at scale, with a **98% bypass success rate** and proxy coverage in **190+ countries**.

- [Anti-Scraping Protection bypass](https://scrapfly.io/docs/scrape-api/anti-scraping-protection) - supports 20+ anti-bot vendors, including Cloudflare, DataDome, PerimeterX, and Akamai.
- [Smart proxy rotation](https://scrapfly.io/docs/scrape-api/proxy) - residential and datacenter pools with country and ASN level geo-targeting.
- [JavaScript rendering](https://scrapfly.io/docs/scrape-api/javascript-rendering) - render SPAs and dynamic pages through real cloud browsers.
- [Browser automation scenarios](https://scrapfly.io/docs/scrape-api/javascript-scenario) - scroll, click, fill forms, and wait for elements without managing a browser fleet.
- [Format conversion](https://scrapfly.io/docs/scrape-api/getting-started#api_param_format) - return pages as HTML, JSON, clean text, or LLM ready Markdown.
- [Session management](https://scrapfly.io/docs/scrape-api/session) - keep cookies, headers, and IPs consistent across multi step flows.
- [Smart caching](https://scrapfly.io/docs/scrape-api/getting-started#api_param_cache) - cache successful responses to cut cost on repeat scraping jobs.
- [Python](https://scrapfly.io/docs/sdk/python), [TypeScript](https://scrapfly.io/docs/sdk/typescript), [Scrapy](https://scrapfly.io/docs/sdk/scrapy), and [no-code integrations](https://scrapfly.io/docs/integration/getting-started) including Make, n8n, Zapier, LangChain, and LlamaIndex.



For login-gated flows on the My Maersk portal, the deep link approach is not enough. Those flows need an interactive browser session. Scrapfly's [Cloud Browser](https://scrapfly.io/products/cloud-browser-api) provides a stealth Chromium instance over CDP, letting existing Playwright, Puppeteer, or Selenium code connect without changes:

python```python
from playwright.sync_api import sync_playwright
from scrapfly import BrowserConfig, ScrapflyClient

client = ScrapflyClient(key="YOUR_SCRAPFLY_API_KEY")
config = BrowserConfig(
    proxy_pool="public_residential_pool",
    country="us",
    os="linux",
)

with sync_playwright() as p:
    browser = p.chromium.connect_over_cdp(client.cloud_browser(config))
    context = browser.contexts[0]
    page = context.pages[0] if context.pages else context.new_page()
    page.goto("https://www.maersk.com/tracking/")
    browser.close()
```



The Cloud Browser path is the escalation for portal flows. For the public tracking and schedule pages, `asp=True` plus `render_js=True` in a standard scrape request is enough. For more on the bypass layer, see [scrapfly.io/bypass](https://scrapfly.io/bypass) and the [Web Scraping API](https://scrapfly.io/products/web-scraping-api).



## FAQ

What does a Maersk container number look like?A Maersk container number follows ISO 6346: a four-letter prefix (owner code plus equipment category), a six-digit serial number, and a check digit, eleven characters total, for example MSKU1234567. Common Maersk prefixes are MSKU, MRKU, and MAEU. The check digit is computed from the remaining digits and can be used to validate the number before a tracking lookup.







Can I track a Maersk shipment by Bill of Lading number?Yes. Both the tracking form and the deep-link URL accept a container number, a BL number, or a booking number. Booking numbers give the earliest visibility, assigned before a container is nominated, while container and BL numbers are the easier reference once the shipment is underway.







Why is my Maersk tracking not updating?Status often pauses while a vessel is at sea or during terminal data-sync delays. A "container not found" result usually means the number is wrong or the container is not yet at a terminal, so build retries and partial-data handling into a scraper.







Does Maersk have a free public tracking API?No. Maersk's official Visibility and Tracking API is subscription-based and approval-gated, and there is no free public developer endpoint, which is why teams scrape the public tracking pages or use third-party tracking APIs.







Why does a plain HTTP request to a Maersk tracking page return no data?The tracking result is rendered by JavaScript after the page loads, so a plain request returns only the page shell, no container data. Akamai may also challenge automated clients before the rendered state is reached. `asp=True` and `render_js=True` in the scrape request handle both.









## Conclusion

Maersk tracking and schedule data is reachable once you treat the site as a JavaScript-rendered, anti-bot-protected target. Request the deterministic tracking deep-link with `asp=True` and `render_js=True`, capture the internal `synergy/tracking` XHR call for the event timeline, and apply the same capture pattern to the `routing/routings-queries` call for point-to-point sailing rows.

For login-gated My Maersk portal flows, the Cloud Browser escalation handles interactive authentication without rewriting existing browser automation code.

[Scrapfly's Web Scraping API](https://scrapfly.io/products/web-scraping-api) handles rendering, anti-bot bypass, residential proxy routing, and structured extraction in a single request. Free credits are available to test the tracking and schedule flows before committing to production volume.



Legal Disclaimer and PrecautionsThis tutorial covers popular web scraping techniques for education. Interacting with public servers requires diligence and respect:

- Do not scrape at rates that could damage the website.
- Do not scrape data that's not available publicly.
- Do not store PII of EU citizens protected by GDPR.
- Do not repurpose *entire* public datasets which can be illegal in some countries.

Scrapfly does not offer legal advice but these are good general rules to follow. For more you should consult a lawyer.

 

   [  Add as a preferred source ](https://google.com/preferences/source?q=scrapfly.io) Table of Contents















 

  Table of Contents- [Key Takeaways](#key-takeaways)
- [Why Scrape Maersk Shipping Data?](#why-scrape-maersk-shipping-data)
- [What Data Can You Extract from Maersk?](#what-data-can-you-extract-from-maersk)
- [How Does Maersk Container Tracking Work?](#how-does-maersk-container-tracking-work)
- [What Inputs Does Maersk Tracking Accept?](#what-inputs-does-maersk-tracking-accept)
- [What Does a Maersk Container Number Look Like?](#what-does-a-maersk-container-number-look-like)
- [How to Set Up Your Maersk Scraper with Python](#how-to-set-up-your-maersk-scraper-with-python)
- [How to Scrape Maersk Container Tracking Events](#how-to-scrape-maersk-container-tracking-events)
- [How to Scrape Maersk Vessel Schedules](#how-to-scrape-maersk-vessel-schedules)
- [Does Maersk Have an Official Tracking API?](#does-maersk-have-an-official-tracking-api)
- [How to Bypass Maersk Blocking and Scale Tracking Lookups with Scrapfly](#how-to-bypass-maersk-blocking-and-scale-tracking-lookups-with-scrapfly)
- [FAQ](#faq)
- [Conclusion](#conclusion)
 
    Join the Newsletter  Get monthly web scraping insights 

 

  



Scale Your Web Scraping

Anti-bot bypass, browser rendering, and rotating proxies, all in one API. Start with 1,000 free credits.

  No credit card required  1,000 free API credits  Anti-bot bypass included 

 [Start Free](https://scrapfly.io/register) [View Docs](https://scrapfly.io/docs/onboarding) 

 Not ready? Get our newsletter instead. 

 

 ## Related Articles

 [     

 python beautifulsoup 

### How to Scrape Ticketmaster in 2026

Scrape public Ticketmaster listings in 2026. Pull artist and discovery events from stable JSON-LD blocks and clear the b...

 

 ](https://scrapfly.io/blog/posts/how-to-scrape-ticketmaster) [     

 python playwright 

### How to Scrape Facebook Marketplace and Events With Python

Scrape public Facebook Pages, Posts, Marketplace, Events, and Groups in Python with Scrapfly: anti-bot bypass, residenti...

 

 ](https://scrapfly.io/blog/posts/how-to-scrape-facebook) [  

 http 

### What is HTTP 409 Error? (Conflict)

HTTP status code 409 generally means a conflict or mismatch with the server state. Learn why it happens and how to avoid...

 

 ](https://scrapfly.io/blog/posts/what-is-http-409-status-code-conflict) 

  



   



 Scale your web scraping effortlessly, **1,000 free credits** [Start Free](https://scrapfly.io/register)