logo

Integrate with Python

GuideUpdated 2026-06-20

Build a complete Amazon scraping workflow in Python using only the standard requests library. This recipe covers every step from creating a collection through to reading your result file, using the real MultiCartAPI endpoints.

Prerequisites

  • A MultiCartAPI account with a valid API key (find it under Account → API Key in the dashboard).
  • An active postcode for the domain you want to scrape. See Add a Postcode / ZIP if you haven't created one yet.
  • The numeric domain ID for your target marketplace and the zipcode row ID for your postcode — both are returned by the reference endpoints below.

Discover your IDs

Before scraping you need two IDs that are stable per account: the domain ID and the zipcode row ID.

import os
import time
import requests

BASE = "https://multicartapi.com/api/v1"
API_KEY = os.environ["MCA_API_KEY"]


def post(path, body=None):
    """POST to the API and raise if the envelope status is not 1."""
    resp = requests.post(
        f"{BASE}{path}",
        headers={"x-api-key": API_KEY, "Content-Type": "application/json"},
        json=body or {},
    )
    resp.raise_for_status()
    data = resp.json()
    if data.get("status") != 1:
        raise RuntimeError(f"API error on {path}: {data}")
    return data["data"]


# 1. List domains to find the ID for your target marketplace
domains = post("/settings/domains/")
for d in domains:
    print(d["id"], d["domain"])
# e.g. 1 → amazon.com.au


# 2. List your active postcodes to find the zipcode row ID
zipcodes = post("/zipcodes/get-zipcodes/", {"status": "active", "domain": 1})
for z in zipcodes:
    print(z["id"], z["zipcode"], z["domain"]["domain"])
# e.g. 15 → 4500, amazon.com.au

Note the integer IDs printed — you will substitute them for DOMAIN_ID and ZIPCODE_ID in the full script below.

Complete end-to-end script

The script below creates one collection per group of ASINs, adds ASINs one collection at a time, triggers a run, polls until the download is ready, then fetches the first result page.

import os
import time
import requests

# ---------------------------------------------------------------------------
# Configuration — set these via environment variables or replace inline
# ---------------------------------------------------------------------------
BASE = "https://multicartapi.com/api/v1"
API_KEY = os.environ["MCA_API_KEY"]

DOMAIN_ID = 1           # integer ID from /settings/domains/ (e.g. 1 = amazon.com.au)
ZIPCODE_ID = 15         # integer ID from /zipcodes/get-zipcodes/ (e.g. 15 = postcode 4500)
DOMAIN_NAME = "amazon.com.au"
POSTCODE_VALUE = "4500"

# ASINs to scrape, grouped by collection name
COLLECTIONS = {
    "AU Laptops June": [
        "B0DJQQ38TG",
        "B0ABC12345",
        "B0XYZ99999",
    ],
    "AU Monitors June": [
        "B0MON11111",
        "B0MON22222",
    ],
}

POLL_INTERVAL = 15   # seconds between download-ready polls
MAX_POLLS = 60       # give up after ~15 minutes per collection


# ---------------------------------------------------------------------------
# Helper
# ---------------------------------------------------------------------------
def post(path, body=None):
    """POST to BASE+path, assert envelope status==1, return data."""
    resp = requests.post(
        f"{BASE}{path}",
        headers={"x-api-key": API_KEY, "Content-Type": "application/json"},
        json=body or {},
    )
    resp.raise_for_status()
    envelope = resp.json()
    if envelope.get("status") != 1:
        raise RuntimeError(f"API error [{path}]: {envelope}")
    return envelope["data"]


# ---------------------------------------------------------------------------
# Step 1 — Create a collection
# ---------------------------------------------------------------------------
def create_collection(name):
    data = post("/schedules/add_schedule/", {
        "name": name,
        "request_type": "amazon",
        "schedule": "Manual",
        "priority": "Normal",
        "notification_email": os.environ.get("MCA_NOTIFY_EMAIL", "[email protected]"),
        "fetch_mode": "full",
    })
    collection_id = data["id"]
    print(f"  Created collection '{name}' → id={collection_id}")
    return collection_id


# ---------------------------------------------------------------------------
# Step 2 — Add ASINs (one collection at a time — never mix collections)
# ---------------------------------------------------------------------------
def add_asins(collection_id, asins):
    """Add ASINs to a single collection using the multiple endpoint."""
    data = post("/schedules/amazon/asin/create/multiple/", {
        "collection": collection_id,
        "domain": DOMAIN_NAME,
        "customer_postcode": POSTCODE_VALUE,
        "asins": ",".join(asins),
        "including_raw_html": False,
    })
    print(f"  Added {data['success_count']} ASINs; errors: {data.get('error_rows', [])}")


