Integrate with Python
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
| Variable | Description |
|---|---|
MCA_API_KEY | Your MultiCartAPI API key (required). |
MCA_NOTIFY_EMAIL | Email 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.
Next steps