# ---------------------------------------------------------------------------
# Step 3 — Run the collection
# ---------------------------------------------------------------------------
def run_collection(collection_id):
    data = post("/schedules/run/result/", {"collection_id": collection_id})
    print(f"  Run triggered: {data}")


# ---------------------------------------------------------------------------
# Step 4 — Poll until is_download_generated is True
# ---------------------------------------------------------------------------
def poll_until_ready(collection_id):
    for attempt in range(1, MAX_POLLS + 1):
        results = post("/schedules/collections/results/download/", {
            "collection_id": collection_id,
            "skip": 0,
            "limit": 1,
        })
        if results and results[0].get("is_download_generated"):
            print(f"  Download ready after {attempt} poll(s).")
            return results[0]
        print(f"  Poll {attempt}/{MAX_POLLS}: not ready yet, waiting {POLL_INTERVAL}s …")
        time.sleep(POLL_INTERVAL)
    raise TimeoutError(f"Collection {collection_id} did not finish within the polling window.")


# ---------------------------------------------------------------------------
# Step 5 — Fetch the first result page file
# ---------------------------------------------------------------------------
def fetch_result_file(runner_record):
    pages = runner_record.get("download_links", {}).get("pages", [])
    if not pages:
        print("  No result pages available.")
        return None

    url = pages[0]
    print(f"  Fetching result file: {url}")
    resp = requests.get(url, headers={"x-api-key": API_KEY})
    resp.raise_for_status()
    return resp.json()


# ---------------------------------------------------------------------------
# Main — iterate per collection; never mix ASIN batches across collections
# ---------------------------------------------------------------------------
def main():
    for name, asins in COLLECTIONS.items():
        print(f"\n=== Collection: {name} ===")

        collection_id = create_collection(name)
        add_asins(collection_id, asins)
        run_collection(collection_id)
        runner = poll_until_ready(collection_id)
        results = fetch_result_file(runner)

        if results:
            total = runner.get("total_results", "?")
            print(f"  Scraped {total} item(s). First record: {results[0] if isinstance(results, list) else results}")

    print("\nDone.")


if __name__ == "__main__":
    main()

Always enqueue per collection — never mix

The add_asins call above is deliberately called once per collection before moving to the next. Never pass a combined ASIN list that spans multiple collections into a single call. Each call to /schedules/amazon/asin/create/multiple/ must target one collection only. Mixing collections in a single ASIN batch causes items to be attributed to the wrong collection and run together incorrectly.

How the script works

Authentication

The post helper reads MCA_API_KEY from the environment and attaches it as the x-api-key request header. Every authenticated endpoint accepts this header.

Envelope check

All MultiCartAPI responses use HTTP 200 regardless of business-logic outcome. The actual result lives in the envelope: status: 1 means success, anything else is an error. The post helper asserts status == 1 and raises immediately if it is not, so you never silently swallow an error.

Create collection (POST /schedules/add_schedule/)

Each collection starts with status: draft. Passing "schedule": "Manual" means the collection only runs when you explicitly call the run endpoint — no automatic re-runs.

Add ASINs (POST /schedules/amazon/asin/create/multiple/)

The multiple endpoint accepts a comma-separated ASIN string plus the domain name and postcode value as strings (not IDs). The backend resolves them internally. Adding the first ASIN auto-promotes the collection from draft to enable.

Run (POST /schedules/run/result/)

Triggers the scrape. Returns immediately — scraping is asynchronous. If you have fewer credits than items in the collection the envelope returns code: 402; the post helper will raise because status will not be 1.

Poll (POST /schedules/collections/results/download/)

The endpoint returns a list of runner records. Poll until is_download_generated is true. The total_results field on the runner shows how many items were scraped.

Fetch result file (GET /results/...)

The URL under download_links.pages[0] points to the result JSON for the first page of that run. Pass your API key as x-api-key on the GET request — the endpoint is owner-scoped and will reject requests without it.

Environment variables

VariableDescription
MCA_API_KEYYour MultiCartAPI API key (required).
MCA_NOTIFY_EMAILEmail address for run-completion notifications (optional, falls back to [email protected]).

Find your API key under Account → API Key in the dashboard, or retrieve it programmatically via POST /users/account-details.